From d519a39302f38bb4dcd4174639d5699dd23f5baa Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 1 Nov 2023 19:17:18 +0800 Subject: [PATCH 01/37] Support storage base path as prefix (#27827) This PR adds a prefix path for all minio storage and override base path will override the path. The previous behavior is undefined officially, so it will be marked as breaking. --- modules/setting/storage.go | 17 +++++++++-- modules/setting/storage_test.go | 53 +++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 3 deletions(-) diff --git a/modules/setting/storage.go b/modules/setting/storage.go index cc3a2899d76ed..f937c7cff3990 100644 --- a/modules/setting/storage.go +++ b/modules/setting/storage.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "path/filepath" + "strings" ) // StorageType is a type of Storage @@ -249,14 +250,24 @@ func getStorageForMinio(targetSec, overrideSec ConfigSection, tp targetSecType, return nil, fmt.Errorf("map minio config failed: %v", err) } - if storage.MinioConfig.BasePath == "" { - storage.MinioConfig.BasePath = name + "/" + var defaultPath string + if storage.MinioConfig.BasePath != "" { + if tp == targetSecIsStorage || tp == targetSecIsDefault { + defaultPath = strings.TrimSuffix(storage.MinioConfig.BasePath, "/") + "/" + name + "/" + } else { + defaultPath = storage.MinioConfig.BasePath + } + } + if defaultPath == "" { + defaultPath = name + "/" } if overrideSec != nil { storage.MinioConfig.ServeDirect = ConfigSectionKeyBool(overrideSec, "SERVE_DIRECT", storage.MinioConfig.ServeDirect) - storage.MinioConfig.BasePath = ConfigSectionKeyString(overrideSec, "MINIO_BASE_PATH", storage.MinioConfig.BasePath) + storage.MinioConfig.BasePath = ConfigSectionKeyString(overrideSec, "MINIO_BASE_PATH", defaultPath) storage.MinioConfig.Bucket = ConfigSectionKeyString(overrideSec, "MINIO_BUCKET", storage.MinioConfig.Bucket) + } else { + storage.MinioConfig.BasePath = defaultPath } return &storage, nil } diff --git a/modules/setting/storage_test.go b/modules/setting/storage_test.go index 20886d4c4e959..6f38bf1d55754 100644 --- a/modules/setting/storage_test.go +++ b/modules/setting/storage_test.go @@ -412,3 +412,56 @@ MINIO_USE_SSL = true assert.EqualValues(t, true, RepoArchive.Storage.MinioConfig.UseSSL) assert.EqualValues(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath) } + +func Test_getStorageConfiguration28(t *testing.T) { + cfg, err := NewConfigProviderFromData(` +[storage] +STORAGE_TYPE = minio +MINIO_ACCESS_KEY_ID = my_access_key +MINIO_SECRET_ACCESS_KEY = my_secret_key +MINIO_USE_SSL = true +MINIO_BASE_PATH = /prefix +`) + assert.NoError(t, err) + assert.NoError(t, loadRepoArchiveFrom(cfg)) + assert.EqualValues(t, "my_access_key", RepoArchive.Storage.MinioConfig.AccessKeyID) + assert.EqualValues(t, "my_secret_key", RepoArchive.Storage.MinioConfig.SecretAccessKey) + assert.EqualValues(t, true, RepoArchive.Storage.MinioConfig.UseSSL) + assert.EqualValues(t, "/prefix/repo-archive/", RepoArchive.Storage.MinioConfig.BasePath) + + cfg, err = NewConfigProviderFromData(` +[storage] +STORAGE_TYPE = minio +MINIO_ACCESS_KEY_ID = my_access_key +MINIO_SECRET_ACCESS_KEY = my_secret_key +MINIO_USE_SSL = true +MINIO_BASE_PATH = /prefix + +[lfs] +MINIO_BASE_PATH = /lfs +`) + assert.NoError(t, err) + assert.NoError(t, loadLFSFrom(cfg)) + assert.EqualValues(t, "my_access_key", LFS.Storage.MinioConfig.AccessKeyID) + assert.EqualValues(t, "my_secret_key", LFS.Storage.MinioConfig.SecretAccessKey) + assert.EqualValues(t, true, LFS.Storage.MinioConfig.UseSSL) + assert.EqualValues(t, "/lfs", LFS.Storage.MinioConfig.BasePath) + + cfg, err = NewConfigProviderFromData(` +[storage] +STORAGE_TYPE = minio +MINIO_ACCESS_KEY_ID = my_access_key +MINIO_SECRET_ACCESS_KEY = my_secret_key +MINIO_USE_SSL = true +MINIO_BASE_PATH = /prefix + +[storage.lfs] +MINIO_BASE_PATH = /lfs +`) + assert.NoError(t, err) + assert.NoError(t, loadLFSFrom(cfg)) + assert.EqualValues(t, "my_access_key", LFS.Storage.MinioConfig.AccessKeyID) + assert.EqualValues(t, "my_secret_key", LFS.Storage.MinioConfig.SecretAccessKey) + assert.EqualValues(t, true, LFS.Storage.MinioConfig.UseSSL) + assert.EqualValues(t, "/lfs", LFS.Storage.MinioConfig.BasePath) +} From 665d12cf8485d8be2189208b60f371365d56bb0e Mon Sep 17 00:00:00 2001 From: nodiscc Date: Wed, 1 Nov 2023 15:14:40 +0000 Subject: [PATCH 02/37] doc: actions/act-runner: document running as a systemd service (#27844) This documents running `act-runner` as a systemd service under a dedicated user account. --- .../content/usage/actions/act-runner.en-us.md | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/docs/content/usage/actions/act-runner.en-us.md b/docs/content/usage/actions/act-runner.en-us.md index 33813f5910d62..6e99beb870ac6 100644 --- a/docs/content/usage/actions/act-runner.en-us.md +++ b/docs/content/usage/actions/act-runner.en-us.md @@ -268,6 +268,40 @@ The runner will fetch jobs from the Gitea instance and run them automatically. Since act runner is still in development, it is recommended to check the latest version and upgrade it regularly. +## Systemd service + +It is also possible to run act-runner as a [systemd](https://en.wikipedia.org/wiki/Systemd) service. Create an unprivileged `act_runner` user on your system, and the following file in `/etc/systemd/system/act_runner.service`. The paths in `ExecStart` and `WorkingDirectory` may need to be adjusted depending on where you installed the `act_runner` binary, its configuration file, and the home directory of the `act_runner` user. + +```ini +[Unit] +Description=Gitea Actions runner +Documentation=https://gitea.com/gitea/act_runner +After=docker.service + +[Service] +ExecStart=/usr/local/bin/act_runner daemon --config /etc/act_runner/config.yaml +ExecReload=/bin/kill -s HUP $MAINPID +WorkingDirectory=/var/lib/act_runner +TimeoutSec=0 +RestartSec=10 +Restart=always +User=act_runner + +[Install] +WantedBy=multi-user.target +``` + +Then: + +```bash +# load the new systemd unit file +sudo systemctl daemon-reload +# start the service and enable it at boot +sudo systemctl enable act_runner --now +``` + +If using Docker, the `act_runner` user should also be added to the `docker` group before starting the service. Keep in mind that this effectively gives `act_runner` root access to the system [[1]](https://docs.docker.com/engine/security/#docker-daemon-attack-surface). + ## Configuration variable You can create configuration variables on the user, organization and repository level. From 9b6e77c489edf3af3a9782c977cc04fde7aa2fd9 Mon Sep 17 00:00:00 2001 From: Moritz Poldrack <33086936+mpldr@users.noreply.github.com> Date: Wed, 1 Nov 2023 19:00:20 +0100 Subject: [PATCH 03/37] refactor postgres connection string building (#27723) This patchset changes the connection string builder to use net.URL and the host/port parser to use the stdlib function for splitting host from port. It also adds a footnote about a potentially required portnumber for postgres UNIX sockets. Fixes: #24552 --- .../config-cheat-sheet.en-us.md | 4 +- modules/setting/database.go | 40 ++++++++++++------- modules/setting/database_test.go | 33 ++++++++------- 3 files changed, 47 insertions(+), 30 deletions(-) diff --git a/docs/content/administration/config-cheat-sheet.en-us.md b/docs/content/administration/config-cheat-sheet.en-us.md index 617715fbaa12e..715bd6d325f59 100644 --- a/docs/content/administration/config-cheat-sheet.en-us.md +++ b/docs/content/administration/config-cheat-sheet.en-us.md @@ -424,7 +424,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a ## Database (`database`) - `DB_TYPE`: **mysql**: The database type in use \[mysql, postgres, mssql, sqlite3\]. -- `HOST`: **127.0.0.1:3306**: Database host address and port or absolute path for unix socket \[mysql, postgres\] (ex: /var/run/mysqld/mysqld.sock). +- `HOST`: **127.0.0.1:3306**: Database host address and port or absolute path for unix socket \[mysql, postgres[^1]\] (ex: /var/run/mysqld/mysqld.sock). - `NAME`: **gitea**: Database name. - `USER`: **root**: Database username. - `PASSWD`: **_empty_**: Database user password. Use \`your password\` or """your password""" for quoting if you use special characters in the password. @@ -455,6 +455,8 @@ The following configuration set `Content-Type: application/vnd.android.package-a - `CONN_MAX_LIFETIME` **0 or 3s**: Sets the maximum amount of time a DB connection may be reused - default is 0, meaning there is no limit (except on MySQL where it is 3s - see #6804 & #7071). - `AUTO_MIGRATION` **true**: Whether execute database models migrations automatically. +[^1]: It may be necessary to specify a hostport even when listening on a unix socket, as the port is part of the socket name. see [#24552](https://github.com/go-gitea/gitea/issues/24552#issuecomment-1681649367) for additional details. + Please see #8540 & #8273 for further discussion of the appropriate values for `MAX_OPEN_CONNS`, `MAX_IDLE_CONNS` & `CONN_MAX_LIFETIME` and their relation to port exhaustion. diff --git a/modules/setting/database.go b/modules/setting/database.go index 709655368c67b..aa42f506bc51b 100644 --- a/modules/setting/database.go +++ b/modules/setting/database.go @@ -6,6 +6,7 @@ package setting import ( "errors" "fmt" + "net" "net/url" "os" "path" @@ -135,15 +136,18 @@ func DBConnStr() (string, error) { // parsePostgreSQLHostPort parses given input in various forms defined in // https://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING // and returns proper host and port number. -func parsePostgreSQLHostPort(info string) (string, string) { - host, port := "127.0.0.1", "5432" - if strings.Contains(info, ":") && !strings.HasSuffix(info, "]") { - idx := strings.LastIndex(info, ":") - host = info[:idx] - port = info[idx+1:] - } else if len(info) > 0 { +func parsePostgreSQLHostPort(info string) (host, port string) { + if h, p, err := net.SplitHostPort(info); err == nil { + host, port = h, p + } else { + // treat the "info" as "host", if it's an IPv6 address, remove the wrapper host = info + if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") { + host = host[1 : len(host)-1] + } } + + // set fallback values if host == "" { host = "127.0.0.1" } @@ -155,14 +159,22 @@ func parsePostgreSQLHostPort(info string) (string, string) { func getPostgreSQLConnectionString(dbHost, dbUser, dbPasswd, dbName, dbParam, dbsslMode string) (connStr string) { host, port := parsePostgreSQLHostPort(dbHost) - if host[0] == '/' { // looks like a unix socket - connStr = fmt.Sprintf("postgres://%s:%s@:%s/%s%ssslmode=%s&host=%s", - url.PathEscape(dbUser), url.PathEscape(dbPasswd), port, dbName, dbParam, dbsslMode, host) - } else { - connStr = fmt.Sprintf("postgres://%s:%s@%s:%s/%s%ssslmode=%s", - url.PathEscape(dbUser), url.PathEscape(dbPasswd), host, port, dbName, dbParam, dbsslMode) + connURL := url.URL{ + Scheme: "postgres", + User: url.UserPassword(dbUser, dbPasswd), + Host: net.JoinHostPort(host, port), + Path: dbName, + OmitHost: false, + RawQuery: dbParam, + } + query := connURL.Query() + if dbHost[0] == '/' { // looks like a unix socket + query.Add("host", dbHost) + connURL.Host = ":" + port } - return connStr + query.Set("sslmode", dbsslMode) + connURL.RawQuery = query.Encode() + return connURL.String() } // ParseMSSQLHostPort splits the host into host and port diff --git a/modules/setting/database_test.go b/modules/setting/database_test.go index 481ca969b1fb2..85271c36cb388 100644 --- a/modules/setting/database_test.go +++ b/modules/setting/database_test.go @@ -10,46 +10,49 @@ import ( ) func Test_parsePostgreSQLHostPort(t *testing.T) { - tests := []struct { + tests := map[string]struct { HostPort string Host string Port string }{ - { + "host-port": { HostPort: "127.0.0.1:1234", Host: "127.0.0.1", Port: "1234", }, - { + "no-port": { HostPort: "127.0.0.1", Host: "127.0.0.1", Port: "5432", }, - { + "ipv6-port": { HostPort: "[::1]:1234", - Host: "[::1]", + Host: "::1", Port: "1234", }, - { + "ipv6-no-port": { HostPort: "[::1]", - Host: "[::1]", + Host: "::1", Port: "5432", }, - { + "unix-socket": { HostPort: "/tmp/pg.sock:1234", Host: "/tmp/pg.sock", Port: "1234", }, - { + "unix-socket-no-port": { HostPort: "/tmp/pg.sock", Host: "/tmp/pg.sock", Port: "5432", }, } - for _, test := range tests { - host, port := parsePostgreSQLHostPort(test.HostPort) - assert.Equal(t, test.Host, host) - assert.Equal(t, test.Port, port) + for k, test := range tests { + t.Run(k, func(t *testing.T) { + t.Log(test.HostPort) + host, port := parsePostgreSQLHostPort(test.HostPort) + assert.Equal(t, test.Host, host) + assert.Equal(t, test.Port, port) + }) } } @@ -72,7 +75,7 @@ func Test_getPostgreSQLConnectionString(t *testing.T) { Name: "gitea", Param: "", SSLMode: "false", - Output: "postgres://testuser:space%20space%20%21%23$%25%5E%5E%25%5E%60%60%60-=%3F=@:5432/giteasslmode=false&host=/tmp/pg.sock", + Output: "postgres://testuser:space%20space%20%21%23$%25%5E%5E%25%5E%60%60%60-=%3F=@:5432/gitea?host=%2Ftmp%2Fpg.sock&sslmode=false", }, { Host: "localhost", @@ -82,7 +85,7 @@ func Test_getPostgreSQLConnectionString(t *testing.T) { Name: "gitea", Param: "", SSLMode: "true", - Output: "postgres://pgsqlusername:I%20love%20Gitea%21@localhost:5432/giteasslmode=true", + Output: "postgres://pgsqlusername:I%20love%20Gitea%21@localhost:5432/gitea?sslmode=true", }, } From e378545f3083990eb36ff5d72477662d9787280d Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Thu, 2 Nov 2023 00:46:26 +0100 Subject: [PATCH 04/37] Filter inactive auth sources (#27870) Fix nil access for inactive auth sources. > Render failed, failed to render template: user/settings/security/security, error: template error: builtin(static):user/settings/security/accountlinks:32:20 : executing "user/settings/security/accountlinks" at <$providerData.IconHTML>: nil pointer evaluating oauth2.Provider.IconHTML Code tries to access the auth source of an `ExternalLoginUser` but the list contains only the active auth sources. --- routers/web/user/setting/security/security.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routers/web/user/setting/security/security.go b/routers/web/user/setting/security/security.go index c687f7314d401..58c637e2b3365 100644 --- a/routers/web/user/setting/security/security.go +++ b/routers/web/user/setting/security/security.go @@ -82,7 +82,7 @@ func loadSecurityData(ctx *context.Context) { // map the provider display name with the AuthSource sources := make(map[*auth_model.Source]string) for _, externalAccount := range accountLinks { - if authSource, err := auth_model.GetSourceByID(ctx, externalAccount.LoginSourceID); err == nil { + if authSource, err := auth_model.GetSourceByID(ctx, externalAccount.LoginSourceID); err == nil && authSource.IsActive { var providerDisplayName string type DisplayNamed interface { From dc52f26d46f12b7e3c647bec3514e6c66684a3af Mon Sep 17 00:00:00 2001 From: silverwind Date: Thu, 2 Nov 2023 05:30:38 +0100 Subject: [PATCH 05/37] Reduce margin/padding on flex-list items and divider (#27872) Small CSS tweak, reduces margin/padding from 14px to 10px, which I think looks better --- web_src/css/modules/divider.css | 4 ++-- web_src/css/shared/flex-list.css | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/web_src/css/modules/divider.css b/web_src/css/modules/divider.css index 8c9649d6ba801..48560bd3d9089 100644 --- a/web_src/css/modules/divider.css +++ b/web_src/css/modules/divider.css @@ -1,5 +1,5 @@ .divider { - margin: 1rem 0; + margin: 10px 0; height: 0; font-weight: var(--font-weight-medium); text-transform: uppercase; @@ -15,7 +15,7 @@ .divider.divider-text { display: flex; align-items: center; - padding: 7px 0; + padding: 5px 0; } .divider.divider-text::before, diff --git a/web_src/css/shared/flex-list.css b/web_src/css/shared/flex-list.css index 93d8c9b49caf6..e1ad6e7f973fc 100644 --- a/web_src/css/shared/flex-list.css +++ b/web_src/css/shared/flex-list.css @@ -6,7 +6,7 @@ display: flex; gap: 8px; align-items: flex-start; - padding: 1em 0; + padding: 10px 0; } .flex-item .flex-item-leading { From 4776fde9e1caa7cee5671715144a668e19a0323c Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Thu, 2 Nov 2023 11:42:02 +0100 Subject: [PATCH 06/37] Display issue task list on project cards (#27865) Display the issue task list on project cards. ![grafik](https://github.com/go-gitea/gitea/assets/1666336/e6cb3196-8980-403c-9795-3a7b03fbfb3c) Co-authored-by: Giteabot --- models/issues/issue.go | 12 ++---------- templates/repo/issue/card.tmpl | 7 +++++++ 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/models/issues/issue.go b/models/issues/issue.go index 8655b9de3fe42..b0ff0adddda18 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -142,22 +142,14 @@ type Issue struct { } var ( - issueTasksPat *regexp.Regexp - issueTasksDonePat *regexp.Regexp -) - -const ( - issueTasksRegexpStr = `(^\s*[-*]\s\[[\sxX]\]\s.)|(\n\s*[-*]\s\[[\sxX]\]\s.)` - issueTasksDoneRegexpStr = `(^\s*[-*]\s\[[xX]\]\s.)|(\n\s*[-*]\s\[[xX]\]\s.)` + issueTasksPat = regexp.MustCompile(`(^\s*[-*]\s\[[\sxX]\]\s.)|(\n\s*[-*]\s\[[\sxX]\]\s.)`) + issueTasksDonePat = regexp.MustCompile(`(^\s*[-*]\s\[[xX]\]\s.)|(\n\s*[-*]\s\[[xX]\]\s.)`) ) // IssueIndex represents the issue index table type IssueIndex db.ResourceIndex func init() { - issueTasksPat = regexp.MustCompile(issueTasksRegexpStr) - issueTasksDonePat = regexp.MustCompile(issueTasksDoneRegexpStr) - db.RegisterModel(new(Issue)) db.RegisterModel(new(IssueIndex)) } diff --git a/templates/repo/issue/card.tmpl b/templates/repo/issue/card.tmpl index 05b7dbaabcf50..3cc853b351314 100644 --- a/templates/repo/issue/card.tmpl +++ b/templates/repo/issue/card.tmpl @@ -49,6 +49,13 @@ {{end}} {{end}} + {{$tasks := .GetTasks}} + {{if gt $tasks 0}} +
+ {{svg "octicon-checklist" 16 "gt-mr-2 gt-vm"}} + {{.GetTasksDone}} / {{$tasks}} +
+ {{end}} {{if or .Labels .Assignees}} From 0ba4ecc3bd8443f0d3a834530a44e0c1334554b7 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 2 Nov 2023 22:14:33 +0800 Subject: [PATCH 07/37] Fix http protocol auth (#27875) --- routers/web/githttp.go | 43 +++++++++++++++++++ routers/web/repo/{http.go => githttp.go} | 0 .../repo/{http_test.go => githttp_test.go} | 0 routers/web/web.go | 18 ++------ 4 files changed, 47 insertions(+), 14 deletions(-) create mode 100644 routers/web/githttp.go rename routers/web/repo/{http.go => githttp.go} (100%) rename routers/web/repo/{http_test.go => githttp_test.go} (100%) diff --git a/routers/web/githttp.go b/routers/web/githttp.go new file mode 100644 index 0000000000000..b2fb5b472f772 --- /dev/null +++ b/routers/web/githttp.go @@ -0,0 +1,43 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package web + +import ( + "net/http" + + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/routers/web/repo" + context_service "code.gitea.io/gitea/services/context" +) + +func requireSignIn(ctx *context.Context) { + if !setting.Service.RequireSignInView { + return + } + + // rely on the results of Contexter + if !ctx.IsSigned { + // TODO: support digit auth - which would be Authorization header with digit + ctx.Resp.Header().Set("WWW-Authenticate", `Basic realm="Gitea"`) + ctx.Error(http.StatusUnauthorized) + } +} + +func gitHTTPRouters(m *web.Route) { + m.Group("", func() { + m.PostOptions("/git-upload-pack", repo.ServiceUploadPack) + m.PostOptions("/git-receive-pack", repo.ServiceReceivePack) + m.GetOptions("/info/refs", repo.GetInfoRefs) + m.GetOptions("/HEAD", repo.GetTextFile("HEAD")) + m.GetOptions("/objects/info/alternates", repo.GetTextFile("objects/info/alternates")) + m.GetOptions("/objects/info/http-alternates", repo.GetTextFile("objects/info/http-alternates")) + m.GetOptions("/objects/info/packs", repo.GetInfoPacks) + m.GetOptions("/objects/info/{file:[^/]*}", repo.GetTextFile("")) + m.GetOptions("/objects/{head:[0-9a-f]{2}}/{hash:[0-9a-f]{38}}", repo.GetLooseObject) + m.GetOptions("/objects/pack/pack-{file:[0-9a-f]{40}}.pack", repo.GetPackFile) + m.GetOptions("/objects/pack/pack-{file:[0-9a-f]{40}}.idx", repo.GetIdxFile) + }, ignSignInAndCsrf, requireSignIn, repo.HTTPGitEnabledHandler, repo.CorsHandler(), context_service.UserAssignmentWeb()) +} diff --git a/routers/web/repo/http.go b/routers/web/repo/githttp.go similarity index 100% rename from routers/web/repo/http.go rename to routers/web/repo/githttp.go diff --git a/routers/web/repo/http_test.go b/routers/web/repo/githttp_test.go similarity index 100% rename from routers/web/repo/http_test.go rename to routers/web/repo/githttp_test.go diff --git a/routers/web/web.go b/routers/web/web.go index 6449f7716cf75..f8b745fb10b55 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -276,6 +276,8 @@ func Routes() *web.Route { return routes } +var ignSignInAndCsrf = verifyAuthWithOptions(&common.VerifyOptions{DisableCSRF: true}) + // registerRoutes register routes func registerRoutes(m *web.Route) { reqSignIn := verifyAuthWithOptions(&common.VerifyOptions{SignInRequired: true}) @@ -283,7 +285,7 @@ func registerRoutes(m *web.Route) { // TODO: rename them to "optSignIn", which means that the "sign-in" could be optional, depends on the VerifyOptions (RequireSignInView) ignSignIn := verifyAuthWithOptions(&common.VerifyOptions{SignInRequired: setting.Service.RequireSignInView}) ignExploreSignIn := verifyAuthWithOptions(&common.VerifyOptions{SignInRequired: setting.Service.RequireSignInView || setting.Service.Explore.RequireSigninView}) - ignSignInAndCsrf := verifyAuthWithOptions(&common.VerifyOptions{DisableCSRF: true}) + validation.AddBindingRules() linkAccountEnabled := func(ctx *context.Context) { @@ -1512,19 +1514,7 @@ func registerRoutes(m *web.Route) { }) }, ignSignInAndCsrf, lfsServerEnabled) - m.Group("", func() { - m.PostOptions("/git-upload-pack", repo.ServiceUploadPack) - m.PostOptions("/git-receive-pack", repo.ServiceReceivePack) - m.GetOptions("/info/refs", repo.GetInfoRefs) - m.GetOptions("/HEAD", repo.GetTextFile("HEAD")) - m.GetOptions("/objects/info/alternates", repo.GetTextFile("objects/info/alternates")) - m.GetOptions("/objects/info/http-alternates", repo.GetTextFile("objects/info/http-alternates")) - m.GetOptions("/objects/info/packs", repo.GetInfoPacks) - m.GetOptions("/objects/info/{file:[^/]*}", repo.GetTextFile("")) - m.GetOptions("/objects/{head:[0-9a-f]{2}}/{hash:[0-9a-f]{38}}", repo.GetLooseObject) - m.GetOptions("/objects/pack/pack-{file:[0-9a-f]{40}}.pack", repo.GetPackFile) - m.GetOptions("/objects/pack/pack-{file:[0-9a-f]{40}}.idx", repo.GetIdxFile) - }, ignSignInAndCsrf, repo.HTTPGitEnabledHandler, repo.CorsHandler(), context_service.UserAssignmentWeb()) + gitHTTPRouters(m) }) }) // ***** END: Repository ***** From dcb648ee71853073d54e8a6e107b764212ede58e Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Thu, 2 Nov 2023 23:49:02 +0900 Subject: [PATCH 08/37] Add `Hide/Show all checks` button to commit status check (#26284) Step one for a GitHub like commit status check ui: ![image](https://github.com/go-gitea/gitea/assets/18380374/22953b88-1f91-4d19-bc57-ad92d33fa11f) ![image](https://github.com/go-gitea/gitea/assets/18380374/78572a49-c9b0-472b-86a8-8293197e807b) ![image](https://github.com/go-gitea/gitea/assets/18380374/bc5c8d1c-2ab5-4b03-b8c6-20c34b86d856) Step two: ![image](https://github.com/go-gitea/gitea/assets/18380374/938b359e-8823-4192-b82d-55fa40b986fd) ![image](https://github.com/go-gitea/gitea/assets/18380374/2de5bb8f-40f5-462a-8d6d-bac13a32bc2a) The design now will list all commit status checks which takes too much space. This is a pre-improve for #26247 --------- Co-authored-by: delvh Co-authored-by: silverwind Co-authored-by: wxiaoguang --- options/locale/locale_en-US.ini | 2 + templates/repo/commit_page.tmpl | 2 +- templates/repo/commit_statuses.tmpl | 12 +--- templates/repo/commits_list.tmpl | 2 +- templates/repo/commits_list_small.tmpl | 2 +- templates/repo/issue/view_content/pull.tmpl | 9 ++- templates/repo/pulls/status.tmpl | 67 +++++++++++++-------- templates/repo/view_list.tmpl | 2 +- templates/shared/issuelist.tmpl | 2 +- web_src/css/modules/tippy.css | 57 +++++++++++------- web_src/css/repo.css | 38 +++++++----- web_src/js/features/repo-commit.js | 1 + web_src/js/features/repo-issue-pr-status.js | 12 ++++ web_src/js/features/repo-legacy.js | 2 + web_src/js/modules/tippy.js | 2 +- 15 files changed, 132 insertions(+), 80 deletions(-) create mode 100644 web_src/js/features/repo-issue-pr-status.js diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 8c40bbc01df59..a7a7a4f4c50f9 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1786,6 +1786,8 @@ pulls.status_checks_failure = Some checks failed pulls.status_checks_error = Some checks reported errors pulls.status_checks_requested = Required pulls.status_checks_details = Details +pulls.status_checks_hide_all = Hide all checks +pulls.status_checks_show_all = Show all checks pulls.update_branch = Update branch by merge pulls.update_branch_rebase = Update branch by rebase pulls.update_branch_success = Branch update was successful diff --git a/templates/repo/commit_page.tmpl b/templates/repo/commit_page.tmpl index c0dfc6e0ad1a1..efdbb23e00ae7 100644 --- a/templates/repo/commit_page.tmpl +++ b/templates/repo/commit_page.tmpl @@ -19,7 +19,7 @@ {{end}}
-

{{RenderCommitMessage $.Context .Commit.Message $.RepoLink ($.Repository.ComposeMetas ctx)}}{{template "repo/commit_statuses" dict "Status" .CommitStatus "Statuses" .CommitStatuses "root" $}}

+

{{RenderCommitMessage $.Context .Commit.Message $.RepoLink ($.Repository.ComposeMetas ctx)}}{{template "repo/commit_statuses" dict "Status" .CommitStatus "Statuses" .CommitStatuses}}

{{if not $.PageIsWiki}}
diff --git a/templates/repo/commit_statuses.tmpl b/templates/repo/commit_statuses.tmpl index 0006dcb4a70dd..ec2be6c38d5af 100644 --- a/templates/repo/commit_statuses.tmpl +++ b/templates/repo/commit_statuses.tmpl @@ -8,15 +8,7 @@ {{template "repo/commit_status" .Status}} {{end}} -
- {{range .Statuses}} - - {{end}} +
+ {{template "repo/pulls/status" (dict "CommitStatuses" .Statuses "CommitStatus" .Status)}}
{{end}} diff --git a/templates/repo/commits_list.tmpl b/templates/repo/commits_list.tmpl index cdb893f51f265..77f1684245178 100644 --- a/templates/repo/commits_list.tmpl +++ b/templates/repo/commits_list.tmpl @@ -66,7 +66,7 @@ {{if IsMultilineCommitMessage .Message}} {{end}} - {{template "repo/commit_statuses" dict "Status" .Status "Statuses" .Statuses "root" $}} + {{template "repo/commit_statuses" dict "Status" .Status "Statuses" .Statuses}} {{if IsMultilineCommitMessage .Message}}
{{RenderCommitBody $.Context .Message $commitRepoLink ($.Repository.ComposeMetas ctx)}}
{{end}} diff --git a/templates/repo/commits_list_small.tmpl b/templates/repo/commits_list_small.tmpl index 5b715a71d4eed..63eb5945bc2f7 100644 --- a/templates/repo/commits_list_small.tmpl +++ b/templates/repo/commits_list_small.tmpl @@ -14,7 +14,7 @@ {{$commitLink:= printf "%s/commit/%s" $.comment.Issue.PullRequest.BaseRepo.Link (PathEscape .ID.String)}} - {{template "repo/commit_statuses" dict "Status" .Status "Statuses" .Statuses "root" $.root}} + {{template "repo/commit_statuses" dict "Status" .Status "Statuses" .Statuses}} {{$class := "ui sha label"}} {{if .Signature}} {{$class = (print $class " isSigned")}} diff --git a/templates/repo/issue/view_content/pull.tmpl b/templates/repo/issue/view_content/pull.tmpl index e06404376ceb6..2b5776ea03a52 100644 --- a/templates/repo/issue/view_content/pull.tmpl +++ b/templates/repo/issue/view_content/pull.tmpl @@ -20,7 +20,14 @@ {{- else if .Issue.PullRequest.CanAutoMerge}}green {{- else}}red{{end}}">{{svg "octicon-git-merge" 40}}
- {{template "repo/pulls/status" .}} +
+ {{template "repo/pulls/status" (dict + "CommitStatus" .LatestCommitStatus + "CommitStatuses" .LatestCommitStatuses + "ShowHideChecks" true + "is_context_required" .is_context_required + )}} +
{{$showGeneralMergeForm := false}}
{{if .Issue.PullRequest.HasMerged}} diff --git a/templates/repo/pulls/status.tmpl b/templates/repo/pulls/status.tmpl index 476b89a42511f..ae508b8fa4fa2 100644 --- a/templates/repo/pulls/status.tmpl +++ b/templates/repo/pulls/status.tmpl @@ -1,27 +1,43 @@ -{{if $.LatestCommitStatus}} - {{if not $.Issue.PullRequest.HasMerged}} -
- {{if eq .LatestCommitStatus.State "pending"}} - {{ctx.Locale.Tr "repo.pulls.status_checking"}} - {{else if eq .LatestCommitStatus.State "success"}} - {{ctx.Locale.Tr "repo.pulls.status_checks_success"}} - {{else if eq .LatestCommitStatus.State "warning"}} - {{ctx.Locale.Tr "repo.pulls.status_checks_warning"}} - {{else if eq .LatestCommitStatus.State "failure"}} - {{ctx.Locale.Tr "repo.pulls.status_checks_failure"}} - {{else if eq .LatestCommitStatus.State "error"}} - {{ctx.Locale.Tr "repo.pulls.status_checks_error"}} - {{else}} - {{ctx.Locale.Tr "repo.pulls.status_checking"}} - {{end}} -
- {{end}} +{{/* +Template Attributes: +* CommitStatus: summary of all commit status state +* CommitStatuses: all commit status elements +* ShowHideChecks: whether use a button to show/hide the checks +* is_context_required: Used in pull request commit status check table +*/}} + +{{if .CommitStatus}} +
+
+ {{if eq .CommitStatus.State "pending"}} + {{ctx.Locale.Tr "repo.pulls.status_checking"}} + {{else if eq .CommitStatus.State "success"}} + {{ctx.Locale.Tr "repo.pulls.status_checks_success"}} + {{else if eq .CommitStatus.State "warning"}} + {{ctx.Locale.Tr "repo.pulls.status_checks_warning"}} + {{else if eq .CommitStatus.State "failure"}} + {{ctx.Locale.Tr "repo.pulls.status_checks_failure"}} + {{else if eq .CommitStatus.State "error"}} + {{ctx.Locale.Tr "repo.pulls.status_checks_error"}} + {{else}} + {{ctx.Locale.Tr "repo.pulls.status_checking"}} + {{end}} - {{range $.LatestCommitStatuses}} -
- {{template "repo/commit_status" .}} -
- {{.Context}} {{.Description}} + {{if .ShowHideChecks}} +
+ +
+ {{end}} +
+ +
+ {{range .CommitStatuses}} +
+ {{template "repo/commit_status" .}} +
{{.Context}} {{.Description}}
{{if $.is_context_required}} {{if (call $.is_context_required .Context)}}
{{ctx.Locale.Tr "repo.pulls.status_checks_requested"}}
{{end}} @@ -29,6 +45,7 @@ {{if .TargetURL}}{{ctx.Locale.Tr "repo.pulls.status_checks_details"}}{{end}}
-
- {{end}} + {{end}} +
+
{{end}} diff --git a/templates/repo/view_list.tmpl b/templates/repo/view_list.tmpl index 7e60f27291ee7..836c633ced668 100644 --- a/templates/repo/view_list.tmpl +++ b/templates/repo/view_list.tmpl @@ -24,7 +24,7 @@ {{template "repo/shabox_badge" dict "root" $ "verification" .LatestCommitVerification}} {{end}} - {{template "repo/commit_statuses" dict "Status" .LatestCommitStatus "Statuses" .LatestCommitStatuses "root" $}} + {{template "repo/commit_statuses" dict "Status" .LatestCommitStatus "Statuses" .LatestCommitStatuses}} {{$commitLink:= printf "%s/commit/%s" .RepoLink (PathEscape .LatestCommit.ID.String)}} {{RenderCommitMessageLinkSubject $.Context .LatestCommit.Message $.RepoLink $commitLink ($.Repository.ComposeMetas ctx)}} {{if IsMultilineCommitMessage .LatestCommit.Message}} diff --git a/templates/shared/issuelist.tmpl b/templates/shared/issuelist.tmpl index 4214a03dcbb2c..e0d2e102e5ef7 100644 --- a/templates/shared/issuelist.tmpl +++ b/templates/shared/issuelist.tmpl @@ -16,7 +16,7 @@ {{RenderEmoji $.Context .Title | RenderCodeBlock}} {{if .IsPull}} {{if (index $.CommitStatuses .PullRequest.ID)}} - {{template "repo/commit_statuses" dict "Status" (index $.CommitLastStatus .PullRequest.ID) "Statuses" (index $.CommitStatuses .PullRequest.ID) "root" $}} + {{template "repo/commit_statuses" dict "Status" (index $.CommitLastStatus .PullRequest.ID) "Statuses" (index $.CommitStatuses .PullRequest.ID)}} {{end}} {{end}} diff --git a/web_src/css/modules/tippy.css b/web_src/css/modules/tippy.css index fe32597280331..45feab757477a 100644 --- a/web_src/css/modules/tippy.css +++ b/web_src/css/modules/tippy.css @@ -6,7 +6,7 @@ } [data-tippy-root] { - max-width: calc(100vw - 10px); + max-width: calc(100vw - 32px); } .tippy-box { @@ -18,37 +18,59 @@ font-size: 1rem; } +.tippy-content { + position: relative; + padding: 1rem; /* if you need different padding, use different data-theme */ + z-index: 1; +} + +/* tooltip theme for text tooltips */ + .tippy-box[data-theme="tooltip"] { background-color: var(--color-tooltip-bg); color: var(--color-tooltip-text); border: none; } +.tippy-box[data-theme="tooltip"] .tippy-content { + padding: 0.5rem 1rem; +} + +.tippy-box[data-theme="tooltip"] .tippy-svg-arrow-inner, +.tippy-box[data-theme="tooltip"] .tippy-svg-arrow-outer { + fill: var(--color-tooltip-bg); +} + +/* menu theme for .ui.menu */ + .tippy-box[data-theme="menu"] { background-color: var(--color-menu); color: var(--color-text); } -.tippy-box[data-theme="form-fetch-error"] { - border-color: var(--color-error-border); - background-color: var(--color-error-bg); - color: var(--color-error-text); +.tippy-box[data-theme="menu"] .tippy-content { + padding: 0; } -.tippy-content { - position: relative; - padding: 1rem; - z-index: 1; +.tippy-box[data-theme="menu"] .tippy-svg-arrow-inner { + fill: var(--color-menu); } -.tippy-box[data-theme="tooltip"] .tippy-content { - padding: 0.5rem 1rem; -} +/* box-with-header theme to look like .ui.attached.segment. can contain .ui.attached.header */ -.tippy-box[data-theme="menu"] .tippy-content { +.tippy-box[data-theme="box-with-header"] .tippy-content { + background: var(--color-box-body); padding: 0; } +.tippy-box[data-theme="box-with-header"][data-placement^="top"] .tippy-svg-arrow-inner { + fill: var(--color-box-body); +} + +.tippy-box[data-theme="box-with-header"][data-placement^="bottom"] .tippy-svg-arrow-inner { + fill: var(--color-box-header); +} + .tippy-box[data-placement^="top"] > .tippy-svg-arrow { bottom: 0; } @@ -107,12 +129,3 @@ .tippy-svg-arrow-inner { fill: var(--color-body); } - -.tippy-box[data-theme="tooltip"] .tippy-svg-arrow-inner, -.tippy-box[data-theme="tooltip"] .tippy-svg-arrow-outer { - fill: var(--color-tooltip-bg); -} - -.tippy-box[data-theme="menu"] .tippy-svg-arrow-inner { - fill: var(--color-menu); -} diff --git a/web_src/css/repo.css b/web_src/css/repo.css index b17917c246b1e..e4995d42298b6 100644 --- a/web_src/css/repo.css +++ b/web_src/css/repo.css @@ -3074,43 +3074,49 @@ tbody.commit-list { } } -.pr-status { - padding: 0 !important; /* To clear fomantic's padding on .ui.segment elements */ - display: flex; - align-items: center; +.commit-status-header { + border: none !important; /* reset the default ".ui.attached.header" styles, to use the outer border */ + margin: 0 !important; } -.pr-status .commit-status { - margin: 1em; - flex-shrink: 0; +.commit-status-list { + max-height: 195px; /* fit exactly 4 items */ + overflow-x: hidden; + transition: max-height .2s; } -.pr-status .status-context { +.commit-status-item { + padding: 14px 10px !important; display: flex; - justify-content: space-between; - width: 100%; + gap: 8px; + align-items: center; + border-top: 1px solid var(--color-secondary); +} + +.commit-status-item .commit-status { + flex-shrink: 0; } -.pr-status .status-context > span { - padding: 1em 0; +.commit-status-item .status-context { + color: var(--color-text); + flex: 1; } -.pr-status .status-details { +.commit-status-item .status-details { display: flex; - padding-right: 0.5em; align-items: center; justify-content: flex-end; } @media (max-width: 767.98px) { - .pr-status .status-details { + .commit-status-item .status-details { flex-direction: column; align-items: flex-end; justify-content: center; } } -.pr-status .status-details > span { +.commit-status-item .status-details > span { padding-right: 0.5em; /* To match the alignment with the "required" label */ } diff --git a/web_src/js/features/repo-commit.js b/web_src/js/features/repo-commit.js index 7240bf398aec6..76b34d2077719 100644 --- a/web_src/js/features/repo-commit.js +++ b/web_src/js/features/repo-commit.js @@ -66,6 +66,7 @@ export function initCommitStatuses() { placement: top ? 'top-start' : 'bottom-start', interactive: true, role: 'dialog', + theme: 'box-with-header', }); }); } diff --git a/web_src/js/features/repo-issue-pr-status.js b/web_src/js/features/repo-issue-pr-status.js new file mode 100644 index 0000000000000..30106157061a6 --- /dev/null +++ b/web_src/js/features/repo-issue-pr-status.js @@ -0,0 +1,12 @@ +export function initRepoPullRequestCommitStatus() { + for (const btn of document.querySelectorAll('.commit-status-hide-checks')) { + const panel = btn.closest('.commit-status-panel'); + const list = panel.querySelector('.commit-status-list'); + btn.addEventListener('click', () => { + list.style.maxHeight = list.style.maxHeight ? '' : '0px'; // toggle + list.style.overflow = 'hidden'; // hide scrollbar when hiding + btn.textContent = btn.getAttribute(list.style.maxHeight ? 'data-show-all' : 'data-hide-all'); + }); + list.addEventListener('animationend', () => list.style.overflow = ''); + } +} diff --git a/web_src/js/features/repo-legacy.js b/web_src/js/features/repo-legacy.js index 0aacc6ee85e83..8b3b7b5c12cc9 100644 --- a/web_src/js/features/repo-legacy.js +++ b/web_src/js/features/repo-legacy.js @@ -20,6 +20,7 @@ import {initCommentContent, initMarkupContent} from '../markup/content.js'; import {initCompReactionSelector} from './comp/ReactionSelector.js'; import {initRepoSettingBranches} from './repo-settings.js'; import {initRepoPullRequestMergeForm} from './repo-issue-pr-form.js'; +import {initRepoPullRequestCommitStatus} from './repo-issue-pr-status.js'; import {hideElem, showElem} from '../utils/dom.js'; import {getComboMarkdownEditor, initComboMarkdownEditor} from './comp/ComboMarkdownEditor.js'; import {attachRefIssueContextPopup} from './contextpopup.js'; @@ -546,6 +547,7 @@ export function initRepository() { initCompReactionSelector($(document)); initRepoPullRequestMergeForm(); + initRepoPullRequestCommitStatus(); } // Pull request diff --git a/web_src/js/modules/tippy.js b/web_src/js/modules/tippy.js index ec7ee2141f9f9..d27908962f53a 100644 --- a/web_src/js/modules/tippy.js +++ b/web_src/js/modules/tippy.js @@ -34,7 +34,7 @@ export function createTippy(target, opts = {}) { }, arrow: ``, role: 'menu', // HTML role attribute, only tooltips should use "tooltip" - theme: other.role || 'menu', // CSS theme, we support either "tooltip" or "menu" + theme: other.role || 'menu', // CSS theme, either "tooltip", "menu" or "box-with-header" plugins: [followCursor], ...other, }); From 80715ae5c7ca41a8ee2ad7b6459c587cc63195bf Mon Sep 17 00:00:00 2001 From: GiteaBot Date: Fri, 3 Nov 2023 00:23:16 +0000 Subject: [PATCH 09/37] [skip ci] Updated translations via Crowdin --- options/locale/locale_sk-SK.ini | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/options/locale/locale_sk-SK.ini b/options/locale/locale_sk-SK.ini index 6a1df50553a6f..d137a6d2beda6 100644 --- a/options/locale/locale_sk-SK.ini +++ b/options/locale/locale_sk-SK.ini @@ -77,20 +77,24 @@ milestones=Míľniky ok=OK cancel=Zrušiť +retry=Opakovať save=Uložiť add=Pridať add_all=Pridať všetko remove=Odstrániť remove_all=Odstrániť všetko +remove_label_str=Odstrániť položku „%s“ edit=Upraviť enabled=Povolené copy=Kopírovať copy_url=Kopírovať URL +copy_content=Kopírovať obsah copy_branch=Kopírovať meno vetvy copy_success=Skopírované! copy_error=Kopírovanie zlyhalo +copy_type_unsupported=Tento typ súboru nie je možné skopírovať write=Zapísať preview=Náhľad From 1bf5527eac6b947010c8faf408f6747de2a2384f Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 3 Nov 2023 09:41:00 +0800 Subject: [PATCH 10/37] Refactor Find Sources and fix bug when view a user who belongs to an unactive auth source (#27798) The steps to reproduce it. First, create a new oauth2 source. Then, a user login with this oauth2 source. Disable the oauth2 source. Visit users -> settings -> security, 500 will be displayed. This is because this page only load active Oauth2 sources but not all Oauth2 sources. --- cmd/admin_auth.go | 2 +- models/activities/statistic.go | 2 +- models/auth/oauth2.go | 9 ---- models/auth/source.go | 49 ++++++++----------- routers/web/admin/auths.go | 14 +++--- routers/web/admin/users.go | 10 ++-- routers/web/auth/auth.go | 12 ++--- routers/web/user/setting/security/security.go | 26 +++++++++- services/auth/signin.go | 5 +- services/auth/source/oauth2/init.go | 9 +++- services/auth/source/oauth2/providers.go | 47 ++++++++++-------- services/auth/sspi.go | 5 +- services/auth/sync.go | 2 +- templates/user/auth/signin_inner.tmpl | 7 ++- templates/user/auth/signup_inner.tmpl | 7 ++- 15 files changed, 115 insertions(+), 91 deletions(-) diff --git a/cmd/admin_auth.go b/cmd/admin_auth.go index 3b308d77f7987..014ddf329f94d 100644 --- a/cmd/admin_auth.go +++ b/cmd/admin_auth.go @@ -62,7 +62,7 @@ func runListAuth(c *cli.Context) error { return err } - authSources, err := auth_model.Sources(ctx) + authSources, err := auth_model.FindSources(ctx, auth_model.FindSourcesOptions{}) if err != nil { return err } diff --git a/models/activities/statistic.go b/models/activities/statistic.go index 009c8c5ab474e..e9dab6fc10b6b 100644 --- a/models/activities/statistic.go +++ b/models/activities/statistic.go @@ -102,7 +102,7 @@ func GetStatistic(ctx context.Context) (stats Statistic) { stats.Counter.Follow, _ = e.Count(new(user_model.Follow)) stats.Counter.Mirror, _ = e.Count(new(repo_model.Mirror)) stats.Counter.Release, _ = e.Count(new(repo_model.Release)) - stats.Counter.AuthSource = auth.CountSources(ctx) + stats.Counter.AuthSource = auth.CountSources(ctx, auth.FindSourcesOptions{}) stats.Counter.Webhook, _ = e.Count(new(webhook.Webhook)) stats.Counter.Milestone, _ = e.Count(new(issues_model.Milestone)) stats.Counter.Label, _ = e.Count(new(issues_model.Label)) diff --git a/models/auth/oauth2.go b/models/auth/oauth2.go index d73ad6965d2f0..76a4e9d835bcd 100644 --- a/models/auth/oauth2.go +++ b/models/auth/oauth2.go @@ -631,15 +631,6 @@ func (err ErrOAuthApplicationNotFound) Unwrap() error { return util.ErrNotExist } -// GetActiveOAuth2ProviderSources returns all actived LoginOAuth2 sources -func GetActiveOAuth2ProviderSources(ctx context.Context) ([]*Source, error) { - sources := make([]*Source, 0, 1) - if err := db.GetEngine(ctx).Where("is_active = ? and type = ?", true, OAuth2).Find(&sources); err != nil { - return nil, err - } - return sources, nil -} - // GetActiveOAuth2SourceByName returns a OAuth2 AuthSource based on the given name func GetActiveOAuth2SourceByName(ctx context.Context, name string) (*Source, error) { authSource := new(Source) diff --git a/models/auth/source.go b/models/auth/source.go index 0f57d1702a774..b3f3262cc206c 100644 --- a/models/auth/source.go +++ b/models/auth/source.go @@ -14,6 +14,7 @@ import ( "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" + "xorm.io/builder" "xorm.io/xorm" "xorm.io/xorm/convert" ) @@ -240,37 +241,26 @@ func CreateSource(ctx context.Context, source *Source) error { return err } -// Sources returns a slice of all login sources found in DB. -func Sources(ctx context.Context) ([]*Source, error) { - auths := make([]*Source, 0, 6) - return auths, db.GetEngine(ctx).Find(&auths) +type FindSourcesOptions struct { + IsActive util.OptionalBool + LoginType Type } -// SourcesByType returns all sources of the specified type -func SourcesByType(ctx context.Context, loginType Type) ([]*Source, error) { - sources := make([]*Source, 0, 1) - if err := db.GetEngine(ctx).Where("type = ?", loginType).Find(&sources); err != nil { - return nil, err +func (opts FindSourcesOptions) ToConds() builder.Cond { + conds := builder.NewCond() + if !opts.IsActive.IsNone() { + conds = conds.And(builder.Eq{"is_active": opts.IsActive.IsTrue()}) } - return sources, nil -} - -// AllActiveSources returns all active sources -func AllActiveSources(ctx context.Context) ([]*Source, error) { - sources := make([]*Source, 0, 5) - if err := db.GetEngine(ctx).Where("is_active = ?", true).Find(&sources); err != nil { - return nil, err + if opts.LoginType != NoType { + conds = conds.And(builder.Eq{"`type`": opts.LoginType}) } - return sources, nil + return conds } -// ActiveSources returns all active sources of the specified type -func ActiveSources(ctx context.Context, tp Type) ([]*Source, error) { - sources := make([]*Source, 0, 1) - if err := db.GetEngine(ctx).Where("is_active = ? and type = ?", true, tp).Find(&sources); err != nil { - return nil, err - } - return sources, nil +// FindSources returns a slice of login sources found in DB according to given conditions. +func FindSources(ctx context.Context, opts FindSourcesOptions) ([]*Source, error) { + auths := make([]*Source, 0, 6) + return auths, db.GetEngine(ctx).Where(opts.ToConds()).Find(&auths) } // IsSSPIEnabled returns true if there is at least one activated login @@ -279,7 +269,10 @@ func IsSSPIEnabled(ctx context.Context) bool { if !db.HasEngine { return false } - sources, err := ActiveSources(ctx, SSPI) + sources, err := FindSources(ctx, FindSourcesOptions{ + IsActive: util.OptionalBoolTrue, + LoginType: SSPI, + }) if err != nil { log.Error("ActiveSources: %v", err) return false @@ -354,8 +347,8 @@ func UpdateSource(ctx context.Context, source *Source) error { } // CountSources returns number of login sources. -func CountSources(ctx context.Context) int64 { - count, _ := db.GetEngine(ctx).Count(new(Source)) +func CountSources(ctx context.Context, opts FindSourcesOptions) int64 { + count, _ := db.GetEngine(ctx).Where(opts.ToConds()).Count(new(Source)) return count } diff --git a/routers/web/admin/auths.go b/routers/web/admin/auths.go index da91e31efe11c..23946d64afa33 100644 --- a/routers/web/admin/auths.go +++ b/routers/web/admin/auths.go @@ -48,13 +48,13 @@ func Authentications(ctx *context.Context) { ctx.Data["PageIsAdminAuthentications"] = true var err error - ctx.Data["Sources"], err = auth.Sources(ctx) + ctx.Data["Sources"], err = auth.FindSources(ctx, auth.FindSourcesOptions{}) if err != nil { ctx.ServerError("auth.Sources", err) return } - ctx.Data["Total"] = auth.CountSources(ctx) + ctx.Data["Total"] = auth.CountSources(ctx, auth.FindSourcesOptions{}) ctx.HTML(http.StatusOK, tplAuths) } @@ -99,7 +99,7 @@ func NewAuthSource(ctx *context.Context) { ctx.Data["AuthSources"] = authSources ctx.Data["SecurityProtocols"] = securityProtocols ctx.Data["SMTPAuths"] = smtp.Authenticators - oauth2providers := oauth2.GetOAuth2Providers() + oauth2providers := oauth2.GetSupportedOAuth2Providers() ctx.Data["OAuth2Providers"] = oauth2providers ctx.Data["SSPIAutoCreateUsers"] = true @@ -242,7 +242,7 @@ func NewAuthSourcePost(ctx *context.Context) { ctx.Data["AuthSources"] = authSources ctx.Data["SecurityProtocols"] = securityProtocols ctx.Data["SMTPAuths"] = smtp.Authenticators - oauth2providers := oauth2.GetOAuth2Providers() + oauth2providers := oauth2.GetSupportedOAuth2Providers() ctx.Data["OAuth2Providers"] = oauth2providers ctx.Data["SSPIAutoCreateUsers"] = true @@ -284,7 +284,7 @@ func NewAuthSourcePost(ctx *context.Context) { ctx.RenderWithErr(err.Error(), tplAuthNew, form) return } - existing, err := auth.SourcesByType(ctx, auth.SSPI) + existing, err := auth.FindSources(ctx, auth.FindSourcesOptions{LoginType: auth.SSPI}) if err != nil || len(existing) > 0 { ctx.Data["Err_Type"] = true ctx.RenderWithErr(ctx.Tr("admin.auths.login_source_of_type_exist"), tplAuthNew, form) @@ -334,7 +334,7 @@ func EditAuthSource(ctx *context.Context) { ctx.Data["SecurityProtocols"] = securityProtocols ctx.Data["SMTPAuths"] = smtp.Authenticators - oauth2providers := oauth2.GetOAuth2Providers() + oauth2providers := oauth2.GetSupportedOAuth2Providers() ctx.Data["OAuth2Providers"] = oauth2providers source, err := auth.GetSourceByID(ctx, ctx.ParamsInt64(":authid")) @@ -368,7 +368,7 @@ func EditAuthSourcePost(ctx *context.Context) { ctx.Data["PageIsAdminAuthentications"] = true ctx.Data["SMTPAuths"] = smtp.Authenticators - oauth2providers := oauth2.GetOAuth2Providers() + oauth2providers := oauth2.GetSupportedOAuth2Providers() ctx.Data["OAuth2Providers"] = oauth2providers source, err := auth.GetSourceByID(ctx, ctx.ParamsInt64(":authid")) diff --git a/routers/web/admin/users.go b/routers/web/admin/users.go index 91a578fb55482..630d739836beb 100644 --- a/routers/web/admin/users.go +++ b/routers/web/admin/users.go @@ -90,7 +90,9 @@ func NewUser(ctx *context.Context) { ctx.Data["login_type"] = "0-0" - sources, err := auth.Sources(ctx) + sources, err := auth.FindSources(ctx, auth.FindSourcesOptions{ + IsActive: util.OptionalBoolTrue, + }) if err != nil { ctx.ServerError("auth.Sources", err) return @@ -109,7 +111,9 @@ func NewUserPost(ctx *context.Context) { ctx.Data["DefaultUserVisibilityMode"] = setting.Service.DefaultUserVisibilityMode ctx.Data["AllowedUserVisibilityModes"] = setting.Service.AllowedUserVisibilityModesSlice.ToVisibleTypeSlice() - sources, err := auth.Sources(ctx) + sources, err := auth.FindSources(ctx, auth.FindSourcesOptions{ + IsActive: util.OptionalBoolTrue, + }) if err != nil { ctx.ServerError("auth.Sources", err) return @@ -230,7 +234,7 @@ func prepareUserInfo(ctx *context.Context) *user_model.User { ctx.Data["LoginSource"] = &auth.Source{} } - sources, err := auth.Sources(ctx) + sources, err := auth.FindSources(ctx, auth.FindSourcesOptions{}) if err != nil { ctx.ServerError("auth.Sources", err) return nil diff --git a/routers/web/auth/auth.go b/routers/web/auth/auth.go index e27307ef1afc6..0ea91fc759a9a 100644 --- a/routers/web/auth/auth.go +++ b/routers/web/auth/auth.go @@ -160,12 +160,11 @@ func SignIn(ctx *context.Context) { return } - orderedOAuth2Names, oauth2Providers, err := oauth2.GetActiveOAuth2Providers(ctx) + oauth2Providers, err := oauth2.GetOAuth2Providers(ctx, util.OptionalBoolTrue) if err != nil { ctx.ServerError("UserSignIn", err) return } - ctx.Data["OrderedOAuth2Names"] = orderedOAuth2Names ctx.Data["OAuth2Providers"] = oauth2Providers ctx.Data["Title"] = ctx.Tr("sign_in") ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login" @@ -184,12 +183,11 @@ func SignIn(ctx *context.Context) { func SignInPost(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("sign_in") - orderedOAuth2Names, oauth2Providers, err := oauth2.GetActiveOAuth2Providers(ctx) + oauth2Providers, err := oauth2.GetOAuth2Providers(ctx, util.OptionalBoolTrue) if err != nil { ctx.ServerError("UserSignIn", err) return } - ctx.Data["OrderedOAuth2Names"] = orderedOAuth2Names ctx.Data["OAuth2Providers"] = oauth2Providers ctx.Data["Title"] = ctx.Tr("sign_in") ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login" @@ -408,13 +406,12 @@ func SignUp(ctx *context.Context) { ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/sign_up" - orderedOAuth2Names, oauth2Providers, err := oauth2.GetActiveOAuth2Providers(ctx) + oauth2Providers, err := oauth2.GetOAuth2Providers(ctx, util.OptionalBoolTrue) if err != nil { ctx.ServerError("UserSignUp", err) return } - ctx.Data["OrderedOAuth2Names"] = orderedOAuth2Names ctx.Data["OAuth2Providers"] = oauth2Providers context.SetCaptchaData(ctx) @@ -438,13 +435,12 @@ func SignUpPost(ctx *context.Context) { ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/sign_up" - orderedOAuth2Names, oauth2Providers, err := oauth2.GetActiveOAuth2Providers(ctx) + oauth2Providers, err := oauth2.GetOAuth2Providers(ctx, util.OptionalBoolTrue) if err != nil { ctx.ServerError("UserSignUp", err) return } - ctx.Data["OrderedOAuth2Names"] = orderedOAuth2Names ctx.Data["OAuth2Providers"] = oauth2Providers context.SetCaptchaData(ctx) diff --git a/routers/web/user/setting/security/security.go b/routers/web/user/setting/security/security.go index 58c637e2b3365..e64901ae728e7 100644 --- a/routers/web/user/setting/security/security.go +++ b/routers/web/user/setting/security/security.go @@ -6,12 +6,14 @@ package security import ( "net/http" + "sort" auth_model "code.gitea.io/gitea/models/auth" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/services/auth/source/oauth2" ) @@ -105,11 +107,31 @@ func loadSecurityData(ctx *context.Context) { } ctx.Data["AccountLinks"] = sources - orderedOAuth2Names, oauth2Providers, err := oauth2.GetActiveOAuth2Providers(ctx) + authSources, err := auth_model.FindSources(ctx, auth_model.FindSourcesOptions{ + IsActive: util.OptionalBoolNone, + LoginType: auth_model.OAuth2, + }) if err != nil { - ctx.ServerError("GetActiveOAuth2Providers", err) + ctx.ServerError("FindSources", err) return } + + var orderedOAuth2Names []string + oauth2Providers := make(map[string]oauth2.Provider) + for _, source := range authSources { + provider, err := oauth2.CreateProviderFromSource(source) + if err != nil { + ctx.ServerError("CreateProviderFromSource", err) + return + } + oauth2Providers[source.Name] = provider + if source.IsActive { + orderedOAuth2Names = append(orderedOAuth2Names, source.Name) + } + } + + sort.Strings(orderedOAuth2Names) + ctx.Data["OrderedOAuth2Names"] = orderedOAuth2Names ctx.Data["OAuth2Providers"] = oauth2Providers diff --git a/services/auth/signin.go b/services/auth/signin.go index 5fdf6d2bd7ab2..2e534536817e4 100644 --- a/services/auth/signin.go +++ b/services/auth/signin.go @@ -11,6 +11,7 @@ import ( "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/services/auth/source/oauth2" "code.gitea.io/gitea/services/auth/source/smtp" @@ -85,7 +86,9 @@ func UserSignIn(ctx context.Context, username, password string) (*user_model.Use } } - sources, err := auth.AllActiveSources(ctx) + sources, err := auth.FindSources(ctx, auth.FindSourcesOptions{ + IsActive: util.OptionalBoolTrue, + }) if err != nil { return nil, nil, err } diff --git a/services/auth/source/oauth2/init.go b/services/auth/source/oauth2/init.go index cfaddaa35d55c..0ebbdaebd411f 100644 --- a/services/auth/source/oauth2/init.go +++ b/services/auth/source/oauth2/init.go @@ -12,6 +12,7 @@ import ( "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/util" "github.com/google/uuid" "github.com/gorilla/sessions" @@ -63,7 +64,13 @@ func ResetOAuth2(ctx context.Context) error { // initOAuth2Sources is used to load and register all active OAuth2 providers func initOAuth2Sources(ctx context.Context) error { - authSources, _ := auth.GetActiveOAuth2ProviderSources(ctx) + authSources, err := auth.FindSources(ctx, auth.FindSourcesOptions{ + IsActive: util.OptionalBoolTrue, + LoginType: auth.OAuth2, + }) + if err != nil { + return err + } for _, source := range authSources { oauth2Source, ok := source.Cfg.(*Source) if !ok { diff --git a/services/auth/source/oauth2/providers.go b/services/auth/source/oauth2/providers.go index cd158614a2e4e..3b45b252f7099 100644 --- a/services/auth/source/oauth2/providers.go +++ b/services/auth/source/oauth2/providers.go @@ -15,6 +15,7 @@ import ( "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/util" "github.com/markbates/goth" ) @@ -80,10 +81,10 @@ func RegisterGothProvider(provider GothProvider) { gothProviders[provider.Name()] = provider } -// GetOAuth2Providers returns the map of unconfigured OAuth2 providers +// GetSupportedOAuth2Providers returns the map of unconfigured OAuth2 providers // key is used as technical name (like in the callbackURL) // values to display -func GetOAuth2Providers() []Provider { +func GetSupportedOAuth2Providers() []Provider { providers := make([]Provider, 0, len(gothProviders)) for _, provider := range gothProviders { @@ -95,33 +96,39 @@ func GetOAuth2Providers() []Provider { return providers } -// GetActiveOAuth2Providers returns the map of configured active OAuth2 providers -// key is used as technical name (like in the callbackURL) -// values to display -func GetActiveOAuth2Providers(ctx context.Context) ([]string, map[string]Provider, error) { - // Maybe also separate used and unused providers so we can force the registration of only 1 active provider for each type +func CreateProviderFromSource(source *auth.Source) (Provider, error) { + oauth2Cfg, ok := source.Cfg.(*Source) + if !ok { + return nil, fmt.Errorf("invalid OAuth2 source config: %v", oauth2Cfg) + } + gothProv := gothProviders[oauth2Cfg.Provider] + return &AuthSourceProvider{GothProvider: gothProv, sourceName: source.Name, iconURL: oauth2Cfg.IconURL}, nil +} - authSources, err := auth.GetActiveOAuth2ProviderSources(ctx) +// GetOAuth2Providers returns the list of configured OAuth2 providers +func GetOAuth2Providers(ctx context.Context, isActive util.OptionalBool) ([]Provider, error) { + authSources, err := auth.FindSources(ctx, auth.FindSourcesOptions{ + IsActive: isActive, + LoginType: auth.OAuth2, + }) if err != nil { - return nil, nil, err + return nil, err } - var orderedKeys []string - providers := make(map[string]Provider) + providers := make([]Provider, 0, len(authSources)) for _, source := range authSources { - oauth2Cfg, ok := source.Cfg.(*Source) - if !ok { - log.Error("Invalid OAuth2 source config: %v", oauth2Cfg) - continue + provider, err := CreateProviderFromSource(source) + if err != nil { + return nil, err } - gothProv := gothProviders[oauth2Cfg.Provider] - providers[source.Name] = &AuthSourceProvider{GothProvider: gothProv, sourceName: source.Name, iconURL: oauth2Cfg.IconURL} - orderedKeys = append(orderedKeys, source.Name) + providers = append(providers, provider) } - sort.Strings(orderedKeys) + sort.Slice(providers, func(i, j int) bool { + return providers[i].Name() < providers[j].Name() + }) - return orderedKeys, providers, nil + return providers, nil } // RegisterProviderWithGothic register a OAuth2 provider in goth lib diff --git a/services/auth/sspi.go b/services/auth/sspi.go index 573d94b42c2c0..bc8ec948f29cd 100644 --- a/services/auth/sspi.go +++ b/services/auth/sspi.go @@ -130,7 +130,10 @@ func (s *SSPI) Verify(req *http.Request, w http.ResponseWriter, store DataStore, // getConfig retrieves the SSPI configuration from login sources func (s *SSPI) getConfig(ctx context.Context) (*sspi.Source, error) { - sources, err := auth.ActiveSources(ctx, auth.SSPI) + sources, err := auth.FindSources(ctx, auth.FindSourcesOptions{ + IsActive: util.OptionalBoolTrue, + LoginType: auth.SSPI, + }) if err != nil { return nil, err } diff --git a/services/auth/sync.go b/services/auth/sync.go index 25b9460b9921f..11a59d41ae1b4 100644 --- a/services/auth/sync.go +++ b/services/auth/sync.go @@ -15,7 +15,7 @@ import ( func SyncExternalUsers(ctx context.Context, updateExisting bool) error { log.Trace("Doing: SyncExternalUsers") - ls, err := auth.Sources(ctx) + ls, err := auth.FindSources(ctx, auth.FindSourcesOptions{}) if err != nil { log.Error("SyncExternalUsers: %v", err) return err diff --git a/templates/user/auth/signin_inner.tmpl b/templates/user/auth/signin_inner.tmpl index f38b0a26087f5..7f744b24d87bf 100644 --- a/templates/user/auth/signin_inner.tmpl +++ b/templates/user/auth/signin_inner.tmpl @@ -52,16 +52,15 @@
{{end}} - {{if and .OrderedOAuth2Names .OAuth2Providers}} + {{if .OAuth2Providers}}
{{ctx.Locale.Tr "sign_in_or"}}
- {{range $key := .OrderedOAuth2Names}} - {{$provider := index $.OAuth2Providers $key}} - diff --git a/templates/user/auth/signup_inner.tmpl b/templates/user/auth/signup_inner.tmpl index 068ccbc6182c1..c75e33a18a0e9 100644 --- a/templates/user/auth/signup_inner.tmpl +++ b/templates/user/auth/signup_inner.tmpl @@ -56,16 +56,15 @@ {{end}} {{end}} - {{if and .OrderedOAuth2Names .OAuth2Providers}} + {{if .OAuth2Providers}}
{{ctx.Locale.Tr "sign_in_or"}}
- {{range $key := .OrderedOAuth2Names}} - {{$provider := index $.OAuth2Providers $key}} - From ae396ac7c02fdae65d5ea0cd5aafe5d3ca7c2ee0 Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Fri, 3 Nov 2023 15:56:53 +0800 Subject: [PATCH 11/37] Fix DownloadFunc when migrating releases (#27887) We should not use `asset.ID` in DownloadFunc because DownloadFunc is a closure. https://github.com/go-gitea/gitea/blob/1bf5527eac6b947010c8faf408f6747de2a2384f/services/migrations/gitea_downloader.go#L284-L295 A similar bug when migrating from GitHub has been fixed in #14703. This PR fixes the bug when migrating from Gitea and GitLab. --- services/migrations/gitea_downloader.go | 10 ++++++---- services/migrations/gitlab.go | 5 +++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/services/migrations/gitea_downloader.go b/services/migrations/gitea_downloader.go index b9ba93325b91a..d402a238f27a3 100644 --- a/services/migrations/gitea_downloader.go +++ b/services/migrations/gitea_downloader.go @@ -282,6 +282,8 @@ func (g *GiteaDownloader) convertGiteaRelease(rel *gitea_sdk.Release) *base.Rele httpClient := NewMigrationHTTPClient() for _, asset := range rel.Attachments { + assetID := asset.ID // Don't optimize this, for closure we need a local variable + assetDownloadURL := asset.DownloadURL size := int(asset.Size) dlCount := int(asset.DownloadCount) r.Assets = append(r.Assets, &base.ReleaseAsset{ @@ -292,18 +294,18 @@ func (g *GiteaDownloader) convertGiteaRelease(rel *gitea_sdk.Release) *base.Rele Created: asset.Created, DownloadURL: &asset.DownloadURL, DownloadFunc: func() (io.ReadCloser, error) { - asset, _, err := g.client.GetReleaseAttachment(g.repoOwner, g.repoName, rel.ID, asset.ID) + asset, _, err := g.client.GetReleaseAttachment(g.repoOwner, g.repoName, rel.ID, assetID) if err != nil { return nil, err } - if !hasBaseURL(asset.DownloadURL, g.baseURL) { - WarnAndNotice("Unexpected AssetURL for assetID[%d] in %s: %s", asset.ID, g, asset.DownloadURL) + if !hasBaseURL(assetDownloadURL, g.baseURL) { + WarnAndNotice("Unexpected AssetURL for assetID[%d] in %s: %s", assetID, g, assetDownloadURL) return io.NopCloser(strings.NewReader(asset.DownloadURL)), nil } // FIXME: for a private download? - req, err := http.NewRequest("GET", asset.DownloadURL, nil) + req, err := http.NewRequest("GET", assetDownloadURL, nil) if err != nil { return nil, err } diff --git a/services/migrations/gitlab.go b/services/migrations/gitlab.go index 51dde8b67764f..22bc4cf8f39d7 100644 --- a/services/migrations/gitlab.go +++ b/services/migrations/gitlab.go @@ -310,6 +310,7 @@ func (g *GitlabDownloader) convertGitlabRelease(rel *gitlab.Release) *base.Relea httpClient := NewMigrationHTTPClient() for k, asset := range rel.Assets.Links { + assetID := asset.ID // Don't optimize this, for closure we need a local variable r.Assets = append(r.Assets, &base.ReleaseAsset{ ID: int64(asset.ID), Name: asset.Name, @@ -317,13 +318,13 @@ func (g *GitlabDownloader) convertGitlabRelease(rel *gitlab.Release) *base.Relea Size: &zero, DownloadCount: &zero, DownloadFunc: func() (io.ReadCloser, error) { - link, _, err := g.client.ReleaseLinks.GetReleaseLink(g.repoID, rel.TagName, asset.ID, gitlab.WithContext(g.ctx)) + link, _, err := g.client.ReleaseLinks.GetReleaseLink(g.repoID, rel.TagName, assetID, gitlab.WithContext(g.ctx)) if err != nil { return nil, err } if !hasBaseURL(link.URL, g.baseURL) { - WarnAndNotice("Unexpected AssetURL for assetID[%d] in %s: %s", asset.ID, g, link.URL) + WarnAndNotice("Unexpected AssetURL for assetID[%d] in %s: %s", assetID, g, link.URL) return io.NopCloser(strings.NewReader(link.URL)), nil } From d1dc9cb334e90e09219420638444383fd636d431 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 3 Nov 2023 22:01:45 +0800 Subject: [PATCH 12/37] Refactor the function RemoveOrgUser (#27582) This PR is a small refactor to merge removeOrgUser into RemoveOrgUser. --- models/org.go | 30 +++++++++++------------------- models/org_team.go | 2 +- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/models/org.go b/models/org.go index 119465b96237f..5e0deeb8de7a2 100644 --- a/models/org.go +++ b/models/org.go @@ -14,12 +14,11 @@ import ( repo_model "code.gitea.io/gitea/models/repo" ) -func removeOrgUser(ctx context.Context, orgID, userID int64) error { +// RemoveOrgUser removes user from given organization. +func RemoveOrgUser(ctx context.Context, orgID, userID int64) error { ou := new(organization.OrgUser) - sess := db.GetEngine(ctx) - - has, err := sess. + has, err := db.GetEngine(ctx). Where("uid=?", userID). And("org_id=?", orgID). Get(ou) @@ -52,7 +51,13 @@ func removeOrgUser(ctx context.Context, orgID, userID int64) error { } } - if _, err := sess.ID(ou.ID).Delete(ou); err != nil { + ctx, committer, err := db.TxContext(ctx) + if err != nil { + return err + } + defer committer.Close() + + if _, err := db.GetEngine(ctx).ID(ou.ID).Delete(ou); err != nil { return err } else if _, err = db.Exec(ctx, "UPDATE `user` SET num_members=num_members-1 WHERE id=?", orgID); err != nil { return err @@ -74,7 +79,7 @@ func removeOrgUser(ctx context.Context, orgID, userID int64) error { } if len(repoIDs) > 0 { - if _, err = sess. + if _, err = db.GetEngine(ctx). Where("user_id = ?", userID). In("repo_id", repoIDs). Delete(new(access_model.Access)); err != nil { @@ -93,18 +98,5 @@ func removeOrgUser(ctx context.Context, orgID, userID int64) error { } } - return nil -} - -// RemoveOrgUser removes user from given organization. -func RemoveOrgUser(ctx context.Context, orgID, userID int64) error { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - if err := removeOrgUser(ctx, orgID, userID); err != nil { - return err - } return committer.Commit() } diff --git a/models/org_team.go b/models/org_team.go index acbab560892df..03a4f6e98d8fa 100644 --- a/models/org_team.go +++ b/models/org_team.go @@ -502,7 +502,7 @@ func removeInvalidOrgUser(ctx context.Context, userID, orgID int64) error { }); err != nil { return err } else if count == 0 { - return removeOrgUser(ctx, orgID, userID) + return RemoveOrgUser(ctx, orgID, userID) } return nil } From c46080bc9d8358bfd0e7a1174d773a46c7cdf120 Mon Sep 17 00:00:00 2001 From: Earl Warren <109468362+earl-warren@users.noreply.github.com> Date: Fri, 3 Nov 2023 15:40:48 +0100 Subject: [PATCH 13/37] Remove "tabindex" from some form buttons (#27892) Remove the "tabindex" from some form buttons on the "diff box" / "issue view content" page, let the browser use the default tab order. --------- Co-authored-by: Gusted Co-authored-by: wxiaoguang --- templates/repo/diff/box.tmpl | 4 ++-- templates/repo/issue/view_content.tmpl | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/templates/repo/diff/box.tmpl b/templates/repo/diff/box.tmpl index c037c3b365238..e00917746ead6 100644 --- a/templates/repo/diff/box.tmpl +++ b/templates/repo/diff/box.tmpl @@ -238,8 +238,8 @@ "DropzoneParentContainer" ".ui.form" )}}
- - + +
diff --git a/templates/repo/issue/view_content.tmpl b/templates/repo/issue/view_content.tmpl index c17919f5bfb42..9967997b92411 100644 --- a/templates/repo/issue/view_content.tmpl +++ b/templates/repo/issue/view_content.tmpl @@ -92,7 +92,7 @@
{{if and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .DisableStatusChange)}} {{if .Issue.IsClosed}} - {{else}} @@ -100,12 +100,12 @@ {{if .Issue.IsPull}} {{$closeTranslationKey = "repo.pulls.close"}} {{end}} - {{end}} {{end}} -
@@ -162,8 +162,8 @@
- - + +
From da0c4b8d10cce85c854370ae3c399b542cb04306 Mon Sep 17 00:00:00 2001 From: Earl Warren <109468362+earl-warren@users.noreply.github.com> Date: Fri, 3 Nov 2023 16:21:05 +0100 Subject: [PATCH 14/37] Remove SSH workaround (#27893) Revert the workaround of #26409 and fix #26411, update github.com/gliderlabs/ssh to include https://github.com/gliderlabs/ssh/commit/02f9d573009f8c13755b6b90fa14a4f549b17b22 --- go.mod | 2 +- go.sum | 8 ++------ modules/ssh/ssh.go | 9 ++------- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/go.mod b/go.mod index a3b4656f76c8f..97997cad6d566 100644 --- a/go.mod +++ b/go.mod @@ -36,7 +36,7 @@ require ( github.com/ethantkoenig/rupture v1.0.1 github.com/felixge/fgprof v0.9.3 github.com/fsnotify/fsnotify v1.6.0 - github.com/gliderlabs/ssh v0.3.5 + github.com/gliderlabs/ssh v0.3.6-0.20230927171611-ece6c7995e46 github.com/go-ap/activitypub v0.0.0-20231003111253-1fba3772399b github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73 github.com/go-chi/chi/v5 v5.0.10 diff --git a/go.sum b/go.sum index 0e57ec5368961..fbe4fe06e568e 100644 --- a/go.sum +++ b/go.sum @@ -329,8 +329,8 @@ github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4 github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/fxamacker/cbor/v2 v2.5.0 h1:oHsG0V/Q6E/wqTS2O1Cozzsy69nqCiguo5Q1a1ADivE= github.com/fxamacker/cbor/v2 v2.5.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= -github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= -github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4= +github.com/gliderlabs/ssh v0.3.6-0.20230927171611-ece6c7995e46 h1:fYiA820jw7wmAvdXrHwMItxjJkra7dT9y8yiXhtzb94= +github.com/gliderlabs/ssh v0.3.6-0.20230927171611-ece6c7995e46/go.mod h1:i/TCLcdiX9Up/vs+Rp8c3yMbqp2Y4Y7Nh9uzGFCa5pM= github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= github.com/go-ap/activitypub v0.0.0-20231003111253-1fba3772399b h1:VLD6IPBDkqEsOZ+EfLO6MayuHycZ0cv4BStTlRoZduo= @@ -1237,7 +1237,6 @@ golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= @@ -1337,9 +1336,7 @@ golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1353,7 +1350,6 @@ golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= diff --git a/modules/ssh/ssh.go b/modules/ssh/ssh.go index 37624ab679c86..f8e4f569b87f4 100644 --- a/modules/ssh/ssh.go +++ b/modules/ssh/ssh.go @@ -17,7 +17,6 @@ import ( "os" "os/exec" "path/filepath" - "reflect" "strconv" "strings" "sync" @@ -165,10 +164,6 @@ func sessionHandler(session ssh.Session) { } func publicKeyHandler(ctx ssh.Context, key ssh.PublicKey) bool { - // FIXME: the "ssh.Context" is not thread-safe, so db operations should use the immutable parent "Context" - // TODO: Remove after https://github.com/gliderlabs/ssh/pull/211 - parentCtx := reflect.ValueOf(ctx).Elem().FieldByName("Context").Interface().(context.Context) - if log.IsDebug() { // <- FingerprintSHA256 is kinda expensive so only calculate it if necessary log.Debug("Handle Public Key: Fingerprint: %s from %s", gossh.FingerprintSHA256(key), ctx.RemoteAddr()) } @@ -200,7 +195,7 @@ func publicKeyHandler(ctx ssh.Context, key ssh.PublicKey) bool { // look for the exact principal principalLoop: for _, principal := range cert.ValidPrincipals { - pkey, err := asymkey_model.SearchPublicKeyByContentExact(parentCtx, principal) + pkey, err := asymkey_model.SearchPublicKeyByContentExact(ctx, principal) if err != nil { if asymkey_model.IsErrKeyNotExist(err) { log.Debug("Principal Rejected: %s Unknown Principal: %s", ctx.RemoteAddr(), principal) @@ -257,7 +252,7 @@ func publicKeyHandler(ctx ssh.Context, key ssh.PublicKey) bool { log.Debug("Handle Public Key: %s Fingerprint: %s is not a certificate", ctx.RemoteAddr(), gossh.FingerprintSHA256(key)) } - pkey, err := asymkey_model.SearchPublicKeyByContent(parentCtx, strings.TrimSpace(string(gossh.MarshalAuthorizedKey(key)))) + pkey, err := asymkey_model.SearchPublicKeyByContent(ctx, strings.TrimSpace(string(gossh.MarshalAuthorizedKey(key)))) if err != nil { if asymkey_model.IsErrKeyNotExist(err) { log.Warn("Unknown public key: %s from %s", gossh.FingerprintSHA256(key), ctx.RemoteAddr()) From 59f9ef9feebb19337b8a153e6cac0371ca706268 Mon Sep 17 00:00:00 2001 From: Earl Warren <109468362+earl-warren@users.noreply.github.com> Date: Sun, 5 Nov 2023 13:48:32 +0100 Subject: [PATCH 15/37] Remove action runners on user deletion (#27902) - On user deletion, delete action runners that the user has created. - Add a database consistency check to remove action runners that have nonexistent belonging owner. - Resolves https://codeberg.org/forgejo/forgejo/issues/1720 (cherry picked from commit 009ca7223dab054f7f760b7ccae69e745eebfabb) Co-authored-by: Gusted --- models/actions/runner.go | 24 ++++++++++++++++++++++++ modules/doctor/dbconsistency.go | 7 +++++++ services/user/delete.go | 2 ++ 3 files changed, 33 insertions(+) diff --git a/models/actions/runner.go b/models/actions/runner.go index ec6b49cf16741..2c092c2b4a36d 100644 --- a/models/actions/runner.go +++ b/models/actions/runner.go @@ -266,3 +266,27 @@ func CreateRunner(ctx context.Context, t *ActionRunner) error { _, err := db.GetEngine(ctx).Insert(t) return err } + +func CountRunnersWithoutBelongingOwner(ctx context.Context) (int64, error) { + // Only affect action runners were a owner ID is set, as actions runners + // could also be created on a repository. + return db.GetEngine(ctx).Table("action_runner"). + Join("LEFT", "user", "`action_runner`.owner_id = `user`.id"). + Where("`action_runner`.owner_id != ?", 0). + And(builder.IsNull{"`user`.id"}). + Count(new(ActionRunner)) +} + +func FixRunnersWithoutBelongingOwner(ctx context.Context) (int64, error) { + subQuery := builder.Select("`action_runner`.id"). + From("`action_runner`"). + Join("LEFT", "user", "`action_runner`.owner_id = `user`.id"). + Where(builder.Neq{"`action_runner`.owner_id": 0}). + And(builder.IsNull{"`user`.id"}) + b := builder.Delete(builder.In("id", subQuery)).From("`action_runner`") + res, err := db.GetEngine(ctx).Exec(b) + if err != nil { + return 0, err + } + return res.RowsAffected() +} diff --git a/modules/doctor/dbconsistency.go b/modules/doctor/dbconsistency.go index e5fc5785e826c..ac983f916157c 100644 --- a/modules/doctor/dbconsistency.go +++ b/modules/doctor/dbconsistency.go @@ -6,6 +6,7 @@ package doctor import ( "context" + actions_model "code.gitea.io/gitea/models/actions" activities_model "code.gitea.io/gitea/models/activities" "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" @@ -151,6 +152,12 @@ func checkDBConsistency(ctx context.Context, logger log.Logger, autofix bool) er Fixer: activities_model.FixActionCreatedUnixString, FixedMessage: "Set to zero", }, + { + Name: "Action Runners without existing owner", + Counter: actions_model.CountRunnersWithoutBelongingOwner, + Fixer: actions_model.FixRunnersWithoutBelongingOwner, + FixedMessage: "Removed", + }, } // TODO: function to recalc all counters diff --git a/services/user/delete.go b/services/user/delete.go index 01e3c37b39f34..c4617e064e782 100644 --- a/services/user/delete.go +++ b/services/user/delete.go @@ -10,6 +10,7 @@ import ( _ "image/jpeg" // Needed for jpeg support + actions_model "code.gitea.io/gitea/models/actions" activities_model "code.gitea.io/gitea/models/activities" asymkey_model "code.gitea.io/gitea/models/asymkey" auth_model "code.gitea.io/gitea/models/auth" @@ -90,6 +91,7 @@ func deleteUser(ctx context.Context, u *user_model.User, purge bool) (err error) &pull_model.AutoMerge{DoerID: u.ID}, &pull_model.ReviewState{UserID: u.ID}, &user_model.Redirect{RedirectUserID: u.ID}, + &actions_model.ActionRunner{OwnerID: u.ID}, ); err != nil { return fmt.Errorf("deleteBeans: %w", err) } From 966dcb99e3eb96c466e36316200be341fedc453f Mon Sep 17 00:00:00 2001 From: Nanguan Lin <70063547+lng2020@users.noreply.github.com> Date: Sun, 5 Nov 2023 21:25:40 +0800 Subject: [PATCH 16/37] Fix 500 when deleting a dismissed review (#27903) Fix #27767 Add a test to ensure its behavior --- models/issues/review.go | 10 ++++++++++ models/issues/review_test.go | 30 ++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/models/issues/review.go b/models/issues/review.go index 1cb5379b2f6ec..3db73a09ebcb7 100644 --- a/models/issues/review.go +++ b/models/issues/review.go @@ -897,6 +897,16 @@ func DeleteReview(ctx context.Context, r *Review) error { return err } + opts = FindCommentsOptions{ + Type: CommentTypeDismissReview, + IssueID: r.IssueID, + ReviewID: r.ID, + } + + if _, err := sess.Where(opts.ToConds()).Delete(new(Comment)); err != nil { + return err + } + if _, err := sess.ID(r.ID).Delete(new(Review)); err != nil { return err } diff --git a/models/issues/review_test.go b/models/issues/review_test.go index 63e2aef2cce02..1868cb1bfab25 100644 --- a/models/issues/review_test.go +++ b/models/issues/review_test.go @@ -8,6 +8,7 @@ import ( "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" + repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" @@ -258,3 +259,32 @@ func TestDeleteReview(t *testing.T) { assert.NoError(t, err) assert.True(t, review1.Official) } + +func TestDeleteDismissedReview(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2}) + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}) + review, err := issues_model.CreateReview(db.DefaultContext, issues_model.CreateReviewOptions{ + Content: "reject", + Type: issues_model.ReviewTypeReject, + Official: false, + Issue: issue, + Reviewer: user, + }) + assert.NoError(t, err) + assert.NoError(t, issues_model.DismissReview(db.DefaultContext, review, true)) + comment, err := issues_model.CreateComment(db.DefaultContext, &issues_model.CreateCommentOptions{ + Type: issues_model.CommentTypeDismissReview, + Doer: user, + Repo: repo, + Issue: issue, + ReviewID: review.ID, + Content: "dismiss", + }) + assert.NoError(t, err) + unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: comment.ID}) + assert.NoError(t, issues_model.DeleteReview(db.DefaultContext, review)) + unittest.AssertNotExistsBean(t, &issues_model.Comment{ID: comment.ID}) +} From 37a7c551d46de8cd2a44c8a16ee251ce23af0da8 Mon Sep 17 00:00:00 2001 From: sebastian-sauer Date: Mon, 6 Nov 2023 00:39:32 +0100 Subject: [PATCH 17/37] Show correct commit sha when viewing single commit diff (#27916) Show the correct sha when viewing a single commit. ![image](https://github.com/go-gitea/gitea/assets/1135157/5f39a84e-11ed-4700-b40b-eb9da6e91bec) --- templates/repo/diff/box.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/repo/diff/box.tmpl b/templates/repo/diff/box.tmpl index e00917746ead6..945c521a5773c 100644 --- a/templates/repo/diff/box.tmpl +++ b/templates/repo/diff/box.tmpl @@ -53,7 +53,7 @@ {{if not .DiffNotAvailable}} {{if and .IsShowingOnlySingleCommit .PageIsPullFiles}}
-
{{ctx.Locale.Tr "repo.pulls.showing_only_single_commit" (ShortSha .BeforeCommitID)}} - {{ctx.Locale.Tr "repo.pulls.show_all_commits"}}
+
{{ctx.Locale.Tr "repo.pulls.showing_only_single_commit" (ShortSha .AfterCommitID)}} - {{ctx.Locale.Tr "repo.pulls.show_all_commits"}}
{{else if and (not .IsShowingAllCommits) .PageIsPullFiles}}
From 1ccdb546f06f40d797f312b8f21c45f8ed8d25a1 Mon Sep 17 00:00:00 2001 From: silverwind Date: Mon, 6 Nov 2023 01:11:34 +0100 Subject: [PATCH 18/37] Install poetry dependencies with --no-root (#27919) Poetry 1.7.0 or higher will print a warning otherwise, see discussions: https://github.com/python-poetry/poetry/pull/8369 https://github.com/python-poetry/poetry/issues/1132 > --no-root Do not install the root package (the current project). --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c38e88a8e0ad9..68d8238794221 100644 --- a/Makefile +++ b/Makefile @@ -875,7 +875,7 @@ node_modules: package-lock.json @touch node_modules .venv: poetry.lock - poetry install + poetry install --no-root @touch .venv .PHONY: update From 1f501dae9e12cf092ec6eb5416620d5287a97074 Mon Sep 17 00:00:00 2001 From: delvh Date: Mon, 6 Nov 2023 03:05:24 +0100 Subject: [PATCH 19/37] Fix JS NPE when viewing specific range of PR commits (#27912) This should be the easiest fix. While other solutions might be possible that exterminate the root cause, they will not be as trivial. --- web_src/js/components/DiffCommitSelector.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_src/js/components/DiffCommitSelector.vue b/web_src/js/components/DiffCommitSelector.vue index 3f7100d201db4..439840c30639f 100644 --- a/web_src/js/components/DiffCommitSelector.vue +++ b/web_src/js/components/DiffCommitSelector.vue @@ -94,7 +94,7 @@ export default { focusElem(elem, prevElem) { if (elem) { elem.tabIndex = 0; - prevElem.tabIndex = -1; + if (prevElem) prevElem.tabIndex = -1; elem.focus(); } }, From 8557a9455b06c2e17982e9bae5263617500cf5b4 Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Mon, 6 Nov 2023 07:09:34 +0100 Subject: [PATCH 20/37] Revert #27870 (#27917) Now that we have #27798 we don't need the check from #27870 anymore. With the check it's not possible to remove an inactive auth source from the user. --- routers/web/user/setting/security/security.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routers/web/user/setting/security/security.go b/routers/web/user/setting/security/security.go index e64901ae728e7..ec269776e2b6b 100644 --- a/routers/web/user/setting/security/security.go +++ b/routers/web/user/setting/security/security.go @@ -84,7 +84,7 @@ func loadSecurityData(ctx *context.Context) { // map the provider display name with the AuthSource sources := make(map[*auth_model.Source]string) for _, externalAccount := range accountLinks { - if authSource, err := auth_model.GetSourceByID(ctx, externalAccount.LoginSourceID); err == nil && authSource.IsActive { + if authSource, err := auth_model.GetSourceByID(ctx, externalAccount.LoginSourceID); err == nil { var providerDisplayName string type DisplayNamed interface { From 4f4fea734cbd97fbc606e772999a8ac7a93dc46b Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Mon, 6 Nov 2023 09:22:39 +0100 Subject: [PATCH 21/37] Unify two factor check (#27915) Fixes #27819 We have support for two factor logins with the normal web login and with basic auth. For basic auth the two factor check was implemented at three different places and you need to know that this check is necessary. This PR moves the check into the basic auth itself. --- modules/context/api.go | 27 -------------- routers/api/v1/api.go | 36 ------------------- services/auth/basic.go | 24 +++++++++++-- tests/integration/api_twofa_test.go | 55 +++++++++++++++++++++++++++++ 4 files changed, 77 insertions(+), 65 deletions(-) create mode 100644 tests/integration/api_twofa_test.go diff --git a/modules/context/api.go b/modules/context/api.go index a46af6ed78232..ba35adf8310d7 100644 --- a/modules/context/api.go +++ b/modules/context/api.go @@ -11,7 +11,6 @@ import ( "net/url" "strings" - "code.gitea.io/gitea/models/auth" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" @@ -211,32 +210,6 @@ func (ctx *APIContext) SetLinkHeader(total, pageSize int) { } } -// CheckForOTP validates OTP -func (ctx *APIContext) CheckForOTP() { - if skip, ok := ctx.Data["SkipLocalTwoFA"]; ok && skip.(bool) { - return // Skip 2FA - } - - otpHeader := ctx.Req.Header.Get("X-Gitea-OTP") - twofa, err := auth.GetTwoFactorByUID(ctx, ctx.Doer.ID) - if err != nil { - if auth.IsErrTwoFactorNotEnrolled(err) { - return // No 2FA enrollment for this user - } - ctx.Error(http.StatusInternalServerError, "GetTwoFactorByUID", err) - return - } - ok, err := twofa.ValidateTOTP(otpHeader) - if err != nil { - ctx.Error(http.StatusInternalServerError, "ValidateTOTP", err) - return - } - if !ok { - ctx.Error(http.StatusUnauthorized, "", nil) - return - } -} - // APIContexter returns apicontext as middleware func APIContexter() func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 61658d213b365..cadddb44c39ef 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -316,10 +316,6 @@ func reqToken() func(ctx *context.APIContext) { return } - if ctx.IsBasicAuth { - ctx.CheckForOTP() - return - } if ctx.IsSigned { return } @@ -344,7 +340,6 @@ func reqBasicOrRevProxyAuth() func(ctx *context.APIContext) { ctx.Error(http.StatusUnauthorized, "reqBasicAuth", "auth required") return } - ctx.CheckForOTP() } } @@ -701,12 +696,6 @@ func bind[T any](_ T) any { } } -// The OAuth2 plugin is expected to be executed first, as it must ignore the user id stored -// in the session (if there is a user id stored in session other plugins might return the user -// object for that id). -// -// The Session plugin is expected to be executed second, in order to skip authentication -// for users that have already signed in. func buildAuthGroup() *auth.Group { group := auth.NewGroup( &auth.OAuth2{}, @@ -786,31 +775,6 @@ func verifyAuthWithOptions(options *common.VerifyOptions) func(ctx *context.APIC }) return } - if ctx.IsSigned && ctx.IsBasicAuth { - if skip, ok := ctx.Data["SkipLocalTwoFA"]; ok && skip.(bool) { - return // Skip 2FA - } - twofa, err := auth_model.GetTwoFactorByUID(ctx, ctx.Doer.ID) - if err != nil { - if auth_model.IsErrTwoFactorNotEnrolled(err) { - return // No 2FA enrollment for this user - } - ctx.InternalServerError(err) - return - } - otpHeader := ctx.Req.Header.Get("X-Gitea-OTP") - ok, err := twofa.ValidateTOTP(otpHeader) - if err != nil { - ctx.InternalServerError(err) - return - } - if !ok { - ctx.JSON(http.StatusForbidden, map[string]string{ - "message": "Only signed in user is allowed to call APIs.", - }) - return - } - } } if options.AdminRequired { diff --git a/services/auth/basic.go b/services/auth/basic.go index 6c3fbf595e44c..1184d12d1c4b4 100644 --- a/services/auth/basic.go +++ b/services/auth/basic.go @@ -15,6 +15,7 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web/middleware" ) @@ -131,11 +132,30 @@ func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore return nil, err } - if skipper, ok := source.Cfg.(LocalTwoFASkipper); ok && skipper.IsSkipLocalTwoFA() { - store.GetData()["SkipLocalTwoFA"] = true + if skipper, ok := source.Cfg.(LocalTwoFASkipper); !ok || !skipper.IsSkipLocalTwoFA() { + if err := validateTOTP(req, u); err != nil { + return nil, err + } } log.Trace("Basic Authorization: Logged in user %-v", u) return u, nil } + +func validateTOTP(req *http.Request, u *user_model.User) error { + twofa, err := auth_model.GetTwoFactorByUID(req.Context(), u.ID) + if err != nil { + if auth_model.IsErrTwoFactorNotEnrolled(err) { + // No 2FA enrollment for this user + return nil + } + return err + } + if ok, err := twofa.ValidateTOTP(req.Header.Get("X-Gitea-OTP")); err != nil { + return err + } else if !ok { + return util.NewInvalidArgumentErrorf("invalid provided OTP") + } + return nil +} diff --git a/tests/integration/api_twofa_test.go b/tests/integration/api_twofa_test.go new file mode 100644 index 0000000000000..1e5e26b8ccb0b --- /dev/null +++ b/tests/integration/api_twofa_test.go @@ -0,0 +1,55 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package integration + +import ( + "net/http" + "testing" + "time" + + auth_model "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/tests" + + "github.com/pquerna/otp/totp" + "github.com/stretchr/testify/assert" +) + +func TestAPITwoFactor(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 16}) + + req := NewRequestf(t, "GET", "/api/v1/user") + req = AddBasicAuthHeader(req, user.Name) + MakeRequest(t, req, http.StatusOK) + + otpKey, err := totp.Generate(totp.GenerateOpts{ + SecretSize: 40, + Issuer: "gitea-test", + AccountName: user.Name, + }) + assert.NoError(t, err) + + tfa := &auth_model.TwoFactor{ + UID: user.ID, + } + assert.NoError(t, tfa.SetSecret(otpKey.Secret())) + + assert.NoError(t, auth_model.NewTwoFactor(db.DefaultContext, tfa)) + + req = NewRequestf(t, "GET", "/api/v1/user") + req = AddBasicAuthHeader(req, user.Name) + MakeRequest(t, req, http.StatusUnauthorized) + + passcode, err := totp.GenerateCode(otpKey.Secret(), time.Now()) + assert.NoError(t, err) + + req = NewRequestf(t, "GET", "/api/v1/user") + req = AddBasicAuthHeader(req, user.Name) + req.Header.Set("X-Gitea-OTP", passcode) + MakeRequest(t, req, http.StatusOK) +} From 7a2ff6c1621c8b1bfb24fa10bf4fa714a0882c9f Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Mon, 6 Nov 2023 18:23:50 +0900 Subject: [PATCH 22/37] Fix edit topic UI (#27925) Before: desktop view: ![image](https://github.com/go-gitea/gitea/assets/18380374/c04d8730-5dac-4318-9643-72801da0ca16) mobile view: ![image](https://github.com/go-gitea/gitea/assets/18380374/187220d3-1fcc-4db8-9cf8-ee88fe92d989) after click `Save` btn: ![image](https://github.com/go-gitea/gitea/assets/18380374/c3242470-7e6f-4ddc-a2ac-d896bbd39529) ![image](https://github.com/go-gitea/gitea/assets/18380374/82e03ba5-a6d2-4437-b074-387e7ad5b6ba) refresh the page, you will see that `gt-m-0` is missing after save topic: ![image](https://github.com/go-gitea/gitea/assets/18380374/26914820-9363-483c-af70-78b76de47523) After: desktop view: ![image](https://github.com/go-gitea/gitea/assets/18380374/12d878e1-351e-4983-b3d9-5216e9f30a19) mobile view: ![image](https://github.com/go-gitea/gitea/assets/18380374/4adf9b0f-977b-4bbc-a42f-b9bfb2d648dd) after click `Save` btn: ![image](https://github.com/go-gitea/gitea/assets/18380374/064c911e-d074-4432-8a55-8ff8ddb93989) --- templates/repo/home.tmpl | 6 +++--- web_src/js/features/repo-home.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl index d3f0ead729999..81b2ba1b0bd19 100644 --- a/templates/repo/home.tmpl +++ b/templates/repo/home.tmpl @@ -35,9 +35,9 @@
{{end}} {{if and .Permission.IsAdmin (not .Repository.IsArchived)}} -
-
-