Skip to content

Commit 283be88

Browse files
Merge branch 'master' into branch-protection-anyone
# Conflicts: # public/js/index.js # public/js/index.js.map
2 parents 459c59c + e6036d9 commit 283be88

File tree

206 files changed

+6506
-1312
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

206 files changed

+6506
-1312
lines changed

docs/content/doc/features/authentication.en-us.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,3 +216,42 @@ configure this, set the fields below:
216216

217217
- Log in to Gitea as an Administrator and click on "Authentication" under Admin Panel.
218218
Then click `Add New Source` and fill in the details, changing all where appropriate.
219+
220+
## SPNEGO with SSPI (Kerberos/NTLM, for Windows only)
221+
222+
Gitea supports SPNEGO single sign-on authentication (the scheme defined by RFC4559) for the web part of the server via the Security Support Provider Interface (SSPI) built in Windows. SSPI works only in Windows environments - when both the server and the clients are running Windows.
223+
224+
Before activating SSPI single sign-on authentication (SSO) you have to prepare your environment:
225+
226+
- Create a separate user account in active directory, under which the `gitea.exe` process will be running (eg. `user` under domain `domain.local`):
227+
228+
- Create a service principal name for the host where `gitea.exe` is running with class `HTTP`:
229+
- Start `Command Prompt` or `PowerShell` as a priviledged domain user (eg. Domain Administrator)
230+
- Run the command below, replacing `host.domain.local` with the fully qualified domain name (FQDN) of the server where the web application will be running, and `domain\user` with the name of the account created in the previous step:
231+
```
232+
setspn -A HTTP/host.domain.local domain\user
233+
```
234+
235+
- Sign in (*sign out if you were already signed in*) with the user created
236+
237+
- Make sure that `ROOT_URL` in the `[server]` section of `custom/conf/app.ini` is the fully qualified domain name of the server where the web application will be running - the same you used when creating the service principal name (eg. `host.domain.local`)
238+
239+
- Start the web server (`gitea.exe web`)
240+
241+
- Enable SSPI authentication by adding an `SPNEGO with SSPI` authentication source in `Site Administration -> Authentication Sources`
242+
243+
- Sign in to a client computer in the same domain with any domain user (client computer, different from the server running `gitea.exe`)
244+
245+
- If you are using Chrome, Edge or Internet Explorer, add the URL of the web app to the Local intranet sites (`Internet Options -> Security -> Local intranet -> Sites`)
246+
247+
- Start Chrome, Edge or Internet Explorer and navigate to the FQDN URL of gitea (eg. `http://host.domain.local:3000`)
248+
249+
- Click the `Sign In` button on the dashboard and choose SSPI to be automatically logged in with the same user that is currently logged on to the computer
250+
251+
- If it does not work, make sure that:
252+
- You are not running the web browser on the same server where gitea is running. You should be running the web browser on a domain joined computer (client) that is different from the server. If both the client and server are runnning on the same computer NTLM will be prefered over Kerberos.
253+
- There is only one `HTTP/...` SPN for the host
254+
- The SPN contains only the hostname, without the port
255+
- You have added the URL of the web app to the `Local intranet zone`
256+
- The clocks of the server and client should not differ with more than 5 minutes (depends on group policy)
257+
- `Integrated Windows Authentication` should be enabled in Internet Explorer (under `Advanced settings`)

go.mod

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ require (
7878
github.com/pquerna/otp v0.0.0-20160912161815-54653902c20e
7979
github.com/prometheus/client_golang v1.1.0
8080
github.com/prometheus/procfs v0.0.4 // indirect
81+
github.com/quasoft/websspi v1.0.0
8182
github.com/remyoudompheng/bigfft v0.0.0-20190321074620-2f0d2b0e0001 // indirect
8283
github.com/russross/blackfriday/v2 v2.0.1
8384
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca // indirect
@@ -101,7 +102,7 @@ require (
101102
golang.org/x/crypto v0.0.0-20191117063200-497ca9f6d64f
102103
golang.org/x/net v0.0.0-20191101175033-0deb6923b6d9
103104
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
104-
golang.org/x/sys v0.0.0-20190910064555-bbd175535a8b
105+
golang.org/x/sys v0.0.0-20191010194322-b09406accb47
105106
golang.org/x/text v0.3.2
106107
golang.org/x/tools v0.0.0-20190910221609-7f5965fd7709 // indirect
107108
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect

go.sum

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,8 @@ github.com/prometheus/procfs v0.0.4 h1:w8DjqFMJDjuVwdZBQoOozr4MVWOnwF7RcL/7uxBjY
459459
github.com/prometheus/procfs v0.0.4/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
460460
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
461461
github.com/prometheus/tsdb v0.10.0/go.mod h1:oi49uRhEe9dPUTlS3JRZOwJuVi6tmh10QSgwXEyGCt4=
462+
github.com/quasoft/websspi v1.0.0 h1:5nDgdM5xSur9s+B5w2xQ5kxf5nUGqgFgU4W0aDLZ8Mw=
463+
github.com/quasoft/websspi v1.0.0/go.mod h1:HmVdl939dQ0WIXZhyik+ARdI03M6bQzaSEKcgpFmewk=
462464
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
463465
github.com/remyoudompheng/bigfft v0.0.0-20190321074620-2f0d2b0e0001 h1:YDeskXpkNDhPdWN3REluVa46HQOVuVkjkd2sWnrABNQ=
464466
github.com/remyoudompheng/bigfft v0.0.0-20190321074620-2f0d2b0e0001/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
@@ -656,8 +658,8 @@ golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7w
656658
golang.org/x/sys v0.0.0-20190730183949-1393eb018365/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
657659
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
658660
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
659-
golang.org/x/sys v0.0.0-20190910064555-bbd175535a8b h1:3S2h5FadpNr0zUUCVZjlKIEYF+KaX/OBplTGo89CYHI=
660-
golang.org/x/sys v0.0.0-20190910064555-bbd175535a8b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
661+
golang.org/x/sys v0.0.0-20191010194322-b09406accb47 h1:/XfQ9z7ib8eEJX2hdgFTZJ/ntt0swNk5oYBziWeTCvY=
662+
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
661663
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
662664
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
663665
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=

models/login_source.go

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ const (
3939
LoginPAM // 4
4040
LoginDLDAP // 5
4141
LoginOAuth2 // 6
42+
LoginSSPI // 7
4243
)
4344

4445
// LoginNames contains the name of LoginType values.
@@ -48,6 +49,7 @@ var LoginNames = map[LoginType]string{
4849
LoginSMTP: "SMTP",
4950
LoginPAM: "PAM",
5051
LoginOAuth2: "OAuth2",
52+
LoginSSPI: "SPNEGO with SSPI",
5153
}
5254

5355
// SecurityProtocolNames contains the name of SecurityProtocol values.
@@ -63,6 +65,7 @@ var (
6365
_ core.Conversion = &SMTPConfig{}
6466
_ core.Conversion = &PAMConfig{}
6567
_ core.Conversion = &OAuth2Config{}
68+
_ core.Conversion = &SSPIConfig{}
6669
)
6770

6871
// LDAPConfig holds configuration for LDAP login source.
@@ -140,6 +143,25 @@ func (cfg *OAuth2Config) ToDB() ([]byte, error) {
140143
return json.Marshal(cfg)
141144
}
142145

146+
// SSPIConfig holds configuration for SSPI single sign-on.
147+
type SSPIConfig struct {
148+
AutoCreateUsers bool
149+
AutoActivateUsers bool
150+
StripDomainNames bool
151+
SeparatorReplacement string
152+
DefaultLanguage string
153+
}
154+
155+
// FromDB fills up an SSPIConfig from serialized format.
156+
func (cfg *SSPIConfig) FromDB(bs []byte) error {
157+
return json.Unmarshal(bs, cfg)
158+
}
159+
160+
// ToDB exports an SSPIConfig to a serialized format.
161+
func (cfg *SSPIConfig) ToDB() ([]byte, error) {
162+
return json.Marshal(cfg)
163+
}
164+
143165
// LoginSource represents an external way for authorizing users.
144166
type LoginSource struct {
145167
ID int64 `xorm:"pk autoincr"`
@@ -176,6 +198,8 @@ func (source *LoginSource) BeforeSet(colName string, val xorm.Cell) {
176198
source.Cfg = new(PAMConfig)
177199
case LoginOAuth2:
178200
source.Cfg = new(OAuth2Config)
201+
case LoginSSPI:
202+
source.Cfg = new(SSPIConfig)
179203
default:
180204
panic("unrecognized login source type: " + com.ToStr(*val))
181205
}
@@ -212,6 +236,11 @@ func (source *LoginSource) IsOAuth2() bool {
212236
return source.Type == LoginOAuth2
213237
}
214238

239+
// IsSSPI returns true of this source is of the SSPI type.
240+
func (source *LoginSource) IsSSPI() bool {
241+
return source.Type == LoginSSPI
242+
}
243+
215244
// HasTLS returns true of this source supports TLS.
216245
func (source *LoginSource) HasTLS() bool {
217246
return ((source.IsLDAP() || source.IsDLDAP()) &&
@@ -264,6 +293,11 @@ func (source *LoginSource) OAuth2() *OAuth2Config {
264293
return source.Cfg.(*OAuth2Config)
265294
}
266295

296+
// SSPI returns SSPIConfig for this source, if of SSPI type.
297+
func (source *LoginSource) SSPI() *SSPIConfig {
298+
return source.Cfg.(*SSPIConfig)
299+
}
300+
267301
// CreateLoginSource inserts a LoginSource in the DB if not already
268302
// existing with the given name.
269303
func CreateLoginSource(source *LoginSource) error {
@@ -300,6 +334,38 @@ func LoginSources() ([]*LoginSource, error) {
300334
return auths, x.Find(&auths)
301335
}
302336

337+
// LoginSourcesByType returns all sources of the specified type
338+
func LoginSourcesByType(loginType LoginType) ([]*LoginSource, error) {
339+
sources := make([]*LoginSource, 0, 1)
340+
if err := x.Where("type = ?", loginType).Find(&sources); err != nil {
341+
return nil, err
342+
}
343+
return sources, nil
344+
}
345+
346+
// ActiveLoginSources returns all active sources of the specified type
347+
func ActiveLoginSources(loginType LoginType) ([]*LoginSource, error) {
348+
sources := make([]*LoginSource, 0, 1)
349+
if err := x.Where("is_actived = ? and type = ?", true, loginType).Find(&sources); err != nil {
350+
return nil, err
351+
}
352+
return sources, nil
353+
}
354+
355+
// IsSSPIEnabled returns true if there is at least one activated login
356+
// source of type LoginSSPI
357+
func IsSSPIEnabled() bool {
358+
if !HasEngine {
359+
return false
360+
}
361+
sources, err := ActiveLoginSources(LoginSSPI)
362+
if err != nil {
363+
log.Error("ActiveLoginSources: %v", err)
364+
return false
365+
}
366+
return len(sources) > 0
367+
}
368+
303369
// GetLoginSourceByID returns login source by given ID.
304370
func GetLoginSourceByID(id int64) (*LoginSource, error) {
305371
source := new(LoginSource)
@@ -719,8 +785,8 @@ func UserSignIn(username, password string) (*User, error) {
719785
}
720786

721787
for _, source := range sources {
722-
if source.IsOAuth2() {
723-
// don't try to authenticate against OAuth2 sources
788+
if source.IsOAuth2() || source.IsSSPI() {
789+
// don't try to authenticate against OAuth2 and SSPI sources here
724790
continue
725791
}
726792
authUser, err := ExternalUserLogin(nil, username, password, source, true)

models/repo_indexer.go

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ func (repo *Repository) updateIndexerStatus(sha string) error {
6060
}
6161

6262
type repoIndexerOperation struct {
63-
repo *Repository
63+
repoID int64
6464
deleted bool
6565
watchers []chan<- error
6666
}
@@ -145,7 +145,7 @@ func populateRepoIndexer(maxRepoID int64) {
145145
}
146146
for _, repo := range repos {
147147
repoIndexerOperationQueue <- repoIndexerOperation{
148-
repo: repo,
148+
repoID: repo.ID,
149149
deleted: false,
150150
}
151151
maxRepoID = repo.ID - 1
@@ -154,7 +154,12 @@ func populateRepoIndexer(maxRepoID int64) {
154154
log.Info("Done populating the repo indexer with existing repositories")
155155
}
156156

157-
func updateRepoIndexer(repo *Repository) error {
157+
func updateRepoIndexer(repoID int64) error {
158+
repo, err := getRepositoryByID(x, repoID)
159+
if err != nil {
160+
return err
161+
}
162+
158163
sha, err := getDefaultBranchSha(repo)
159164
if err != nil {
160165
return err
@@ -362,11 +367,11 @@ func processRepoIndexerOperationQueue() {
362367
op := <-repoIndexerOperationQueue
363368
var err error
364369
if op.deleted {
365-
if err = indexer.DeleteRepoFromIndexer(op.repo.ID); err != nil {
370+
if err = indexer.DeleteRepoFromIndexer(op.repoID); err != nil {
366371
log.Error("DeleteRepoFromIndexer: %v", err)
367372
}
368373
} else {
369-
if err = updateRepoIndexer(op.repo); err != nil {
374+
if err = updateRepoIndexer(op.repoID); err != nil {
370375
log.Error("updateRepoIndexer: %v", err)
371376
}
372377
}
@@ -378,12 +383,12 @@ func processRepoIndexerOperationQueue() {
378383

379384
// DeleteRepoFromIndexer remove all of a repository's entries from the indexer
380385
func DeleteRepoFromIndexer(repo *Repository, watchers ...chan<- error) {
381-
addOperationToQueue(repoIndexerOperation{repo: repo, deleted: true, watchers: watchers})
386+
addOperationToQueue(repoIndexerOperation{repoID: repo.ID, deleted: true, watchers: watchers})
382387
}
383388

384389
// UpdateRepoIndexer update a repository's entries in the indexer
385390
func UpdateRepoIndexer(repo *Repository, watchers ...chan<- error) {
386-
addOperationToQueue(repoIndexerOperation{repo: repo, deleted: false, watchers: watchers})
391+
addOperationToQueue(repoIndexerOperation{repoID: repo.ID, deleted: false, watchers: watchers})
387392
}
388393

389394
func addOperationToQueue(op repoIndexerOperation) {

0 commit comments

Comments
 (0)