loading up the forgejo repo on tangled to test page performance
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Drop SSPI auth support and more Windows files (#7148)

## Dropping SSPI auth support

SSPI authentication relied on Microsoft Windows support, removal started in https://codeberg.org/forgejo/forgejo/pulls/5353, because it was broken anyway. We have no knowledge of any users using SSPI authentication. However, if you somehow managed to run Forgejo on Windows, or want to upgrade from a Gitea version which does, please ensure that you do not use SSPI as an authentication mechanism for user accounts. Feel free to reach out if you need assistance.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7148
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Reviewed-by: 0ko <0ko@noreply.codeberg.org>
Co-authored-by: Otto Richter <otto@codeberg.org>
Co-committed-by: Otto Richter <otto@codeberg.org>

+39 -816
+2 -25
Makefile
··· 59 59 CGO_CFLAGS ?= $(shell $(GO) env CGO_CFLAGS) $(CGO_EXTRA_CFLAGS) 60 60 endif 61 61 62 - ifeq ($(GOOS),windows) 63 - IS_WINDOWS := yes 64 - else ifeq ($(patsubst Windows%,Windows,$(OS)),Windows) 65 - ifeq ($(GOOS),) 66 - IS_WINDOWS := yes 67 - endif 68 - endif 69 - ifeq ($(IS_WINDOWS),yes) 70 - GOFLAGS := -v -buildmode=exe 71 - EXECUTABLE ?= gitea.exe 72 - else 73 - GOFLAGS := -v 74 - EXECUTABLE ?= gitea 75 - endif 62 + GOFLAGS := -v 63 + EXECUTABLE ?= gitea 76 64 77 65 ifeq ($(shell sed --version 2>/dev/null | grep -q GNU && echo gnu),gnu) 78 66 SED_INPLACE := sed -i ··· 498 486 $(GO) run $(GOLANGCI_LINT_PACKAGE) run $(GOLANGCI_LINT_ARGS) --fix 499 487 $(RUN_DEADCODE) > .deadcode-out 500 488 501 - # workaround step for the lint-go-windows CI task because 'go run' can not 502 - # have distinct GOOS/GOARCH for its build and run steps 503 - .PHONY: lint-go-windows 504 - lint-go-windows: 505 - @GOOS= GOARCH= $(GO) install $(GOLANGCI_LINT_PACKAGE) 506 - golangci-lint run 507 - 508 489 .PHONY: lint-go-vet 509 490 lint-go-vet: 510 491 @echo "Running go vet..." ··· 876 857 877 858 $(DIST_DIRS): 878 859 mkdir -p $(DIST_DIRS) 879 - 880 - .PHONY: release-windows 881 - release-windows: | $(DIST_DIRS) 882 - CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION) . 883 860 884 861 .PHONY: release-linux 885 862 release-linux: | $(DIST_DIRS)
+9 -12
models/asymkey/ssh_key_authorized_keys.go
··· 87 87 } 88 88 defer f.Close() 89 89 90 - // Note: chmod command does not support in Windows. 91 - if !setting.IsWindows { 92 - fi, err := f.Stat() 93 - if err != nil { 90 + fi, err := f.Stat() 91 + if err != nil { 92 + return err 93 + } 94 + 95 + // .ssh directory should have mode 700, and authorized_keys file should have mode 600. 96 + if fi.Mode().Perm() > 0o600 { 97 + log.Error("authorized_keys file has unusual permission flags: %s - setting to -rw-------", fi.Mode().Perm().String()) 98 + if err = f.Chmod(0o600); err != nil { 94 99 return err 95 - } 96 - 97 - // .ssh directory should have mode 700, and authorized_keys file should have mode 600. 98 - if fi.Mode().Perm() > 0o600 { 99 - log.Error("authorized_keys file has unusual permission flags: %s - setting to -rw-------", fi.Mode().Perm().String()) 100 - if err = f.Chmod(0o600); err != nil { 101 - return err 102 - } 103 100 } 104 101 } 105 102
+1 -21
models/auth/source.go
··· 32 32 PAM // 4 33 33 DLDAP // 5 34 34 OAuth2 // 6 35 - SSPI // 7 35 + _ // 7 (was SSPI) 36 36 Remote // 8 37 37 ) 38 38 ··· 53 53 SMTP: "SMTP", 54 54 PAM: "PAM", 55 55 OAuth2: "OAuth2", 56 - SSPI: "SPNEGO with SSPI", 57 56 Remote: "Remote", 58 57 } 59 58 ··· 178 177 return source.Type == OAuth2 179 178 } 180 179 181 - // IsSSPI returns true of this source is of the SSPI type. 182 - func (source *Source) IsSSPI() bool { 183 - return source.Type == SSPI 184 - } 185 - 186 180 func (source *Source) IsRemote() bool { 187 181 return source.Type == Remote 188 182 } ··· 263 257 conds = conds.And(builder.Eq{"`type`": opts.LoginType}) 264 258 } 265 259 return conds 266 - } 267 - 268 - // IsSSPIEnabled returns true if there is at least one activated login 269 - // source of type LoginSSPI 270 - func IsSSPIEnabled(ctx context.Context) bool { 271 - exist, err := db.Exist[Source](ctx, FindSourcesOptions{ 272 - IsActive: optional.Some(true), 273 - LoginType: SSPI, 274 - }.ToConds()) 275 - if err != nil { 276 - log.Error("IsSSPIEnabled: failed to query active SSPI sources: %v", err) 277 - return false 278 - } 279 - return exist 280 260 } 281 261 282 262 // GetSourceByID returns login source by given ID.
-4
models/migrations/test/tests.go
··· 11 11 "os" 12 12 "path" 13 13 "path/filepath" 14 - "runtime" 15 14 "strings" 16 15 "testing" 17 16 "time" ··· 123 122 os.Exit(1) 124 123 } 125 124 giteaBinary := "gitea" 126 - if runtime.GOOS == "windows" { 127 - giteaBinary += ".exe" 128 - } 129 125 setting.AppPath = path.Join(giteaRoot, giteaBinary) 130 126 if _, err := os.Stat(setting.AppPath); err != nil { 131 127 fmt.Printf("Could not find gitea binary at %s\n", setting.AppPath)
+1 -1
modules/git/blame.go
··· 139 139 cmd := NewCommandContextNoGlobals(ctx, "blame", "--porcelain") 140 140 if ignoreRevsFile != nil { 141 141 // Possible improvement: use --ignore-revs-file /dev/stdin on unix 142 - // There is no equivalent on Windows. May be implemented if Gitea uses an external git backend. 142 + // This was not done in Gitea because it would not have been compatible with Windows. 143 143 cmd.AddOptionValues("--ignore-revs-file", *ignoreRevsFile) 144 144 } 145 145 cmd.AddDynamicArguments(commit.ID.String()).
-12
modules/git/command.go
··· 12 12 "io" 13 13 "os" 14 14 "os/exec" 15 - "runtime" 16 15 "runtime/trace" 17 16 "strings" 18 17 "time" ··· 357 356 elapsed := time.Since(startTime) 358 357 if elapsed > time.Second { 359 358 log.Debug("slow git.Command.Run: %s (%s)", c, elapsed) 360 - } 361 - 362 - // We need to check if the context is canceled by the program on Windows. 363 - // This is because Windows does not have signal checking when terminating the process. 364 - // It always returns exit code 1, unlike Linux, which has many exit codes for signals. 365 - if runtime.GOOS == "windows" && 366 - err != nil && 367 - err.Error() == "" && 368 - cmd.ProcessState.ExitCode() == 1 && 369 - ctx.Err() == context.Canceled { 370 - return ctx.Err() 371 359 } 372 360 373 361 if err != nil && ctx.Err() != context.DeadlineExceeded {
+3 -24
modules/git/git.go
··· 59 59 return fmt.Errorf("invalid git version output: %s", stdout) 60 60 } 61 61 62 - var versionString string 63 - 64 - // Handle special case on Windows. 65 - i := strings.Index(fields[2], "windows") 66 - if i >= 1 { 67 - versionString = fields[2][:i-1] 68 - } else { 69 - versionString = fields[2] 70 - } 62 + versionString := fields[2] 71 63 72 64 var err error 73 65 gitVersion, err = version.NewVersion(versionString) ··· 280 272 // Thus the owner uid/gid for files on these filesystems will be marked as root. 281 273 // As Gitea now always use its internal git config file, and access to the git repositories is managed through Gitea, 282 274 // it is now safe to set "safe.directory=*" for internal usage only. 283 - // Please note: the wildcard "*" is only supported by Git 2.30.4/2.31.3/2.32.2/2.33.3/2.34.3/2.35.3/2.36 and later 284 - // Although only supported by Git 2.30.4/2.31.3/2.32.2/2.33.3/2.34.3/2.35.3/2.36 and later - this setting is tolerated by earlier versions 275 + // Please note: the wildcard "*" is only supported by Git 2.30.4/2.31.3/2.32.2/2.33.3/2.34.3/2.35.3/2.36 and later, 276 + // but is tolerated by earlier versions 285 277 if err := configAddNonExist("safe.directory", "*"); err != nil { 286 278 return err 287 - } 288 - if runtime.GOOS == "windows" { 289 - if err := configSet("core.longpaths", "true"); err != nil { 290 - return err 291 - } 292 - if setting.Git.DisableCoreProtectNTFS { 293 - err = configSet("core.protectNTFS", "false") 294 - } else { 295 - err = configUnsetAll("core.protectNTFS", "false") 296 - } 297 - if err != nil { 298 - return err 299 - } 300 279 } 301 280 302 281 // By default partial clones are disabled, enable them from git v2.22
-2
modules/graceful/manager_unix.go
··· 1 1 // Copyright 2019 The Gitea Authors. All rights reserved. 2 2 // SPDX-License-Identifier: MIT 3 3 4 - //go:build !windows 5 - 6 4 package graceful 7 5 8 6 import (
-2
modules/graceful/net_unix.go
··· 3 3 4 4 // This code is heavily inspired by the archived gofacebook/gracenet/net.go handler 5 5 6 - //go:build !windows 7 - 8 6 package graceful 9 7 10 8 import (
-2
modules/graceful/restart_unix.go
··· 3 3 4 4 // This code is heavily inspired by the archived gofacebook/gracenet/net.go handler 5 5 6 - //go:build !windows 7 - 8 6 package graceful 9 7 10 8 import (
-2
modules/log/color_console_other.go
··· 1 1 // Copyright 2022 The Gitea Authors. All rights reserved. 2 2 // SPDX-License-Identifier: MIT 3 3 4 - //go:build !windows 5 - 6 4 package log 7 5 8 6 import (
-42
modules/log/color_console_windows.go
··· 1 - // Copyright 2019 The Gitea Authors. All rights reserved. 2 - // SPDX-License-Identifier: MIT 3 - 4 - package log 5 - 6 - import ( 7 - "os" 8 - 9 - "github.com/mattn/go-isatty" 10 - "golang.org/x/sys/windows" 11 - ) 12 - 13 - func enableVTMode(console windows.Handle) bool { 14 - mode := uint32(0) 15 - err := windows.GetConsoleMode(console, &mode) 16 - if err != nil { 17 - return false 18 - } 19 - 20 - // EnableVirtualTerminalProcessing is the console mode to allow ANSI code 21 - // interpretation on the console. See: 22 - // https://docs.microsoft.com/en-us/windows/console/setconsolemode 23 - // It only works on Windows 10. Earlier terminals will fail with an err which we will 24 - // handle to say don't color 25 - mode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING 26 - err = windows.SetConsoleMode(console, mode) 27 - return err == nil 28 - } 29 - 30 - func init() { 31 - if isatty.IsTerminal(os.Stdout.Fd()) { 32 - CanColorStdout = enableVTMode(windows.Stdout) 33 - } else { 34 - CanColorStdout = isatty.IsCygwinTerminal(os.Stderr.Fd()) 35 - } 36 - 37 - if isatty.IsTerminal(os.Stderr.Fd()) { 38 - CanColorStderr = enableVTMode(windows.Stderr) 39 - } else { 40 - CanColorStderr = isatty.IsCygwinTerminal(os.Stderr.Fd()) 41 - } 42 - }
-4
modules/markup/external/external.go
··· 9 9 "io" 10 10 "os" 11 11 "os/exec" 12 - "runtime" 13 12 "strings" 14 13 15 14 "code.gitea.io/gitea/modules/graceful" ··· 70 69 } 71 70 72 71 func envMark(envName string) string { 73 - if runtime.GOOS == "windows" { 74 - return "%" + envName + "%" 75 - } 76 72 return "$" + envName 77 73 } 78 74
-2
modules/process/manager_unix.go
··· 1 1 // Copyright 2022 The Gitea Authors. All rights reserved. 2 2 // SPDX-License-Identifier: MIT 3 3 4 - //go:build !windows 5 - 6 4 package process 7 5 8 6 import (
-5
modules/repository/hooks.go
··· 7 7 "fmt" 8 8 "os" 9 9 "path/filepath" 10 - "runtime" 11 10 12 11 "code.gitea.io/gitea/modules/setting" 13 12 "code.gitea.io/gitea/modules/util" ··· 146 145 } 147 146 148 147 func checkExecutable(filename string) bool { 149 - // windows has no concept of a executable bit 150 - if runtime.GOOS == "windows" { 151 - return true 152 - } 153 148 fileInfo, err := os.Stat(filename) 154 149 if err != nil { 155 150 return false
+1 -5
modules/setting/path.go
··· 34 34 func getAppPath() (string, error) { 35 35 var appPath string 36 36 var err error 37 - if IsWindows && filepath.IsAbs(os.Args[0]) { 38 - appPath = filepath.Clean(os.Args[0]) 39 - } else { 40 - appPath, err = exec.LookPath(os.Args[0]) 41 - } 37 + appPath, err = exec.LookPath(os.Args[0]) 42 38 if err != nil { 43 39 if !errors.Is(err, exec.ErrDot) { 44 40 return "", err
+1 -7
modules/setting/setting.go
··· 8 8 import ( 9 9 "fmt" 10 10 "os" 11 - "runtime" 12 11 "strings" 13 12 "time" 14 13 ··· 34 33 RunMode string 35 34 RunUser string 36 35 IsProd bool 37 - IsWindows bool 38 36 39 37 // IsInTesting indicates whether the testing is running. A lot of unreliable code causes a lot of nonsense error logs during testing 40 38 // TODO: this is only a temporary solution, we should make the test code more reliable ··· 42 40 ) 43 41 44 42 func init() { 45 - IsWindows = runtime.GOOS == "windows" 46 43 if AppVer == "" { 47 44 AppVer = "dev" 48 45 } 49 46 50 - // We can rely on log.CanColorStdout being set properly because modules/log/console_windows.go comes before modules/setting/setting.go lexicographically 51 47 // By default set this logger at Info - we'll change it later, but we need to start with something. 52 48 log.SetConsoleLogger(log.DEFAULT, "console", log.INFO) 53 49 } 54 50 55 51 // IsRunUserMatchCurrentUser returns false if configured run user does not match 56 52 // actual user that runs the app. The first return value is the actual user name. 57 - // This check is ignored under Windows since SSH remote login is not the main 58 - // method to login on Windows. 59 53 func IsRunUserMatchCurrentUser(runUser string) (string, bool) { 60 - if IsWindows || SSH.StartBuiltinServer { 54 + if SSH.StartBuiltinServer { 61 55 return "", true 62 56 } 63 57
+1 -8
modules/user/user.go
··· 6 6 import ( 7 7 "os" 8 8 "os/user" 9 - "runtime" 10 - "strings" 11 9 ) 12 10 13 11 // CurrentUsername return current login OS user name ··· 16 14 if err != nil { 17 15 return fallbackCurrentUsername() 18 16 } 19 - username := userinfo.Username 20 - if runtime.GOOS == "windows" { 21 - parts := strings.Split(username, "\\") 22 - username = parts[len(parts)-1] 23 - } 24 - return username 17 + return userinfo.Username 25 18 } 26 19 27 20 // Old method, used if new method doesn't work on your OS for some reason
-5
modules/user/user_test.go
··· 5 5 6 6 import ( 7 7 "os/exec" 8 - "runtime" 9 8 "strings" 10 9 "testing" 11 10 ) ··· 22 21 user := CurrentUsername() 23 22 if len(user) == 0 { 24 23 t.Errorf("expected non-empty user, got: %s", user) 25 - } 26 - // Windows whoami is weird, so just skip remaining tests 27 - if runtime.GOOS == "windows" { 28 - t.Skip("skipped test because of weird whoami on Windows") 29 24 } 30 25 whoami, err := getWhoamiOutput() 31 26 if err != nil {
-2
modules/util/file_unix.go
··· 1 1 // Copyright 2022 The Gitea Authors. All rights reserved. 2 2 // SPDX-License-Identifier: MIT 3 3 4 - //go:build !windows 5 - 6 4 package util 7 5 8 6 import (
-2
modules/util/file_unix_test.go
··· 1 1 // Copyright 2022 The Gitea Authors. All rights reserved. 2 2 // SPDX-License-Identifier: MIT 3 3 4 - //go:build !windows 5 - 6 4 package util 7 5 8 6 import (
+4 -37
modules/util/path.go
··· 10 10 "os" 11 11 "path" 12 12 "path/filepath" 13 - "regexp" 14 - "runtime" 15 13 "strings" 16 14 ) 17 15 ··· 78 76 79 77 // POSIX filesystem can have `\` in file names. Windows: `\` and `/` are both used for path separators 80 78 // to keep the behavior consistent, we do not allow `\` in file names, replace all `\` with `/` 81 - if isOSWindows() { 82 - elems[0] = filepath.Clean(base) 83 - } else { 84 - elems[0] = filepath.Clean(strings.ReplaceAll(base, "\\", pathSeparator)) 85 - } 79 + elems[0] = filepath.Clean(strings.ReplaceAll(base, "\\", pathSeparator)) 86 80 if !filepath.IsAbs(elems[0]) { 87 81 // This shouldn't happen. If there is really necessary to pass in relative path, return the full path with filepath.Abs() instead 88 82 panic(fmt.Sprintf("FilePathJoinAbs: %q (for path %v) is not absolute, do not guess a relative path based on current working directory", elems[0], elems)) ··· 91 85 if s == "" { 92 86 continue 93 87 } 94 - if isOSWindows() { 95 - elems = append(elems, filepath.Clean(pathSeparator+s)) 96 - } else { 97 - elems = append(elems, filepath.Clean(pathSeparator+strings.ReplaceAll(s, "\\", pathSeparator))) 98 - } 88 + elems = append(elems, filepath.Clean(pathSeparator+strings.ReplaceAll(s, "\\", pathSeparator))) 99 89 } 100 90 // the elems[0] must be an absolute path, just join them together 101 91 return filepath.Join(elems...) ··· 217 207 return statDir(rootPath, "", isIncludeDir, false, false) 218 208 } 219 209 220 - func isOSWindows() bool { 221 - return runtime.GOOS == "windows" 222 - } 223 - 224 - var driveLetterRegexp = regexp.MustCompile("/[A-Za-z]:/") 225 - 226 210 // FileURLToPath extracts the path information from a file://... url. 227 211 // It returns an error only if the URL is not a file URL. 228 212 func FileURLToPath(u *url.URL) (string, error) { ··· 230 214 return "", errors.New("URL scheme is not 'file': " + u.String()) 231 215 } 232 216 233 - path := u.Path 234 - 235 - if !isOSWindows() { 236 - return path, nil 237 - } 238 - 239 - // If it looks like there's a Windows drive letter at the beginning, strip off the leading slash. 240 - if driveLetterRegexp.MatchString(path) { 241 - return path[1:], nil 242 - } 243 - return path, nil 217 + return u.Path, nil 244 218 } 245 219 246 220 // HomeDir returns path of '~'(in Linux) on Windows, ··· 249 223 // TODO: some users run Gitea with mismatched uid and "HOME=xxx" (they set HOME=xxx by environment manually) 250 224 // TODO: when running gitea as a sub command inside git, the HOME directory is not the user's home directory 251 225 // so at the moment we can not use `user.Current().HomeDir` 252 - if isOSWindows() { 253 - home = os.Getenv("USERPROFILE") 254 - if home == "" { 255 - home = os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH") 256 - } 257 - } else { 258 - home = os.Getenv("HOME") 259 - } 226 + home = os.Getenv("HOME") 260 227 261 228 if home == "" { 262 229 return "", errors.New("cannot get home directory")
+12 -40
modules/util/path_test.go
··· 5 5 6 6 import ( 7 7 "net/url" 8 - "runtime" 9 8 "testing" 10 9 11 10 "github.com/stretchr/testify/assert" ··· 17 16 url string 18 17 expected string 19 18 haserror bool 20 - windows bool 21 19 }{ 22 20 // case 0 23 21 { ··· 34 32 url: "file:///path", 35 33 expected: "/path", 36 34 }, 37 - // case 3 38 - { 39 - url: "file:///C:/path", 40 - expected: "C:/path", 41 - windows: true, 42 - }, 43 35 } 44 36 45 37 for n, c := range cases { 46 - if c.windows && runtime.GOOS != "windows" { 47 - continue 48 - } 49 38 u, _ := url.Parse(c.url) 50 39 p, err := FileURLToPath(u) 51 40 if c.haserror { ··· 177 166 assert.Equal(t, c.expected, PathJoinRelX(c.elems...), "case: %v", c.elems) 178 167 } 179 168 180 - // for POSIX only, but the result is similar on Windows, because the first element must be an absolute path 181 - if isOSWindows() { 182 - cases = []struct { 183 - elems []string 184 - expected string 185 - }{ 186 - {[]string{`C:\..`}, `C:\`}, 187 - {[]string{`C:\a`}, `C:\a`}, 188 - {[]string{`C:\a/`}, `C:\a`}, 189 - {[]string{`C:\..\a\`, `../b`, `c\..`, `d`}, `C:\a\b\d`}, 190 - {[]string{`C:\a/..\b`}, `C:\b`}, 191 - {[]string{`C:\a`, ``, `b`}, `C:\a\b`}, 192 - {[]string{`C:\a`, `..`, `b`}, `C:\a\b`}, 193 - {[]string{`C:\lfs`, `repo/..`, `user/../path`}, `C:\lfs\path`}, 194 - } 195 - } else { 196 - cases = []struct { 197 - elems []string 198 - expected string 199 - }{ 200 - {[]string{`/..`}, `/`}, 201 - {[]string{`/a`}, `/a`}, 202 - {[]string{`/a/`}, `/a`}, 203 - {[]string{`/../a/`, `../b`, `c/..`, `d`}, `/a/b/d`}, 204 - {[]string{`/a\..\b`}, `/b`}, 205 - {[]string{`/a`, ``, `b`}, `/a/b`}, 206 - {[]string{`/a`, `..`, `b`}, `/a/b`}, 207 - {[]string{`/lfs`, `repo/..`, `user/../path`}, `/lfs/path`}, 208 - } 169 + cases = []struct { 170 + elems []string 171 + expected string 172 + }{ 173 + {[]string{`/..`}, `/`}, 174 + {[]string{`/a`}, `/a`}, 175 + {[]string{`/a/`}, `/a`}, 176 + {[]string{`/../a/`, `../b`, `c/..`, `d`}, `/a/b/d`}, 177 + {[]string{`/a\..\b`}, `/b`}, 178 + {[]string{`/a`, ``, `b`}, `/a/b`}, 179 + {[]string{`/a`, `..`, `b`}, `/a/b`}, 180 + {[]string{`/lfs`, `repo/..`, `user/../path`}, `/lfs/path`}, 209 181 } 210 182 for _, c := range cases { 211 183 assert.Equal(t, c.expected, FilePathJoinAbs(c.elems[0], c.elems[1:]...), "case: %v", c.elems)
-21
modules/util/remove.go
··· 5 5 6 6 import ( 7 7 "os" 8 - "runtime" 9 8 "syscall" 10 9 "time" 11 10 ) 12 - 13 - const windowsSharingViolationError syscall.Errno = 32 14 11 15 12 // Remove removes the named file or (empty) directory with at most 5 attempts. 16 13 func Remove(name string) error { ··· 22 19 } 23 20 unwrapped := err.(*os.PathError).Err 24 21 if unwrapped == syscall.EBUSY || unwrapped == syscall.ENOTEMPTY || unwrapped == syscall.EPERM || unwrapped == syscall.EMFILE || unwrapped == syscall.ENFILE { 25 - // try again 26 - <-time.After(100 * time.Millisecond) 27 - continue 28 - } 29 - 30 - if unwrapped == windowsSharingViolationError && runtime.GOOS == "windows" { 31 22 // try again 32 23 <-time.After(100 * time.Millisecond) 33 24 continue ··· 56 47 continue 57 48 } 58 49 59 - if unwrapped == windowsSharingViolationError && runtime.GOOS == "windows" { 60 - // try again 61 - <-time.After(100 * time.Millisecond) 62 - continue 63 - } 64 - 65 50 if unwrapped == syscall.ENOENT { 66 51 // it's already gone 67 52 return nil ··· 80 65 } 81 66 unwrapped := err.(*os.LinkError).Err 82 67 if unwrapped == syscall.EBUSY || unwrapped == syscall.ENOTEMPTY || unwrapped == syscall.EPERM || unwrapped == syscall.EMFILE || unwrapped == syscall.ENFILE { 83 - // try again 84 - <-time.After(100 * time.Millisecond) 85 - continue 86 - } 87 - 88 - if unwrapped == windowsSharingViolationError && runtime.GOOS == "windows" { 89 68 // try again 90 69 <-time.After(100 * time.Millisecond) 91 70 continue
+1 -14
options/locale/locale_en-US.ini
··· 609 609 TreeName = File path 610 610 Content = Content 611 611 612 - SSPISeparatorReplacement = Separator 613 - SSPIDefaultLanguage = Default language 614 - 615 612 require_error = ` cannot be empty.` 616 613 alpha_dash_error = ` should contain only alphanumeric, dash ("-") and underscore ("_") characters.` 617 614 alpha_dash_dot_error = ` should contain only alphanumeric, dash ("-"), underscore ("_") and dot (".") characters.` ··· 3300 3297 auths.oauth2_restricted_group = Group claim value for restricted users. (Optional - requires claim name above) 3301 3298 auths.oauth2_map_group_to_team = Map claimed groups to organization teams. (Optional - requires claim name above) 3302 3299 auths.oauth2_map_group_to_team_removal = Remove users from synchronized teams if user does not belong to corresponding group. 3303 - auths.sspi_auto_create_users = Automatically create users 3304 - auths.sspi_auto_create_users_helper = Allow SSPI auth method to automatically create new accounts for users that login for the first time 3305 - auths.sspi_auto_activate_users = Automatically activate users 3306 - auths.sspi_auto_activate_users_helper = Allow SSPI auth method to automatically activate new users 3307 - auths.sspi_strip_domain_names = Remove domain names from usernames 3308 - auths.sspi_strip_domain_names_helper = If checked, domain names will be removed from logon names (eg. "DOMAIN\user" and "user@example.org" both will become just "user"). 3309 - auths.sspi_separator_replacement = Separator to use instead of \, / and @ 3310 - auths.sspi_separator_replacement_helper = The character to use to replace the separators of down-level logon names (eg. the \ in "DOMAIN\user") and user principal names (eg. the @ in "user@example.org"). 3311 - auths.sspi_default_language = Default user language 3312 - auths.sspi_default_language_helper = Default language for users automatically created by SSPI auth method. Leave empty if you prefer language to be automatically detected. 3313 3300 auths.tips = Tips 3314 3301 auths.tips.gmail_settings = Gmail settings: 3315 3302 auths.tips.oauth2.general = OAuth2 authentication ··· 3965 3952 filepreview.truncated = Preview has been truncated 3966 3953 3967 3954 [translation_meta] 3968 - test = This is a test string. It is not displayed in Forgejo UI but is used for testing purposes. Feel free to enter "ok" to save time (or a fun fact of your choice) to hit that sweet 100% completion mark :) 3955 + test = This is a test string. It is not displayed in Forgejo UI but is used for testing purposes. Feel free to enter "ok" to save time (or a fun fact of your choice) to hit that sweet 100% completion mark :)
-6
routers/api/shared/middleware.go
··· 6 6 import ( 7 7 "net/http" 8 8 9 - auth_model "code.gitea.io/gitea/models/auth" 10 - "code.gitea.io/gitea/models/db" 11 9 "code.gitea.io/gitea/modules/log" 12 10 "code.gitea.io/gitea/modules/setting" 13 11 "code.gitea.io/gitea/routers/common" ··· 49 47 ) 50 48 if setting.Service.EnableReverseProxyAuthAPI { 51 49 group.Add(&auth.ReverseProxy{}) 52 - } 53 - 54 - if setting.IsWindows && auth_model.IsSSPIEnabled(db.DefaultContext) { 55 - group.Add(&auth.SSPI{}) // it MUST be the last, see the comment of SSPI 56 50 } 57 51 58 52 return group
+1 -10
routers/install/install.go
··· 29 29 "code.gitea.io/gitea/modules/setting" 30 30 "code.gitea.io/gitea/modules/templates" 31 31 "code.gitea.io/gitea/modules/translation" 32 - "code.gitea.io/gitea/modules/user" 33 32 "code.gitea.io/gitea/modules/web" 34 33 "code.gitea.io/gitea/modules/web/middleware" 35 34 "code.gitea.io/gitea/routers/common" ··· 119 118 form.AppSlogan = "Beyond coding. We Forge." 120 119 form.RepoRootPath = setting.RepoRootPath 121 120 form.LFSRootPath = setting.LFS.Storage.Path 122 - 123 - // Note(unknown): it's hard for Windows users change a running user, 124 - // so just use current one if config says default. 125 - if setting.IsWindows && setting.RunUser == "git" { 126 - form.RunUser = user.CurrentUsername() 127 - } else { 128 - form.RunUser = setting.RunUser 129 - } 130 - 121 + form.RunUser = setting.RunUser 131 122 form.Domain = setting.Domain 132 123 form.SSHPort = setting.SSH.Port 133 124 form.HTTPPort = setting.HTTPPort
-2
routers/private/manager_unix.go
··· 1 1 // Copyright 2020 The Gitea Authors. All rights reserved. 2 2 // SPDX-License-Identifier: MIT 3 3 4 - //go:build !windows 5 - 6 4 package private 7 5 8 6 import (
-65
routers/web/admin/auths.go
··· 4 4 package admin 5 5 6 6 import ( 7 - "errors" 8 7 "fmt" 9 8 "net/http" 10 9 "net/url" 11 - "regexp" 12 10 "strconv" 13 11 "strings" 14 12 ··· 18 16 "code.gitea.io/gitea/modules/base" 19 17 "code.gitea.io/gitea/modules/log" 20 18 "code.gitea.io/gitea/modules/setting" 21 - "code.gitea.io/gitea/modules/util" 22 19 "code.gitea.io/gitea/modules/web" 23 20 auth_service "code.gitea.io/gitea/services/auth" 24 21 "code.gitea.io/gitea/services/auth/source/ldap" 25 22 "code.gitea.io/gitea/services/auth/source/oauth2" 26 23 pam_service "code.gitea.io/gitea/services/auth/source/pam" 27 24 "code.gitea.io/gitea/services/auth/source/smtp" 28 - "code.gitea.io/gitea/services/auth/source/sspi" 29 25 "code.gitea.io/gitea/services/context" 30 26 "code.gitea.io/gitea/services/forms" 31 27 ··· 36 32 tplAuths base.TplName = "admin/auth/list" 37 33 tplAuthNew base.TplName = "admin/auth/new" 38 34 tplAuthEdit base.TplName = "admin/auth/edit" 39 - ) 40 - 41 - var ( 42 - separatorAntiPattern = regexp.MustCompile(`[^\w-\.]`) 43 - langCodePattern = regexp.MustCompile(`^[a-z]{2}-[A-Z]{2}$`) 44 35 ) 45 36 46 37 // Authentications show authentication config page ··· 70 61 {auth.DLDAP.String(), auth.DLDAP}, 71 62 {auth.SMTP.String(), auth.SMTP}, 72 63 {auth.OAuth2.String(), auth.OAuth2}, 73 - {auth.SSPI.String(), auth.SSPI}, 74 64 } 75 65 if pam.Supported { 76 66 items = append(items, dropdownItem{auth.Names[auth.PAM], auth.PAM}) ··· 101 91 ctx.Data["SMTPAuths"] = smtp.Authenticators 102 92 oauth2providers := oauth2.GetSupportedOAuth2Providers() 103 93 ctx.Data["OAuth2Providers"] = oauth2providers 104 - 105 - ctx.Data["SSPIAutoCreateUsers"] = true 106 - ctx.Data["SSPIAutoActivateUsers"] = true 107 - ctx.Data["SSPIStripDomainNames"] = true 108 - ctx.Data["SSPISeparatorReplacement"] = "_" 109 - ctx.Data["SSPIDefaultLanguage"] = "" 110 94 111 95 // only the first as default 112 96 ctx.Data["oauth2_provider"] = oauth2providers[0].Name() ··· 209 193 } 210 194 } 211 195 212 - func parseSSPIConfig(ctx *context.Context, form forms.AuthenticationForm) (*sspi.Source, error) { 213 - if util.IsEmptyString(form.SSPISeparatorReplacement) { 214 - ctx.Data["Err_SSPISeparatorReplacement"] = true 215 - return nil, errors.New(ctx.Locale.TrString("form.SSPISeparatorReplacement") + ctx.Locale.TrString("form.require_error")) 216 - } 217 - if separatorAntiPattern.MatchString(form.SSPISeparatorReplacement) { 218 - ctx.Data["Err_SSPISeparatorReplacement"] = true 219 - return nil, errors.New(ctx.Locale.TrString("form.SSPISeparatorReplacement") + ctx.Locale.TrString("form.alpha_dash_dot_error")) 220 - } 221 - 222 - if form.SSPIDefaultLanguage != "" && !langCodePattern.MatchString(form.SSPIDefaultLanguage) { 223 - ctx.Data["Err_SSPIDefaultLanguage"] = true 224 - return nil, errors.New(ctx.Locale.TrString("form.lang_select_error")) 225 - } 226 - 227 - return &sspi.Source{ 228 - AutoCreateUsers: form.SSPIAutoCreateUsers, 229 - AutoActivateUsers: form.SSPIAutoActivateUsers, 230 - StripDomainNames: form.SSPIStripDomainNames, 231 - SeparatorReplacement: form.SSPISeparatorReplacement, 232 - DefaultLanguage: form.SSPIDefaultLanguage, 233 - }, nil 234 - } 235 - 236 196 // NewAuthSourcePost response for adding an auth source 237 197 func NewAuthSourcePost(ctx *context.Context) { 238 198 form := *web.GetForm(ctx).(*forms.AuthenticationForm) ··· 246 206 ctx.Data["SMTPAuths"] = smtp.Authenticators 247 207 oauth2providers := oauth2.GetSupportedOAuth2Providers() 248 208 ctx.Data["OAuth2Providers"] = oauth2providers 249 - 250 - ctx.Data["SSPIAutoCreateUsers"] = true 251 - ctx.Data["SSPIAutoActivateUsers"] = true 252 - ctx.Data["SSPIStripDomainNames"] = true 253 - ctx.Data["SSPISeparatorReplacement"] = "_" 254 - ctx.Data["SSPIDefaultLanguage"] = "" 255 209 256 210 hasTLS := false 257 211 var config convert.Conversion ··· 278 232 ctx.RenderWithErr(ctx.Tr("admin.auths.invalid_openIdConnectAutoDiscoveryURL"), tplAuthNew, form) 279 233 return 280 234 } 281 - } 282 - case auth.SSPI: 283 - var err error 284 - config, err = parseSSPIConfig(ctx, form) 285 - if err != nil { 286 - ctx.RenderWithErr(err.Error(), tplAuthNew, form) 287 - return 288 - } 289 - existing, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{LoginType: auth.SSPI}) 290 - if err != nil || len(existing) > 0 { 291 - ctx.Data["Err_Type"] = true 292 - ctx.RenderWithErr(ctx.Tr("admin.auths.login_source_of_type_exist"), tplAuthNew, form) 293 - return 294 235 } 295 236 default: 296 237 ctx.Error(http.StatusBadRequest) ··· 407 348 ctx.RenderWithErr(ctx.Tr("admin.auths.invalid_openIdConnectAutoDiscoveryURL"), tplAuthEdit, form) 408 349 return 409 350 } 410 - } 411 - case auth.SSPI: 412 - config, err = parseSSPIConfig(ctx, form) 413 - if err != nil { 414 - ctx.RenderWithErr(err.Error(), tplAuthEdit, form) 415 - return 416 351 } 417 352 default: 418 353 ctx.Error(http.StatusBadRequest)
-2
routers/web/auth/auth.go
··· 164 164 ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login" 165 165 ctx.Data["PageIsSignIn"] = true 166 166 ctx.Data["PageIsLogin"] = true 167 - ctx.Data["EnableSSPI"] = auth.IsSSPIEnabled(ctx) 168 167 ctx.Data["EnableInternalSignIn"] = setting.Service.EnableInternalSignIn 169 168 170 169 if setting.Service.EnableCaptcha && setting.Service.RequireCaptchaForLogin { ··· 190 189 ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login" 191 190 ctx.Data["PageIsSignIn"] = true 192 191 ctx.Data["PageIsLogin"] = true 193 - ctx.Data["EnableSSPI"] = auth.IsSSPIEnabled(ctx) 194 192 ctx.Data["EnableInternalSignIn"] = setting.Service.EnableInternalSignIn 195 193 ctx.Data["DisablePassword"] = !setting.Service.EnableInternalSignIn 196 194
-6
routers/web/web.go
··· 8 8 "net/http" 9 9 "strings" 10 10 11 - auth_model "code.gitea.io/gitea/models/auth" 12 - "code.gitea.io/gitea/models/db" 13 11 "code.gitea.io/gitea/models/perm" 14 12 quota_model "code.gitea.io/gitea/models/quota" 15 13 "code.gitea.io/gitea/models/unit" ··· 109 107 group.Add(&auth_service.ReverseProxy{}) // reverseproxy should before Session, otherwise the header will be ignored if user has login 110 108 } 111 109 group.Add(&auth_service.Session{}) 112 - 113 - if setting.IsWindows && auth_model.IsSSPIEnabled(db.DefaultContext) { 114 - group.Add(&auth_service.SSPI{}) // it MUST be the last, see the comment of SSPI 115 - } 116 110 117 111 return group 118 112 }
-1
services/auth/signin.go
··· 18 18 _ "code.gitea.io/gitea/services/auth/source/db" // register the sources (and below) 19 19 _ "code.gitea.io/gitea/services/auth/source/ldap" // register the ldap source 20 20 _ "code.gitea.io/gitea/services/auth/source/pam" // register the pam source 21 - _ "code.gitea.io/gitea/services/auth/source/sspi" // register the sspi source 22 21 ) 23 22 24 23 // UserSignIn validates user name and password.
-18
services/auth/source/sspi/assert_interface_test.go
··· 1 - // Copyright 2021 The Gitea Authors. All rights reserved. 2 - // SPDX-License-Identifier: MIT 3 - 4 - package sspi_test 5 - 6 - import ( 7 - "code.gitea.io/gitea/models/auth" 8 - "code.gitea.io/gitea/services/auth/source/sspi" 9 - ) 10 - 11 - // This test file exists to assert that our Source exposes the interfaces that we expect 12 - // It tightly binds the interfaces and implementation without breaking go import cycles 13 - 14 - type sourceInterface interface { 15 - auth.Config 16 - } 17 - 18 - var _ (sourceInterface) = &sspi.Source{}
-39
services/auth/source/sspi/source.go
··· 1 - // Copyright 2021 The Gitea Authors. All rights reserved. 2 - // SPDX-License-Identifier: MIT 3 - 4 - package sspi 5 - 6 - import ( 7 - "code.gitea.io/gitea/models/auth" 8 - "code.gitea.io/gitea/modules/json" 9 - ) 10 - 11 - // _________ ___________________.___ 12 - // / _____// _____/\______ \ | 13 - // \_____ \ \_____ \ | ___/ | 14 - // / \/ \ | | | | 15 - // /_______ /_______ / |____| |___| 16 - // \/ \/ 17 - 18 - // Source holds configuration for SSPI single sign-on. 19 - type Source struct { 20 - AutoCreateUsers bool 21 - AutoActivateUsers bool 22 - StripDomainNames bool 23 - SeparatorReplacement string 24 - DefaultLanguage string 25 - } 26 - 27 - // FromDB fills up an SSPIConfig from serialized format. 28 - func (cfg *Source) FromDB(bs []byte) error { 29 - return json.UnmarshalHandleDoubleEncode(bs, &cfg) 30 - } 31 - 32 - // ToDB exports an SSPIConfig to a serialized format. 33 - func (cfg *Source) ToDB() ([]byte, error) { 34 - return json.Marshal(cfg) 35 - } 36 - 37 - func init() { 38 - auth.RegisterTypeConfig(auth.SSPI, &Source{}) 39 - }
-223
services/auth/sspi.go
··· 1 - // Copyright 2019 The Gitea Authors. All rights reserved. 2 - // SPDX-License-Identifier: MIT 3 - 4 - package auth 5 - 6 - import ( 7 - "context" 8 - "errors" 9 - "net/http" 10 - "strings" 11 - "sync" 12 - 13 - "code.gitea.io/gitea/models/auth" 14 - "code.gitea.io/gitea/models/db" 15 - user_model "code.gitea.io/gitea/models/user" 16 - "code.gitea.io/gitea/modules/base" 17 - "code.gitea.io/gitea/modules/log" 18 - "code.gitea.io/gitea/modules/optional" 19 - "code.gitea.io/gitea/modules/setting" 20 - "code.gitea.io/gitea/modules/web/middleware" 21 - "code.gitea.io/gitea/services/auth/source/sspi" 22 - gitea_context "code.gitea.io/gitea/services/context" 23 - 24 - gouuid "github.com/google/uuid" 25 - ) 26 - 27 - const ( 28 - tplSignIn base.TplName = "user/auth/signin" 29 - ) 30 - 31 - type SSPIAuth interface { 32 - AppendAuthenticateHeader(w http.ResponseWriter, data string) 33 - Authenticate(r *http.Request, w http.ResponseWriter) (userInfo *SSPIUserInfo, outToken string, err error) 34 - } 35 - 36 - var ( 37 - sspiAuth SSPIAuth // a global instance of the websspi authenticator to avoid acquiring the server credential handle on every request 38 - sspiAuthOnce sync.Once 39 - sspiAuthErrInit error 40 - 41 - // Ensure the struct implements the interface. 42 - _ Method = &SSPI{} 43 - ) 44 - 45 - // SSPI implements the SingleSignOn interface and authenticates requests 46 - // via the built-in SSPI module in Windows for SPNEGO authentication. 47 - // The SSPI plugin is expected to be executed last, as it returns 401 status code if negotiation 48 - // fails (or if negotiation should continue), which would prevent other authentication methods 49 - // to execute at all. 50 - type SSPI struct{} 51 - 52 - // Name represents the name of auth method 53 - func (s *SSPI) Name() string { 54 - return "sspi" 55 - } 56 - 57 - // Verify uses SSPI (Windows implementation of SPNEGO) to authenticate the request. 58 - // If authentication is successful, returns the corresponding user object. 59 - // If negotiation should continue or authentication fails, immediately returns a 401 HTTP 60 - // response code, as required by the SPNEGO protocol. 61 - func (s *SSPI) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) (*user_model.User, error) { 62 - sspiAuthOnce.Do(func() { sspiAuthErrInit = sspiAuthInit() }) 63 - if sspiAuthErrInit != nil { 64 - return nil, sspiAuthErrInit 65 - } 66 - if !s.shouldAuthenticate(req) { 67 - return nil, nil 68 - } 69 - 70 - cfg, err := s.getConfig(req.Context()) 71 - if err != nil { 72 - log.Error("could not get SSPI config: %v", err) 73 - return nil, err 74 - } 75 - 76 - log.Trace("SSPI Authorization: Attempting to authenticate") 77 - userInfo, outToken, err := sspiAuth.Authenticate(req, w) 78 - if err != nil { 79 - log.Warn("Authentication failed with error: %v\n", err) 80 - sspiAuth.AppendAuthenticateHeader(w, outToken) 81 - 82 - // Include the user login page in the 401 response to allow the user 83 - // to login with another authentication method if SSPI authentication 84 - // fails 85 - store.GetData()["Flash"] = map[string]string{ 86 - "ErrorMsg": err.Error(), 87 - } 88 - store.GetData()["EnableOpenIDSignIn"] = setting.Service.EnableOpenIDSignIn 89 - store.GetData()["EnableSSPI"] = true 90 - // in this case, the Verify function is called in Gitea's web context 91 - // FIXME: it doesn't look good to render the page here, why not redirect? 92 - gitea_context.GetWebContext(req).HTML(http.StatusUnauthorized, tplSignIn) 93 - return nil, err 94 - } 95 - if outToken != "" { 96 - sspiAuth.AppendAuthenticateHeader(w, outToken) 97 - } 98 - 99 - username := sanitizeUsername(userInfo.Username, cfg) 100 - if len(username) == 0 { 101 - return nil, nil 102 - } 103 - log.Info("Authenticated as %s\n", username) 104 - 105 - user, err := user_model.GetUserByName(req.Context(), username) 106 - if err != nil { 107 - if !user_model.IsErrUserNotExist(err) { 108 - log.Error("GetUserByName: %v", err) 109 - return nil, err 110 - } 111 - if !cfg.AutoCreateUsers { 112 - log.Error("User '%s' not found", username) 113 - return nil, nil 114 - } 115 - user, err = s.newUser(req.Context(), username, cfg) 116 - if err != nil { 117 - log.Error("CreateUser: %v", err) 118 - return nil, err 119 - } 120 - } 121 - 122 - // Make sure requests to API paths and PWA resources do not create a new session 123 - if !middleware.IsAPIPath(req) && !isAttachmentDownload(req) { 124 - handleSignIn(w, req, sess, user) 125 - } 126 - 127 - log.Trace("SSPI Authorization: Logged in user %-v", user) 128 - return user, nil 129 - } 130 - 131 - // getConfig retrieves the SSPI configuration from login sources 132 - func (s *SSPI) getConfig(ctx context.Context) (*sspi.Source, error) { 133 - sources, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{ 134 - IsActive: optional.Some(true), 135 - LoginType: auth.SSPI, 136 - }) 137 - if err != nil { 138 - return nil, err 139 - } 140 - if len(sources) == 0 { 141 - return nil, errors.New("no active login sources of type SSPI found") 142 - } 143 - if len(sources) > 1 { 144 - return nil, errors.New("more than one active login source of type SSPI found") 145 - } 146 - return sources[0].Cfg.(*sspi.Source), nil 147 - } 148 - 149 - func (s *SSPI) shouldAuthenticate(req *http.Request) (shouldAuth bool) { 150 - shouldAuth = false 151 - path := strings.TrimSuffix(req.URL.Path, "/") 152 - if path == "/user/login" { 153 - if req.FormValue("user_name") != "" && req.FormValue("password") != "" { 154 - shouldAuth = false 155 - } else if req.FormValue("auth_with_sspi") == "1" { 156 - shouldAuth = true 157 - } 158 - } else if middleware.IsAPIPath(req) || isAttachmentDownload(req) { 159 - shouldAuth = true 160 - } 161 - return shouldAuth 162 - } 163 - 164 - // newUser creates a new user object for the purpose of automatic registration 165 - // and populates its name and email with the information present in request headers. 166 - func (s *SSPI) newUser(ctx context.Context, username string, cfg *sspi.Source) (*user_model.User, error) { 167 - email := gouuid.New().String() + "@localhost.localdomain" 168 - user := &user_model.User{ 169 - Name: username, 170 - Email: email, 171 - Language: cfg.DefaultLanguage, 172 - } 173 - emailNotificationPreference := user_model.EmailNotificationsDisabled 174 - overwriteDefault := &user_model.CreateUserOverwriteOptions{ 175 - IsActive: optional.Some(cfg.AutoActivateUsers), 176 - KeepEmailPrivate: optional.Some(true), 177 - EmailNotificationsPreference: &emailNotificationPreference, 178 - } 179 - if err := user_model.CreateUser(ctx, user, overwriteDefault); err != nil { 180 - return nil, err 181 - } 182 - 183 - return user, nil 184 - } 185 - 186 - // stripDomainNames removes NETBIOS domain name and separator from down-level logon names 187 - // (eg. "DOMAIN\user" becomes "user"), and removes the UPN suffix (domain name) and separator 188 - // from UPNs (eg. "user@domain.local" becomes "user") 189 - func stripDomainNames(username string) string { 190 - if strings.Contains(username, "\\") { 191 - parts := strings.SplitN(username, "\\", 2) 192 - if len(parts) > 1 { 193 - username = parts[1] 194 - } 195 - } else if strings.Contains(username, "@") { 196 - parts := strings.Split(username, "@") 197 - if len(parts) > 1 { 198 - username = parts[0] 199 - } 200 - } 201 - return username 202 - } 203 - 204 - func replaceSeparators(username string, cfg *sspi.Source) string { 205 - newSep := cfg.SeparatorReplacement 206 - username = strings.ReplaceAll(username, "\\", newSep) 207 - username = strings.ReplaceAll(username, "/", newSep) 208 - username = strings.ReplaceAll(username, "@", newSep) 209 - return username 210 - } 211 - 212 - func sanitizeUsername(username string, cfg *sspi.Source) string { 213 - if len(username) == 0 { 214 - return "" 215 - } 216 - if cfg.StripDomainNames { 217 - username = stripDomainNames(username) 218 - } 219 - // Replace separators even if we have already stripped the domain name part, 220 - // as the username can contain several separators: eg. "MICROSOFT\useremail@live.com" 221 - username = replaceSeparators(username, cfg) 222 - return username 223 - }
-30
services/auth/sspiauth_posix.go
··· 1 - // Copyright 2023 The Gitea Authors. All rights reserved. 2 - // SPDX-License-Identifier: MIT 3 - 4 - //go:build !windows 5 - 6 - package auth 7 - 8 - import ( 9 - "errors" 10 - "net/http" 11 - ) 12 - 13 - type SSPIUserInfo struct { 14 - Username string // Name of user, usually in the form DOMAIN\User 15 - Groups []string // The global groups the user is a member of 16 - } 17 - 18 - type sspiAuthMock struct{} 19 - 20 - func (s sspiAuthMock) AppendAuthenticateHeader(w http.ResponseWriter, data string) { 21 - } 22 - 23 - func (s sspiAuthMock) Authenticate(r *http.Request, w http.ResponseWriter) (userInfo *SSPIUserInfo, outToken string, err error) { 24 - return nil, "", errors.New("not implemented") 25 - } 26 - 27 - func sspiAuthInit() error { 28 - sspiAuth = &sspiAuthMock{} // TODO: we can mock the SSPI auth in tests 29 - return nil 30 - }
-5
services/forms/auth_form.go
··· 77 77 Oauth2GroupTeamMapRemoval bool 78 78 Oauth2AttributeSSHPublicKey string 79 79 SkipLocalTwoFA bool 80 - SSPIAutoCreateUsers bool 81 - SSPIAutoActivateUsers bool 82 - SSPIStripDomainNames bool 83 - SSPISeparatorReplacement string `binding:"AlphaDashDot;MaxSize(5)"` 84 - SSPIDefaultLanguage string 85 80 GroupTeamMap string `binding:"ValidGroupTeamMap"` 86 81 GroupTeamMapRemoval bool 87 82 }
-45
templates/admin/auth/edit.tmpl
··· 380 380 </div> 381 381 {{end}} 382 382 383 - <!-- SSPI --> 384 - {{if .Source.IsSSPI}} 385 - {{$cfg:=.Source.Cfg}} 386 - <div class="field"> 387 - <div class="ui checkbox"> 388 - <label for="sspi_auto_create_users"><strong>{{ctx.Locale.Tr "admin.auths.sspi_auto_create_users"}}</strong></label> 389 - <input id="sspi_auto_create_users" name="sspi_auto_create_users" class="sspi-auto-create-users" type="checkbox" {{if $cfg.AutoCreateUsers}}checked{{end}}> 390 - <p class="help">{{ctx.Locale.Tr "admin.auths.sspi_auto_create_users_helper"}}</p> 391 - </div> 392 - </div> 393 - <div class="field"> 394 - <div class="ui checkbox"> 395 - <label for="sspi_auto_activate_users"><strong>{{ctx.Locale.Tr "admin.auths.sspi_auto_activate_users"}}</strong></label> 396 - <input id="sspi_auto_activate_users" name="sspi_auto_activate_users" class="sspi-auto-activate-users" type="checkbox" {{if $cfg.AutoActivateUsers}}checked{{end}}> 397 - <p class="help">{{ctx.Locale.Tr "admin.auths.sspi_auto_activate_users_helper"}}</p> 398 - </div> 399 - </div> 400 - <div class="field"> 401 - <div class="ui checkbox"> 402 - <label for="sspi_strip_domain_names"><strong>{{ctx.Locale.Tr "admin.auths.sspi_strip_domain_names"}}</strong></label> 403 - <input id="sspi_strip_domain_names" name="sspi_strip_domain_names" class="sspi-strip-domain-names" type="checkbox" {{if $cfg.StripDomainNames}}checked{{end}}> 404 - <p class="help">{{ctx.Locale.Tr "admin.auths.sspi_strip_domain_names_helper"}}</p> 405 - </div> 406 - </div> 407 - <div class="required field"> 408 - <label for="sspi_separator_replacement">{{ctx.Locale.Tr "admin.auths.sspi_separator_replacement"}}</label> 409 - <input id="sspi_separator_replacement" name="sspi_separator_replacement" value="{{$cfg.SeparatorReplacement}}" required> 410 - <p class="help">{{ctx.Locale.Tr "admin.auths.sspi_separator_replacement_helper"}}</p> 411 - </div> 412 - <div class="field"> 413 - <label for="sspi_default_language">{{ctx.Locale.Tr "admin.auths.sspi_default_language"}}</label> 414 - <div class="ui language selection dropdown" id="sspi_default_language"> 415 - <input name="sspi_default_language" type="hidden" value="{{$cfg.DefaultLanguage}}"> 416 - {{svg "octicon-triangle-down" 14 "dropdown icon"}} 417 - <div class="text">{{range .AllLangs}}{{if eq $cfg.DefaultLanguage .Lang}}{{.Name}}{{end}}{{end}}</div> 418 - <div class="menu"> 419 - <div class="item{{if not $.SSPIDefaultLanguage}} active selected{{end}}" data-value="">-</div> 420 - {{range .AllLangs}} 421 - <div class="item{{if eq $cfg.DefaultLanguage .Lang}} active selected{{end}}" data-value="{{.Lang}}">{{.Name}}</div> 422 - {{end}} 423 - </div> 424 - </div> 425 - <p class="help">{{ctx.Locale.Tr "admin.auths.sspi_default_language_helper"}}</p> 426 - </div> 427 - {{end}} 428 383 {{if .Source.IsLDAP}} 429 384 <div class="inline field"> 430 385 <div class="ui checkbox">
-3
templates/admin/auth/new.tmpl
··· 50 50 <!-- OAuth2 --> 51 51 {{template "admin/auth/source/oauth" .}} 52 52 53 - <!-- SSPI --> 54 - {{template "admin/auth/source/sspi" .}} 55 - 56 53 <div class="ldap field"> 57 54 <div class="ui checkbox"> 58 55 <label><strong>{{ctx.Locale.Tr "admin.auths.attributes_in_bind"}}</strong></label>
-43
templates/admin/auth/source/sspi.tmpl
··· 1 - <div class="sspi field {{if not (eq .type 7)}}tw-hidden{{end}}"> 2 - <div class="field"> 3 - <div class="ui checkbox"> 4 - <label for="sspi_auto_create_users"><strong>{{ctx.Locale.Tr "admin.auths.sspi_auto_create_users"}}</strong></label> 5 - <input id="sspi_auto_create_users" name="sspi_auto_create_users" class="sspi-auto-create-users" type="checkbox" {{if .SSPIAutoCreateUsers}}checked{{end}}> 6 - <p class="help">{{ctx.Locale.Tr "admin.auths.sspi_auto_create_users_helper"}}</p> 7 - </div> 8 - </div> 9 - <div class="field"> 10 - <div class="ui checkbox"> 11 - <label for="sspi_auto_activate_users"><strong>{{ctx.Locale.Tr "admin.auths.sspi_auto_activate_users"}}</strong></label> 12 - <input id="sspi_auto_activate_users" name="sspi_auto_activate_users" class="sspi-auto-activate-users" type="checkbox" {{if .SSPIAutoActivateUsers}}checked{{end}}> 13 - <p class="help">{{ctx.Locale.Tr "admin.auths.sspi_auto_activate_users_helper"}}</p> 14 - </div> 15 - </div> 16 - <div class="field"> 17 - <div class="ui checkbox"> 18 - <label for="sspi_strip_domain_names"><strong>{{ctx.Locale.Tr "admin.auths.sspi_strip_domain_names"}}</strong></label> 19 - <input id="sspi_strip_domain_names" name="sspi_strip_domain_names" class="sspi-strip-domain-names" type="checkbox" {{if .SSPIStripDomainNames}}checked{{end}}> 20 - <p class="help">{{ctx.Locale.Tr "admin.auths.sspi_strip_domain_names_helper"}}</p> 21 - </div> 22 - </div> 23 - <div class="required field"> 24 - <label for="sspi_separator_replacement">{{ctx.Locale.Tr "admin.auths.sspi_separator_replacement"}}</label> 25 - <input id="sspi_separator_replacement" name="sspi_separator_replacement" value="{{.SSPISeparatorReplacement}}"> 26 - <p class="help">{{ctx.Locale.Tr "admin.auths.sspi_separator_replacement_helper"}}</p> 27 - </div> 28 - <div class="field"> 29 - <label for="sspi_default_language">{{ctx.Locale.Tr "admin.auths.sspi_default_language"}}</label> 30 - <div class="ui language selection dropdown" id="sspi_default_language"> 31 - <input name="sspi_default_language" type="hidden" value="{{.SSPIDefaultLanguage}}"> 32 - {{svg "octicon-triangle-down" 14 "dropdown icon"}} 33 - <div class="text">{{range .AllLangs}}{{if eq $.SSPIDefaultLanguage .Lang}}{{.Name}}{{end}}{{end}}</div> 34 - <div class="menu"> 35 - <div class="item{{if not $.SSPIDefaultLanguage}} active selected{{end}}" data-value="">-</div> 36 - {{range .AllLangs}} 37 - <div class="item{{if eq $.SSPIDefaultLanguage .Lang}} active selected{{end}}" data-value="{{.Lang}}">{{.Name}}</div> 38 - {{end}} 39 - </div> 40 - </div> 41 - <p class="help">{{ctx.Locale.Tr "admin.auths.sspi_default_language_helper"}}</p> 42 - </div> 43 - </div>
-6
templates/user/auth/oauth_container.tmpl
··· 19 19 {{ctx.Locale.Tr "auth.sign_in_openid"}} 20 20 </a> 21 21 {{end}} 22 - {{if .EnableSSPI}} 23 - <a class="ui button tw-flex tw-items-center tw-justify-center tw-py-2 tw-w-full" rel="nofollow" href="{{AppSubUrl}}/user/login?auth_with_sspi=1"> 24 - {{svg "fontawesome-windows"}} 25 - &nbsp;SSPI 26 - </a> 27 - {{end}} 28 22 </div> 29 23 </div> 30 24 </div>
-3
tests/test_utils.go
··· 66 66 setting.CustomPath = filepath.Join(setting.AppWorkPath, "custom") 67 67 if requireGitea { 68 68 giteaBinary := "gitea" 69 - if setting.IsWindows { 70 - giteaBinary += ".exe" 71 - } 72 69 setting.AppPath = path.Join(giteaRoot, giteaBinary) 73 70 if _, err := os.Stat(setting.AppPath); err != nil { 74 71 exitf("Could not find gitea binary at %s", setting.AppPath)
+2 -8
web_src/js/features/admin/common.js
··· 123 123 // New authentication 124 124 if (document.querySelector('.admin.new.authentication')) { 125 125 document.getElementById('auth_type')?.addEventListener('change', function () { 126 - hideElem('.ldap, .dldap, .smtp, .pam, .oauth2, .has-tls, .search-page-size, .sspi'); 126 + hideElem('.ldap, .dldap, .smtp, .pam, .oauth2, .has-tls, .search-page-size'); 127 127 128 - for (const input of document.querySelectorAll('.ldap input[required], .binddnrequired input[required], .dldap input[required], .smtp input[required], .pam input[required], .oauth2 input[required], .has-tls input[required], .sspi input[required]')) { 128 + for (const input of document.querySelectorAll('.ldap input[required], .binddnrequired input[required], .dldap input[required], .smtp input[required], .pam input[required], .oauth2 input[required], .has-tls input[required]')) { 129 129 input.removeAttribute('required'); 130 130 } 131 131 ··· 165 165 input.setAttribute('required', 'required'); 166 166 } 167 167 onOAuth2Change(true); 168 - break; 169 - case '7': // SSPI 170 - showElem('.sspi'); 171 - for (const input of document.querySelectorAll('.sspi div.required input')) { 172 - input.setAttribute('required', 'required'); 173 - } 174 168 break; 175 169 } 176 170 if (authType === '2' || authType === '5') {