From 203f79a1210ce4fc2fbbbd543aaee9de16149a31 Mon Sep 17 00:00:00 2001 From: Kerwin Bryant Date: Tue, 19 Nov 2024 14:57:55 +0800 Subject: [PATCH 1/9] Fix a compilation error in the Gitpod environment (#32559) When opening the latest code in **Gitpod** and running `make lint-backend`, the following error occurs: ```bash gitpod /workspace/gitea (main) $ make lint-backend go run github.com/golangci/golangci-lint/cmd/golangci-lint@v1.60.3 run # internal/profilerecord compile: version "go1.23.1" does not match go tool version "go1.22.9" # internal/goarch compile: version "go1.23.1" does not match go tool version "go1.22.9" # unicode/utf8 compile: version "go1.23.1" does not match go tool version "go1.22.9" # internal/coverage/rtcov compile: version "go1.23.1" does not match go tool version "go1.22.9" # internal/byteorder compile: version "go1.23.1" does not match go tool version "go1.22.9" # cmp compile: version "go1.23.1" does not match go tool version "go1.22.9" # internal/itoa compile: version "go1.23.1" does not match go tool version "go1.22.9" # internal/race compile: version "go1.23.1" does not match go tool version "go1.22.9" # internal/goos compile: version "go1.23.1" does not match go tool version "go1.22.9" # internal/unsafeheader compile: version "go1.23.1" does not match go tool version "go1.22.9" # unicode compile: version "go1.23.1" does not match go tool version "go1.22.9" # internal/godebugs compile: version "go1.23.1" does not match go tool version "go1.22.9" # internal/asan compile: version "go1.23.1" does not match go tool version "go1.22.9" # math/bits compile: version "go1.23.1" does not match go tool version "go1.22.9" # internal/goexperiment compile: version "go1.23.1" does not match go tool version "go1.22.9" # internal/msan compile: version "go1.23.1" does not match go tool version "go1.22.9" # internal/runtime/atomic compile: version "go1.23.1" does not match go tool version "go1.22.9" # sync/atomic compile: version "go1.23.1" does not match go tool version "go1.22.9" # internal/runtime/syscall compile: version "go1.23.1" does not match go tool version "go1.22.9" # crypto/internal/alias compile: version "go1.23.1" does not match go tool version "go1.22.9" # encoding compile: version "go1.23.1" does not match go tool version "go1.22.9" # log/internal compile: version "go1.23.1" does not match go tool version "go1.22.9" # vendor/golang.org/x/crypto/cryptobyte/asn1 compile: version "go1.23.1" does not match go tool version "go1.22.9" # github.com/golangci/golangci-lint/pkg/exitcodes compile: version "go1.23.1" does not match go tool version "go1.22.9" # internal/cpu compile: version "go1.23.1" does not match go tool version "go1.22.9" # unicode/utf16 compile: version "go1.23.1" does not match go tool version "go1.22.9" # container/list compile: version "go1.23.1" does not match go tool version "go1.22.9" # crypto/subtle compile: version "go1.23.1" does not match go tool version "go1.22.9" # internal/goversion compile: version "go1.23.1" does not match go tool version "go1.22.9" # golang.org/x/exp/maps compile: version "go1.23.1" does not match go tool version "go1.22.9" # github.com/ccojocar/zxcvbn-go/match compile: version "go1.23.1" does not match go tool version "go1.22.9" # golang.org/x/exp/constraints compile: version "go1.23.1" does not match go tool version "go1.22.9" # golang.org/x/tools/internal/packagesinternal compile: version "go1.23.1" does not match go tool version "go1.22.9" # github.com/quasilyte/go-ruleguard/dsl/types compile: version "go1.23.1" does not match go tool version "go1.22.9" # vendor/golang.org/x/crypto/internal/alias compile: version "go1.23.1" does not match go tool version "go1.22.9" # internal/nettrace compile: version "go1.23.1" does not match go tool version "go1.22.9" # github.com/google/go-cmp/cmp/internal/flags compile: version "go1.23.1" does not match go tool version "go1.22.9" # github.com/gobwas/glob/util/runes compile: version "go1.23.1" does not match go tool version "go1.22.9" # internal/platform compile: version "go1.23.1" does not match go tool version "go1.22.9" # crypto/internal/boring/sig compile: version "go1.23.1" does not match go tool version "go1.22.9" # github.com/quasilyte/gogrep/internal/stdinfo compile: version "go1.23.1" does not match go tool version "go1.22.9" # github.com/daixiang0/gci/pkg/utils compile: version "go1.23.1" does not match go tool version "go1.22.9" # github.com/quasilyte/stdinfo compile: version "go1.23.1" does not match go tool version "go1.22.9" # github.com/Antonboom/testifylint/internal/testify compile: version "go1.23.1" does not match go tool version "go1.22.9" # hash/maphash compile: version "go1.23.1" does not match go tool version "go1.22.9" # github.com/nunnatsa/ginkgolinter/version compile: version "go1.23.1" does not match go tool version "go1.22.9" # google.golang.org/protobuf/internal/flags compile: version "go1.23.1" does not match go tool version "go1.22.9" make: *** [Makefile:413: lint-go] Error 1 ``` (cherry picked from commit 32456b6f314f993efdc65fc90248b6fd1a8d55ef) --- flake.nix | 1 - 1 file changed, 1 deletion(-) diff --git a/flake.nix b/flake.nix index e2f273e341..880b1279b6 100644 --- a/flake.nix +++ b/flake.nix @@ -29,7 +29,6 @@ poetry # backend - go_1_22 gofumpt sqlite ]; From f3afd18b76c50e8493b2163db2711214e14d4cfd Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 19 Nov 2024 08:21:13 -0800 Subject: [PATCH 2/9] Remove unnecessary code (#32560) PushMirrors only be used in the repository setting page. So it should not be loaded on every repository page. (cherry picked from commit 0d5abd9b3e04a09f5d7de720c99e3451723e028e) --- services/context/repo.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/services/context/repo.go b/services/context/repo.go index d2cee086d6..45a046eff6 100644 --- a/services/context/repo.go +++ b/services/context/repo.go @@ -413,14 +413,7 @@ func repoAssignment(ctx *Context, repo *repo_model.Repository) { } } - pushMirrors, _, err := repo_model.GetPushMirrorsByRepoID(ctx, repo.ID, db.ListOptions{}) - if err != nil { - ctx.ServerError("GetPushMirrorsByRepoID", err) - return - } - ctx.Repo.Repository = repo - ctx.Data["PushMirrors"] = pushMirrors ctx.Data["RepoName"] = ctx.Repo.Repository.Name ctx.Data["IsEmptyRepo"] = ctx.Repo.Repository.IsEmpty ctx.Data["DefaultWikiBranchName"] = setting.Repository.DefaultBranch From e9928b757796b69e099f4c15d6efdd99bba26f25 Mon Sep 17 00:00:00 2001 From: Kemal Zebari <60799661+kemzeb@users.noreply.github.com> Date: Tue, 19 Nov 2024 17:05:06 -0800 Subject: [PATCH 3/9] Remove duplicate empty repo check in delete branch API (#32569) Found while working on #32433. This branch will never be executed because we have would have already made the same check a couple lines above. (cherry picked from commit 355889dbc2432554f0bcdb22f918488849f0016c) --- routers/api/v1/repo/branch.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/routers/api/v1/repo/branch.go b/routers/api/v1/repo/branch.go index a468fd90d0..e3e6efa781 100644 --- a/routers/api/v1/repo/branch.go +++ b/routers/api/v1/repo/branch.go @@ -133,11 +133,6 @@ func DeleteBranch(ctx *context.APIContext) { branchName := ctx.Params("*") - if ctx.Repo.Repository.IsEmpty { - ctx.Error(http.StatusForbidden, "", "Git Repository is empty.") - return - } - // check whether branches of this repository has been synced totalNumOfBranches, err := db.Count[git_model.Branch](ctx, git_model.FindBranchOptions{ RepoID: ctx.Repo.Repository.ID, From 262c48409b1224e3f6dc63c8d1e04fef0e0cf2c0 Mon Sep 17 00:00:00 2001 From: Marcell Mars Date: Wed, 20 Nov 2024 15:22:48 +0100 Subject: [PATCH 4/9] Support HTTP POST requests to `/userinfo`, aligning to OpenID Core specification (#32578) This PR adds support for the HTTP POST requests to `/userinfo` endpoint. While the OpenID Core specification says both are supported and recommends using HTTP GET. ref: https://openid.net/specs/openid-connect-core-1_0.html#UserInfo (cherry picked from commit 56bff7ae234ee21d0e4524e401a49385c383ccaf) Conflicts: routers/web/web.go trivial context conflict --- routers/web/web.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routers/web/web.go b/routers/web/web.go index 1a764103fc..fdfda68b1e 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -530,7 +530,7 @@ func registerRoutes(m *web.Route) { m.Post("/authorize", web.Bind(forms.AuthorizationForm{}), auth.AuthorizeOAuth) }, ignSignInAndCsrf, reqSignIn) - m.Methods("GET, OPTIONS", "/userinfo", optionsCorsHandler(), ignSignInAndCsrf, auth.InfoOAuth) + m.Methods("GET, POST, OPTIONS", "/userinfo", optionsCorsHandler(), ignSignInAndCsrf, auth.InfoOAuth) m.Methods("POST, OPTIONS", "/access_token", optionsCorsHandler(), web.Bind(forms.AccessTokenForm{}), ignSignInAndCsrf, auth.AccessTokenOAuth) m.Methods("GET, OPTIONS", "/keys", optionsCorsHandler(), ignSignInAndCsrf, auth.OIDCKeys) m.Methods("POST, OPTIONS", "/introspect", optionsCorsHandler(), web.Bind(forms.IntrospectTokenForm{}), ignSignInAndCsrf, auth.IntrospectOAuth) From ddabba5f89c4b196daeeb2af17de9ec2cec14b63 Mon Sep 17 00:00:00 2001 From: Rowan Bohde Date: Wed, 20 Nov 2024 09:24:09 -0600 Subject: [PATCH 5/9] allow the actions user to login via the jwt token (#32527) We have some actions that leverage the Gitea API that began receiving 401 errors, with a message that the user was not found. These actions use the `ACTIONS_RUNTIME_TOKEN` env var in the actions job to authenticate with the Gitea API. The format of this env var in actions jobs changed with go-gitea/gitea/pull/28885 to be a JWT (with a corresponding update to `act_runner`) Since it was a JWT, the OAuth parsing logic attempted to parse it as an OAuth token, and would return user not found, instead of falling back to look up the running task and assigning it to the actions user. Make ACTIONS_RUNTIME_TOKEN in action runners could be used, attempting to parse Oauth JWTs. The code to parse potential old `ACTION_RUNTIME_TOKEN` was kept in case someone is running an older version of act_runner that doesn't support the Actions JWT. (cherry picked from commit 407b6e6dfc7ee9ebb8a16c7f1a786e4c24d0516e) Conflicts: services/auth/oauth2.go trivial context conflicts because OAuth2 scopes are in Forgejo and not yet in Gitea --- models/fixtures/action_task.yml | 19 ++++++++++++ services/actions/auth.go | 11 +++++-- services/auth/oauth2.go | 24 +++++++++++++- services/auth/oauth2_test.go | 55 +++++++++++++++++++++++++++++++++ 4 files changed, 105 insertions(+), 4 deletions(-) create mode 100644 services/auth/oauth2_test.go diff --git a/models/fixtures/action_task.yml b/models/fixtures/action_task.yml index 443effe08c..d88a8ed8a9 100644 --- a/models/fixtures/action_task.yml +++ b/models/fixtures/action_task.yml @@ -1,3 +1,22 @@ +- + id: 46 + attempt: 3 + runner_id: 1 + status: 3 # 3 is the status code for "cancelled" + started: 1683636528 + stopped: 1683636626 + repo_id: 4 + owner_id: 1 + commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0 + is_fork_pull_request: 0 + token_hash: 6d8ef48297195edcc8e22c70b3020eaa06c52976db67d39b4260c64a69a2cc1508825121b7b8394e48e00b1bf8718b2aaaaa + token_salt: eeeeeeee + token_last_eight: eeeeeeee + log_filename: artifact-test2/2f/47.log + log_in_storage: 1 + log_length: 707 + log_size: 90179 + log_expired: 0 - id: 47 job_id: 192 diff --git a/services/actions/auth.go b/services/actions/auth.go index 8e934d89a8..1ef21f6e0e 100644 --- a/services/actions/auth.go +++ b/services/actions/auth.go @@ -83,7 +83,12 @@ func ParseAuthorizationToken(req *http.Request) (int64, error) { return 0, fmt.Errorf("split token failed") } - token, err := jwt.ParseWithClaims(parts[1], &actionsClaims{}, func(t *jwt.Token) (any, error) { + return TokenToTaskID(parts[1]) +} + +// TokenToTaskID returns the TaskID associated with the provided JWT token +func TokenToTaskID(token string) (int64, error) { + parsedToken, err := jwt.ParseWithClaims(token, &actionsClaims{}, func(t *jwt.Token) (any, error) { if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok { return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"]) } @@ -93,8 +98,8 @@ func ParseAuthorizationToken(req *http.Request) (int64, error) { return 0, err } - c, ok := token.Claims.(*actionsClaims) - if !token.Valid || !ok { + c, ok := parsedToken.Claims.(*actionsClaims) + if !parsedToken.Valid || !ok { return 0, fmt.Errorf("invalid token claim") } diff --git a/services/auth/oauth2.go b/services/auth/oauth2.go index 8b625a193e..b983e57ecd 100644 --- a/services/auth/oauth2.go +++ b/services/auth/oauth2.go @@ -18,6 +18,7 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/web/middleware" + "code.gitea.io/gitea/services/actions" "code.gitea.io/gitea/services/auth/source/oauth2" ) @@ -94,6 +95,18 @@ func CheckOAuthAccessToken(ctx context.Context, accessToken string) (int64, stri return grant.UserID, grantScopes } +// CheckTaskIsRunning verifies that the TaskID corresponds to a running task +func CheckTaskIsRunning(ctx context.Context, taskID int64) bool { + // Verify the task exists + task, err := actions_model.GetTaskByID(ctx, taskID) + if err != nil { + return false + } + + // Verify that it's running + return task.Status == actions_model.StatusRunning +} + // OAuth2 implements the Auth interface and authenticates requests // (API requests only) by looking for an OAuth token in query parameters or the // "Authorization" header. @@ -137,8 +150,17 @@ func parseToken(req *http.Request) (string, bool) { func (o *OAuth2) userIDFromToken(ctx context.Context, tokenSHA string, store DataStore) int64 { // Let's see if token is valid. if strings.Contains(tokenSHA, ".") { - uid, grantScopes := CheckOAuthAccessToken(ctx, tokenSHA) + // First attempt to decode an actions JWT, returning the actions user + if taskID, err := actions.TokenToTaskID(tokenSHA); err == nil { + if CheckTaskIsRunning(ctx, taskID) { + store.GetData()["IsActionsToken"] = true + store.GetData()["ActionsTaskID"] = taskID + return user_model.ActionsUserID + } + } + // Otherwise, check if this is an OAuth access token + uid, grantScopes := CheckOAuthAccessToken(ctx, tokenSHA) if uid != 0 { store.GetData()["IsApiToken"] = true if grantScopes != "" { diff --git a/services/auth/oauth2_test.go b/services/auth/oauth2_test.go new file mode 100644 index 0000000000..c9b4ed06cc --- /dev/null +++ b/services/auth/oauth2_test.go @@ -0,0 +1,55 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package auth + +import ( + "context" + "testing" + + "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/web/middleware" + "code.gitea.io/gitea/services/actions" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestUserIDFromToken(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + + t.Run("Actions JWT", func(t *testing.T) { + const RunningTaskID = 47 + token, err := actions.CreateAuthorizationToken(RunningTaskID, 1, 2) + require.NoError(t, err) + + ds := make(middleware.ContextData) + + o := OAuth2{} + uid := o.userIDFromToken(context.Background(), token, ds) + assert.Equal(t, int64(user_model.ActionsUserID), uid) + assert.Equal(t, true, ds["IsActionsToken"]) + assert.Equal(t, ds["ActionsTaskID"], int64(RunningTaskID)) + }) +} + +func TestCheckTaskIsRunning(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + cases := map[string]struct { + TaskID int64 + Expected bool + }{ + "Running": {TaskID: 47, Expected: true}, + "Missing": {TaskID: 1, Expected: false}, + "Cancelled": {TaskID: 46, Expected: false}, + } + + for name := range cases { + c := cases[name] + t.Run(name, func(t *testing.T) { + actual := CheckTaskIsRunning(context.Background(), c.TaskID) + assert.Equal(t, c.Expected, actual) + }) + } +} From 64824290912b6300ede2b2f95ff77d55dde9859b Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 19 Nov 2024 11:44:21 -0800 Subject: [PATCH 6/9] Fix submodule parsing (cherry picked from commit 33850a83fe4ebd23a762a7aac81614c42e303bfa) This really is just the cherry pick of 407b6e6dfc7ee9ebb8a16c7f1a786e4c24d0516e which is the first commit of the pull request, the one with the change. The rest of the changes is a refactor that is unrelated to the bug fix. Conflicts: modules/git/commit_test.go trivial context conflict --- modules/git/commit.go | 45 ++++++++++++++++++-------------------- modules/git/commit_test.go | 30 +++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 24 deletions(-) diff --git a/modules/git/commit.go b/modules/git/commit.go index b5ae2e0e52..0f3b749227 100644 --- a/modules/git/commit.go +++ b/modules/git/commit.go @@ -17,6 +17,8 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/util" + + "github.com/go-git/go-git/v5/config" ) // Commit represents a git commit. @@ -365,37 +367,32 @@ func (c *Commit) GetSubModules() (*ObjectCache, error) { return nil, err } - rd, err := entry.Blob().DataAsync() + content, err := entry.Blob().GetBlobContent(10 * 1024) if err != nil { return nil, err } - defer rd.Close() - scanner := bufio.NewScanner(rd) - c.submoduleCache = newObjectCache() - var ismodule bool - var path string - for scanner.Scan() { - if strings.HasPrefix(scanner.Text(), "[submodule") { - ismodule = true - continue - } - if ismodule { - fields := strings.Split(scanner.Text(), "=") - k := strings.TrimSpace(fields[0]) - if k == "path" { - path = strings.TrimSpace(fields[1]) - } else if k == "url" { - c.submoduleCache.Set(path, &SubModule{path, strings.TrimSpace(fields[1])}) - ismodule = false - } - } + c.submoduleCache, err = parseSubmoduleContent([]byte(content)) + if err != nil { + return nil, err } - if err = scanner.Err(); err != nil { - return nil, fmt.Errorf("GetSubModules scan: %w", err) + return c.submoduleCache, nil +} + +func parseSubmoduleContent(bs []byte) (*ObjectCache, error) { + cfg := config.NewModules() + if err := cfg.Unmarshal(bs); err != nil { + return nil, err + } + submoduleCache := newObjectCache() + if len(cfg.Submodules) == 0 { + return nil, fmt.Errorf("no submodules found") + } + for _, subModule := range cfg.Submodules { + submoduleCache.Set(subModule.Path, subModule.URL) } - return c.submoduleCache, nil + return submoduleCache, nil } // GetSubModule get the sub module according entryname diff --git a/modules/git/commit_test.go b/modules/git/commit_test.go index af85bfe093..6bb7d776f5 100644 --- a/modules/git/commit_test.go +++ b/modules/git/commit_test.go @@ -369,3 +369,33 @@ func TestParseCommitRenames(t *testing.T) { assert.Equal(t, testcase.renames, renames) } } + +func Test_parseSubmoduleContent(t *testing.T) { + submoduleFiles := []struct { + fileContent string + expectedPath string + expectedURL string + }{ + { + fileContent: `[submodule "jakarta-servlet"] +url = ../../ALP-pool/jakarta-servlet +path = jakarta-servlet`, + expectedPath: "jakarta-servlet", + expectedURL: "../../ALP-pool/jakarta-servlet", + }, + { + fileContent: `[submodule "jakarta-servlet"] +path = jakarta-servlet +url = ../../ALP-pool/jakarta-servlet`, + expectedPath: "jakarta-servlet", + expectedURL: "../../ALP-pool/jakarta-servlet", + }, + } + for _, kase := range submoduleFiles { + submodule, err := parseSubmoduleContent([]byte(kase.fileContent)) + require.NoError(t, err) + v, ok := submodule.Get(kase.expectedPath) + assert.True(t, ok) + assert.Equal(t, kase.expectedURL, v) + } +} From 32a91add34519ef7768ec907888ed837ad0dde2f Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 20 Nov 2024 20:55:32 -0800 Subject: [PATCH 7/9] Fix GetInactiveUsers (#32540) Fix #31480 (cherry picked from commit 9bf821ae6c108379d22ae11d8d5784a4ed7ad647) Conflicts: models/user/user_test.go trivial context conflict --- models/fixtures/user.yml | 1 + models/user/user.go | 18 ++++++++++++------ models/user/user_test.go | 14 ++++++++++++++ 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/models/fixtures/user.yml b/models/fixtures/user.yml index 8e216fbc7d..73b9a97e1b 100644 --- a/models/fixtures/user.yml +++ b/models/fixtures/user.yml @@ -332,6 +332,7 @@ repo_admin_change_team_access: false theme: "" keep_activity_private: false + created_unix: 1730468968 - id: 10 diff --git a/models/user/user.go b/models/user/user.go index 0fa8bb0ca9..96f8c3f729 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -50,19 +50,19 @@ const ( UserTypeIndividual UserType = iota // Historic reason to make it starts at 0. // UserTypeOrganization defines an organization - UserTypeOrganization + UserTypeOrganization // 1 // UserTypeUserReserved reserves a (non-existing) user, i.e. to prevent a spam user from re-registering after being deleted, or to reserve the name until the user is actually created later on - UserTypeUserReserved + UserTypeUserReserved // 2 // UserTypeOrganizationReserved reserves a (non-existing) organization, to be used in combination with UserTypeUserReserved - UserTypeOrganizationReserved + UserTypeOrganizationReserved // 3 // UserTypeBot defines a bot user - UserTypeBot + UserTypeBot // 4 // UserTypeRemoteUser defines a remote user for federated users - UserTypeRemoteUser + UserTypeRemoteUser // 5 ) const ( @@ -919,7 +919,13 @@ func UpdateUserCols(ctx context.Context, u *User, cols ...string) error { // GetInactiveUsers gets all inactive users func GetInactiveUsers(ctx context.Context, olderThan time.Duration) ([]*User, error) { - var cond builder.Cond = builder.Eq{"is_active": false} + cond := builder.And( + builder.Eq{"is_active": false}, + builder.Or( // only plain user + builder.Eq{"`type`": UserTypeIndividual}, + builder.Eq{"`type`": UserTypeUserReserved}, + ), + ) if olderThan > 0 { cond = cond.And(builder.Lt{"created_unix": time.Now().Add(-olderThan).Unix()}) diff --git a/models/user/user_test.go b/models/user/user_test.go index 6f20a577f3..ee456e3ce4 100644 --- a/models/user/user_test.go +++ b/models/user/user_test.go @@ -766,3 +766,17 @@ func TestVerifyUserAuthorizationToken(t *testing.T) { assert.Nil(t, authToken) }) } + +func TestGetInactiveUsers(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + + // all inactive users + // user1's createdunix is 1730468968 + users, err := user_model.GetInactiveUsers(db.DefaultContext, 0) + require.NoError(t, err) + assert.Len(t, users, 1) + interval := time.Now().Unix() - 1730468968 + 3600*24 + users, err = user_model.GetInactiveUsers(db.DefaultContext, time.Duration(interval*int64(time.Second))) + require.NoError(t, err) + require.Empty(t, users) +} From eb0645f1854b7903b8d771480fffd6a4958a2988 Mon Sep 17 00:00:00 2001 From: Rowan Bohde Date: Wed, 20 Nov 2024 22:30:48 -0600 Subject: [PATCH 8/9] disable gravatar in test (#32529) When running e2e tests on flaky networks, gravatar can cause a timeout and test failures. Turn off, and populate avatars on e2e test suite run to make them reliable. (cherry picked from commit 9ac74a1a408136455a9e0586fb8e65163048597b) Conflicts: models/fixtures/user.yml services/repository/contributors_graph_test.go trivial context conflicts --- models/fixtures/system_setting.yml | 2 +- models/fixtures/user.yml | 161 +++++++++--------- modules/repository/commits_test.go | 9 +- .../repository/contributors_graph_test.go | 4 +- tests/integration/opengraph_test.go | 21 ++- 5 files changed, 99 insertions(+), 98 deletions(-) diff --git a/models/fixtures/system_setting.yml b/models/fixtures/system_setting.yml index 30542bc82a..dcad176c89 100644 --- a/models/fixtures/system_setting.yml +++ b/models/fixtures/system_setting.yml @@ -1,7 +1,7 @@ - id: 1 setting_key: 'picture.disable_gravatar' - setting_value: 'false' + setting_value: 'true' version: 1 created: 1653533198 updated: 1653533198 diff --git a/models/fixtures/user.yml b/models/fixtures/user.yml index 73b9a97e1b..dbc6fc3e38 100644 --- a/models/fixtures/user.yml +++ b/models/fixtures/user.yml @@ -23,9 +23,9 @@ allow_import_local: false allow_create_organization: true prohibit_login: false - avatar: avatar1 + avatar: "" avatar_email: user1@example.com - use_custom_avatar: false + use_custom_avatar: true num_followers: 0 num_following: 0 num_stars: 0 @@ -60,8 +60,9 @@ allow_import_local: false allow_create_organization: true prohibit_login: false - avatar: avatar2 + avatar: "" avatar_email: user2@example.com + # cause a random avatar to be generated when referenced for test purposes use_custom_avatar: false num_followers: 2 num_following: 1 @@ -97,9 +98,9 @@ allow_import_local: false allow_create_organization: true prohibit_login: false - avatar: avatar3 + avatar: "" avatar_email: org3@example.com - use_custom_avatar: false + use_custom_avatar: true num_followers: 0 num_following: 0 num_stars: 0 @@ -134,9 +135,9 @@ allow_import_local: false allow_create_organization: true prohibit_login: false - avatar: avatar4 + avatar: "" avatar_email: user4@example.com - use_custom_avatar: false + use_custom_avatar: true num_followers: 0 num_following: 1 num_stars: 0 @@ -171,9 +172,9 @@ allow_import_local: false allow_create_organization: false prohibit_login: false - avatar: avatar5 + avatar: "" avatar_email: user5@example.com - use_custom_avatar: false + use_custom_avatar: true num_followers: 0 num_following: 0 num_stars: 0 @@ -208,9 +209,9 @@ allow_import_local: false allow_create_organization: true prohibit_login: false - avatar: avatar6 + avatar: "" avatar_email: org6@example.com - use_custom_avatar: false + use_custom_avatar: true num_followers: 0 num_following: 0 num_stars: 0 @@ -245,9 +246,9 @@ allow_import_local: false allow_create_organization: true prohibit_login: false - avatar: avatar7 + avatar: "" avatar_email: org7@example.com - use_custom_avatar: false + use_custom_avatar: true num_followers: 0 num_following: 0 num_stars: 0 @@ -282,9 +283,9 @@ allow_import_local: false allow_create_organization: true prohibit_login: false - avatar: avatar8 + avatar: "" avatar_email: user8@example.com - use_custom_avatar: false + use_custom_avatar: true num_followers: 1 num_following: 1 num_stars: 0 @@ -319,9 +320,9 @@ allow_import_local: false allow_create_organization: true prohibit_login: false - avatar: avatar9 + avatar: "" avatar_email: user9@example.com - use_custom_avatar: false + use_custom_avatar: true num_followers: 0 num_following: 0 num_stars: 0 @@ -357,9 +358,9 @@ allow_import_local: false allow_create_organization: true prohibit_login: false - avatar: avatar10 + avatar: "" avatar_email: user10@example.com - use_custom_avatar: false + use_custom_avatar: true num_followers: 0 num_following: 0 num_stars: 0 @@ -394,9 +395,9 @@ allow_import_local: false allow_create_organization: true prohibit_login: false - avatar: avatar11 + avatar: "" avatar_email: user11@example.com - use_custom_avatar: false + use_custom_avatar: true num_followers: 0 num_following: 0 num_stars: 0 @@ -431,9 +432,9 @@ allow_import_local: false allow_create_organization: true prohibit_login: false - avatar: avatar12 + avatar: "" avatar_email: user12@example.com - use_custom_avatar: false + use_custom_avatar: true num_followers: 0 num_following: 0 num_stars: 0 @@ -468,9 +469,9 @@ allow_import_local: false allow_create_organization: true prohibit_login: false - avatar: avatar13 + avatar: "" avatar_email: user13@example.com - use_custom_avatar: false + use_custom_avatar: true num_followers: 0 num_following: 0 num_stars: 0 @@ -505,9 +506,9 @@ allow_import_local: false allow_create_organization: true prohibit_login: false - avatar: avatar14 + avatar: "" avatar_email: user13@example.com - use_custom_avatar: false + use_custom_avatar: true num_followers: 0 num_following: 0 num_stars: 0 @@ -542,9 +543,9 @@ allow_import_local: false allow_create_organization: true prohibit_login: false - avatar: avatar15 + avatar: "" avatar_email: user15@example.com - use_custom_avatar: false + use_custom_avatar: true num_followers: 0 num_following: 0 num_stars: 0 @@ -579,9 +580,9 @@ allow_import_local: false allow_create_organization: true prohibit_login: false - avatar: avatar16 + avatar: "" avatar_email: user16@example.com - use_custom_avatar: false + use_custom_avatar: true num_followers: 0 num_following: 0 num_stars: 0 @@ -616,9 +617,9 @@ allow_import_local: false allow_create_organization: true prohibit_login: false - avatar: avatar17 + avatar: "" avatar_email: org17@example.com - use_custom_avatar: false + use_custom_avatar: true num_followers: 0 num_following: 0 num_stars: 0 @@ -653,9 +654,9 @@ allow_import_local: false allow_create_organization: true prohibit_login: false - avatar: avatar18 + avatar: "" avatar_email: user18@example.com - use_custom_avatar: false + use_custom_avatar: true num_followers: 0 num_following: 0 num_stars: 0 @@ -690,9 +691,9 @@ allow_import_local: false allow_create_organization: true prohibit_login: false - avatar: avatar19 + avatar: "" avatar_email: org19@example.com - use_custom_avatar: false + use_custom_avatar: true num_followers: 0 num_following: 0 num_stars: 0 @@ -727,9 +728,9 @@ allow_import_local: false allow_create_organization: true prohibit_login: false - avatar: avatar20 + avatar: "" avatar_email: user20@example.com - use_custom_avatar: false + use_custom_avatar: true num_followers: 0 num_following: 0 num_stars: 0 @@ -764,9 +765,9 @@ allow_import_local: false allow_create_organization: true prohibit_login: false - avatar: avatar21 + avatar: "" avatar_email: user21@example.com - use_custom_avatar: false + use_custom_avatar: true num_followers: 0 num_following: 0 num_stars: 0 @@ -801,9 +802,9 @@ allow_import_local: false allow_create_organization: true prohibit_login: false - avatar: avatar22 + avatar: "" avatar_email: limited_org@example.com - use_custom_avatar: false + use_custom_avatar: true num_followers: 0 num_following: 0 num_stars: 0 @@ -838,9 +839,9 @@ allow_import_local: false allow_create_organization: true prohibit_login: false - avatar: avatar23 + avatar: "" avatar_email: privated_org@example.com - use_custom_avatar: false + use_custom_avatar: true num_followers: 0 num_following: 0 num_stars: 0 @@ -875,9 +876,9 @@ allow_import_local: false allow_create_organization: true prohibit_login: false - avatar: avatar24 + avatar: "" avatar_email: user24@example.com - use_custom_avatar: false + use_custom_avatar: true num_followers: 0 num_following: 0 num_stars: 0 @@ -912,9 +913,9 @@ allow_import_local: false allow_create_organization: true prohibit_login: false - avatar: avatar25 + avatar: "" avatar_email: org25@example.com - use_custom_avatar: false + use_custom_avatar: true num_followers: 0 num_following: 0 num_stars: 0 @@ -949,9 +950,9 @@ allow_import_local: false allow_create_organization: true prohibit_login: false - avatar: avatar26 + avatar: "" avatar_email: org26@example.com - use_custom_avatar: false + use_custom_avatar: true num_followers: 0 num_following: 0 num_stars: 0 @@ -986,9 +987,9 @@ allow_import_local: false allow_create_organization: true prohibit_login: false - avatar: avatar27 + avatar: "" avatar_email: user27@example.com - use_custom_avatar: false + use_custom_avatar: true num_followers: 0 num_following: 0 num_stars: 0 @@ -1023,9 +1024,9 @@ allow_import_local: false allow_create_organization: true prohibit_login: false - avatar: avatar28 + avatar: "" avatar_email: user28@example.com - use_custom_avatar: false + use_custom_avatar: true num_followers: 0 num_following: 0 num_stars: 0 @@ -1060,9 +1061,9 @@ allow_import_local: false allow_create_organization: true prohibit_login: false - avatar: avatar29 + avatar: "" avatar_email: user29@example.com - use_custom_avatar: false + use_custom_avatar: true num_followers: 0 num_following: 0 num_stars: 0 @@ -1097,9 +1098,9 @@ allow_import_local: false allow_create_organization: true prohibit_login: false - avatar: avatar29 + avatar: "" avatar_email: user30@example.com - use_custom_avatar: false + use_custom_avatar: true num_followers: 0 num_following: 0 num_stars: 0 @@ -1134,9 +1135,9 @@ allow_import_local: false allow_create_organization: true prohibit_login: false - avatar: avatar31 + avatar: "" avatar_email: user31@example.com - use_custom_avatar: false + use_custom_avatar: true num_followers: 0 num_following: 1 num_stars: 0 @@ -1171,9 +1172,9 @@ allow_import_local: false allow_create_organization: true prohibit_login: false - avatar: avatar32 + avatar: "" avatar_email: user30@example.com - use_custom_avatar: false + use_custom_avatar: true num_followers: 0 num_following: 0 num_stars: 0 @@ -1208,9 +1209,9 @@ allow_import_local: false allow_create_organization: true prohibit_login: false - avatar: avatar33 + avatar: "" avatar_email: user33@example.com - use_custom_avatar: false + use_custom_avatar: true num_followers: 1 num_following: 0 num_stars: 0 @@ -1246,7 +1247,7 @@ allow_import_local: false allow_create_organization: false prohibit_login: false - avatar: avatar34 + avatar: "" avatar_email: user34@example.com use_custom_avatar: true num_followers: 0 @@ -1283,9 +1284,9 @@ allow_import_local: false allow_create_organization: true prohibit_login: false - avatar: avatar35 + avatar: "" avatar_email: private_org35@example.com - use_custom_avatar: false + use_custom_avatar: true num_followers: 0 num_following: 0 num_stars: 0 @@ -1320,9 +1321,9 @@ allow_import_local: false allow_create_organization: true prohibit_login: false - avatar: avatar22 + avatar: "" avatar_email: abcde@gitea.com - use_custom_avatar: false + use_custom_avatar: true num_followers: 0 num_following: 0 num_stars: 0 @@ -1357,9 +1358,9 @@ allow_import_local: false allow_create_organization: true prohibit_login: true - avatar: avatar29 + avatar: "" avatar_email: user37@example.com - use_custom_avatar: false + use_custom_avatar: true num_followers: 0 num_following: 0 num_stars: 0 @@ -1394,9 +1395,9 @@ allow_import_local: false allow_create_organization: true prohibit_login: false - avatar: avatar38 + avatar: "" avatar_email: user38@example.com - use_custom_avatar: false + use_custom_avatar: true num_followers: 0 num_following: 0 num_stars: 0 @@ -1431,9 +1432,9 @@ allow_import_local: false allow_create_organization: true prohibit_login: false - avatar: avatar39 + avatar: "" avatar_email: user39@example.com - use_custom_avatar: false + use_custom_avatar: true num_followers: 0 num_following: 0 num_stars: 0 @@ -1468,9 +1469,9 @@ allow_import_local: false allow_create_organization: true prohibit_login: false - avatar: avatar40 + avatar: "" avatar_email: user40@example.com - use_custom_avatar: false + use_custom_avatar: true num_followers: 0 num_following: 0 num_stars: 0 @@ -1505,9 +1506,9 @@ allow_import_local: false allow_create_organization: true prohibit_login: false - avatar: avatar41 + avatar: "" avatar_email: org41@example.com - use_custom_avatar: false + use_custom_avatar: true num_followers: 0 num_following: 0 num_stars: 0 diff --git a/modules/repository/commits_test.go b/modules/repository/commits_test.go index 82841b3268..a22f3d07b8 100644 --- a/modules/repository/commits_test.go +++ b/modules/repository/commits_test.go @@ -4,8 +4,6 @@ package repository import ( - "crypto/md5" - "fmt" "strconv" "testing" "time" @@ -126,15 +124,12 @@ func TestPushCommits_AvatarLink(t *testing.T) { }, } - setting.GravatarSource = "https://secure.gravatar.com/avatar" - setting.OfflineMode = true - assert.Equal(t, - "/avatars/avatar2?size="+strconv.Itoa(28*setting.Avatar.RenderedSizeFactor), + "/avatars/ab53a2911ddf9b4817ac01ddcd3d975f?size="+strconv.Itoa(28*setting.Avatar.RenderedSizeFactor), pushCommits.AvatarLink(db.DefaultContext, "user2@example.com")) assert.Equal(t, - fmt.Sprintf("https://secure.gravatar.com/avatar/%x?d=identicon&s=%d", md5.Sum([]byte("nonexistent@example.com")), 28*setting.Avatar.RenderedSizeFactor), + "/assets/img/avatar_default.png", pushCommits.AvatarLink(db.DefaultContext, "nonexistent@example.com")) } diff --git a/services/repository/contributors_graph_test.go b/services/repository/contributors_graph_test.go index 3f8d614501..c62bef25a1 100644 --- a/services/repository/contributors_graph_test.go +++ b/services/repository/contributors_graph_test.go @@ -43,7 +43,7 @@ func TestRepository_ContributorsGraph(t *testing.T) { dataString, isData := mockCache.Get("key2").(string) assert.True(t, isData) // Verify that JSON is actually stored in the cache. - assert.JSONEq(t, `{"ethantkoenig@gmail.com":{"name":"Ethan Koenig","login":"","avatar_link":"https://secure.gravatar.com/avatar/b42fb195faa8c61b8d88abfefe30e9e3?d=identicon","home_link":"","total_commits":1,"weeks":{"1511654400000":{"week":1511654400000,"additions":3,"deletions":0,"commits":1}}},"jimmy.praet@telenet.be":{"name":"Jimmy Praet","login":"","avatar_link":"https://secure.gravatar.com/avatar/93c49b7c89eb156971d11161c9b52795?d=identicon","home_link":"","total_commits":1,"weeks":{"1624752000000":{"week":1624752000000,"additions":2,"deletions":0,"commits":1}}},"jon@allspice.io":{"name":"Jon","login":"","avatar_link":"https://secure.gravatar.com/avatar/00388ce725e6886f3e07c3733007289b?d=identicon","home_link":"","total_commits":1,"weeks":{"1607817600000":{"week":1607817600000,"additions":10,"deletions":0,"commits":1}}},"total":{"name":"Total","login":"","avatar_link":"","home_link":"","total_commits":3,"weeks":{"1511654400000":{"week":1511654400000,"additions":3,"deletions":0,"commits":1},"1607817600000":{"week":1607817600000,"additions":10,"deletions":0,"commits":1},"1624752000000":{"week":1624752000000,"additions":2,"deletions":0,"commits":1}}}}`, dataString) + assert.JSONEq(t, `{"ethantkoenig@gmail.com":{"name":"Ethan Koenig","login":"","avatar_link":"/assets/img/avatar_default.png","home_link":"","total_commits":1,"weeks":{"1511654400000":{"week":1511654400000,"additions":3,"deletions":0,"commits":1}}},"jimmy.praet@telenet.be":{"name":"Jimmy Praet","login":"","avatar_link":"/assets/img/avatar_default.png","home_link":"","total_commits":1,"weeks":{"1624752000000":{"week":1624752000000,"additions":2,"deletions":0,"commits":1}}},"jon@allspice.io":{"name":"Jon","login":"","avatar_link":"/assets/img/avatar_default.png","home_link":"","total_commits":1,"weeks":{"1607817600000":{"week":1607817600000,"additions":10,"deletions":0,"commits":1}}},"total":{"name":"Total","login":"","avatar_link":"","home_link":"","total_commits":3,"weeks":{"1511654400000":{"week":1511654400000,"additions":3,"deletions":0,"commits":1},"1607817600000":{"week":1607817600000,"additions":10,"deletions":0,"commits":1},"1624752000000":{"week":1624752000000,"additions":2,"deletions":0,"commits":1}}}}`, dataString) var data map[string]*ContributorData require.NoError(t, json.Unmarshal([]byte(dataString), &data)) @@ -62,7 +62,7 @@ func TestRepository_ContributorsGraph(t *testing.T) { assert.EqualValues(t, &ContributorData{ Name: "Ethan Koenig", - AvatarLink: "https://secure.gravatar.com/avatar/b42fb195faa8c61b8d88abfefe30e9e3?d=identicon", + AvatarLink: "/assets/img/avatar_default.png", TotalCommits: 1, Weeks: map[int64]*WeekData{ 1511654400000: { diff --git a/tests/integration/opengraph_test.go b/tests/integration/opengraph_test.go index 8d29e4548d..69eef7f0d9 100644 --- a/tests/integration/opengraph_test.go +++ b/tests/integration/opengraph_test.go @@ -5,6 +5,7 @@ package integration import ( "net/http" + "strings" "testing" "code.gitea.io/gitea/modules/setting" @@ -42,7 +43,7 @@ func TestOpenGraphProperties(t *testing.T) { "og:title": "User Thirty", "og:url": setting.AppURL + "user30", "og:type": "profile", - "og:image": "https://secure.gravatar.com/avatar/eae1f44b34ff27284cb0792c7601c89c?d=identicon", + "og:image": "http://localhost:3003/assets/img/avatar_default.png", "og:site_name": siteName, }, }, @@ -54,7 +55,7 @@ func TestOpenGraphProperties(t *testing.T) { "og:url": setting.AppURL + "the_34-user.with.all.allowedChars", "og:description": "some [commonmark](https://commonmark.org/)!", "og:type": "profile", - "og:image": setting.AppURL + "avatars/avatar34", + "og:image": "http://localhost:3003/assets/img/avatar_default.png", "og:site_name": siteName, }, }, @@ -66,7 +67,7 @@ func TestOpenGraphProperties(t *testing.T) { "og:url": setting.AppURL + "user2/repo1/issues/1", "og:description": "content for the first issue", "og:type": "object", - "og:image": "https://secure.gravatar.com/avatar/ab53a2911ddf9b4817ac01ddcd3d975f?d=identicon", + "og:image": "http://localhost:3003/avatars/ab53a2911ddf9b4817ac01ddcd3d975f", "og:site_name": siteName, }, }, @@ -78,7 +79,7 @@ func TestOpenGraphProperties(t *testing.T) { "og:url": setting.AppURL + "user2/repo1/pulls/2", "og:description": "content for the second issue", "og:type": "object", - "og:image": "https://secure.gravatar.com/avatar/ab53a2911ddf9b4817ac01ddcd3d975f?d=identicon", + "og:image": "http://localhost:3003/avatars/ab53a2911ddf9b4817ac01ddcd3d975f", "og:site_name": siteName, }, }, @@ -89,7 +90,7 @@ func TestOpenGraphProperties(t *testing.T) { "og:title": "repo49/test/test.txt at master", "og:url": setting.AppURL + "/user27/repo49/src/branch/master/test/test.txt", "og:type": "object", - "og:image": "https://secure.gravatar.com/avatar/7095710e927665f1bdd1ced94152f232?d=identicon", + "og:image": "http://localhost:3003/assets/img/avatar_default.png", "og:site_name": siteName, }, }, @@ -100,7 +101,7 @@ func TestOpenGraphProperties(t *testing.T) { "og:title": "Page With Spaced Name", "og:url": setting.AppURL + "/user2/repo1/wiki/Page-With-Spaced-Name", "og:type": "object", - "og:image": "https://secure.gravatar.com/avatar/ab53a2911ddf9b4817ac01ddcd3d975f?d=identicon", + "og:image": "http://localhost:3003/avatars/ab53a2911ddf9b4817ac01ddcd3d975f", "og:site_name": siteName, }, }, @@ -111,7 +112,7 @@ func TestOpenGraphProperties(t *testing.T) { "og:title": "repo1", "og:url": setting.AppURL + "user2/repo1", "og:type": "object", - "og:image": "https://secure.gravatar.com/avatar/ab53a2911ddf9b4817ac01ddcd3d975f?d=identicon", + "og:image": "http://localhost:3003/avatars/ab53a2911ddf9b4817ac01ddcd3d975f", "og:site_name": siteName, }, }, @@ -123,7 +124,7 @@ func TestOpenGraphProperties(t *testing.T) { "og:url": setting.AppURL + "user27/repo49", "og:description": "A wonderful repository with more than just a README.md", "og:type": "object", - "og:image": "https://secure.gravatar.com/avatar/7095710e927665f1bdd1ced94152f232?d=identicon", + "og:image": "http://localhost:3003/assets/img/avatar_default.png", "og:site_name": siteName, }, }, @@ -141,6 +142,10 @@ func TestOpenGraphProperties(t *testing.T) { assert.True(t, foundProp) content, foundContent := selection.Attr("content") assert.True(t, foundContent, "opengraph meta tag without a content property") + if prop == "og:image" { + content = strings.ReplaceAll(content, "http://localhost:3001", "http://localhost:3003") + content = strings.ReplaceAll(content, "http://localhost:3002", "http://localhost:3003") + } foundProps[prop] = content }) From d3ebc5b161599f31df45ae551b5b2a4e76ec19b8 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Sun, 24 Nov 2024 15:43:19 +0000 Subject: [PATCH 9/9] chore(release-notes): notes for the week 2024-48 weekly cherry pick --- release-notes/6062.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 release-notes/6062.md diff --git a/release-notes/6062.md b/release-notes/6062.md new file mode 100644 index 0000000000..7ae7f87548 --- /dev/null +++ b/release-notes/6062.md @@ -0,0 +1,4 @@ +fix: [commit](https://codeberg.org/forgejo/forgejo/commit/32a91add34519ef7768ec907888ed837ad0dde2f) Fix GetInactiveUsers +fix: [commit](https://codeberg.org/forgejo/forgejo/commit/64824290912b6300ede2b2f95ff77d55dde9859b) Fix submodule parsing +fix: [commit](https://codeberg.org/forgejo/forgejo/commit/ddabba5f89c4b196daeeb2af17de9ec2cec14b63) allow the actions user to login via the jwt token +feat: [commit](https://codeberg.org/forgejo/forgejo/commit/262c48409b1224e3f6dc63c8d1e04fef0e0cf2c0) Support HTTP POST requests to `/userinfo`, aligning to OpenID Core specification