1
0
Fork 0
mirror of https://codeberg.org/forgejo/forgejo.git synced 2024-12-12 11:38:00 -05:00

Merge pull request '[gitea] week 2024-48 cherry pick (gitea/main -> forgejo)' (#6062) from earl-warren/wcp/2024-48 into forgejo

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6062
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
This commit is contained in:
Earl Warren 2024-11-26 16:42:38 +00:00
commit e5417fdede
18 changed files with 287 additions and 146 deletions

View file

@ -29,7 +29,6 @@
poetry poetry
# backend # backend
go_1_22
gofumpt gofumpt
sqlite sqlite
]; ];

View file

@ -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 id: 47
job_id: 192 job_id: 192

View file

@ -1,7 +1,7 @@
- -
id: 1 id: 1
setting_key: 'picture.disable_gravatar' setting_key: 'picture.disable_gravatar'
setting_value: 'false' setting_value: 'true'
version: 1 version: 1
created: 1653533198 created: 1653533198
updated: 1653533198 updated: 1653533198

View file

@ -23,9 +23,9 @@
allow_import_local: false allow_import_local: false
allow_create_organization: true allow_create_organization: true
prohibit_login: false prohibit_login: false
avatar: avatar1 avatar: ""
avatar_email: user1@example.com avatar_email: user1@example.com
use_custom_avatar: false use_custom_avatar: true
num_followers: 0 num_followers: 0
num_following: 0 num_following: 0
num_stars: 0 num_stars: 0
@ -60,8 +60,9 @@
allow_import_local: false allow_import_local: false
allow_create_organization: true allow_create_organization: true
prohibit_login: false prohibit_login: false
avatar: avatar2 avatar: ""
avatar_email: user2@example.com avatar_email: user2@example.com
# cause a random avatar to be generated when referenced for test purposes
use_custom_avatar: false use_custom_avatar: false
num_followers: 2 num_followers: 2
num_following: 1 num_following: 1
@ -97,9 +98,9 @@
allow_import_local: false allow_import_local: false
allow_create_organization: true allow_create_organization: true
prohibit_login: false prohibit_login: false
avatar: avatar3 avatar: ""
avatar_email: org3@example.com avatar_email: org3@example.com
use_custom_avatar: false use_custom_avatar: true
num_followers: 0 num_followers: 0
num_following: 0 num_following: 0
num_stars: 0 num_stars: 0
@ -134,9 +135,9 @@
allow_import_local: false allow_import_local: false
allow_create_organization: true allow_create_organization: true
prohibit_login: false prohibit_login: false
avatar: avatar4 avatar: ""
avatar_email: user4@example.com avatar_email: user4@example.com
use_custom_avatar: false use_custom_avatar: true
num_followers: 0 num_followers: 0
num_following: 1 num_following: 1
num_stars: 0 num_stars: 0
@ -171,9 +172,9 @@
allow_import_local: false allow_import_local: false
allow_create_organization: false allow_create_organization: false
prohibit_login: false prohibit_login: false
avatar: avatar5 avatar: ""
avatar_email: user5@example.com avatar_email: user5@example.com
use_custom_avatar: false use_custom_avatar: true
num_followers: 0 num_followers: 0
num_following: 0 num_following: 0
num_stars: 0 num_stars: 0
@ -208,9 +209,9 @@
allow_import_local: false allow_import_local: false
allow_create_organization: true allow_create_organization: true
prohibit_login: false prohibit_login: false
avatar: avatar6 avatar: ""
avatar_email: org6@example.com avatar_email: org6@example.com
use_custom_avatar: false use_custom_avatar: true
num_followers: 0 num_followers: 0
num_following: 0 num_following: 0
num_stars: 0 num_stars: 0
@ -245,9 +246,9 @@
allow_import_local: false allow_import_local: false
allow_create_organization: true allow_create_organization: true
prohibit_login: false prohibit_login: false
avatar: avatar7 avatar: ""
avatar_email: org7@example.com avatar_email: org7@example.com
use_custom_avatar: false use_custom_avatar: true
num_followers: 0 num_followers: 0
num_following: 0 num_following: 0
num_stars: 0 num_stars: 0
@ -282,9 +283,9 @@
allow_import_local: false allow_import_local: false
allow_create_organization: true allow_create_organization: true
prohibit_login: false prohibit_login: false
avatar: avatar8 avatar: ""
avatar_email: user8@example.com avatar_email: user8@example.com
use_custom_avatar: false use_custom_avatar: true
num_followers: 1 num_followers: 1
num_following: 1 num_following: 1
num_stars: 0 num_stars: 0
@ -319,9 +320,9 @@
allow_import_local: false allow_import_local: false
allow_create_organization: true allow_create_organization: true
prohibit_login: false prohibit_login: false
avatar: avatar9 avatar: ""
avatar_email: user9@example.com avatar_email: user9@example.com
use_custom_avatar: false use_custom_avatar: true
num_followers: 0 num_followers: 0
num_following: 0 num_following: 0
num_stars: 0 num_stars: 0
@ -332,6 +333,7 @@
repo_admin_change_team_access: false repo_admin_change_team_access: false
theme: "" theme: ""
keep_activity_private: false keep_activity_private: false
created_unix: 1730468968
- -
id: 10 id: 10
@ -356,9 +358,9 @@
allow_import_local: false allow_import_local: false
allow_create_organization: true allow_create_organization: true
prohibit_login: false prohibit_login: false
avatar: avatar10 avatar: ""
avatar_email: user10@example.com avatar_email: user10@example.com
use_custom_avatar: false use_custom_avatar: true
num_followers: 0 num_followers: 0
num_following: 0 num_following: 0
num_stars: 0 num_stars: 0
@ -393,9 +395,9 @@
allow_import_local: false allow_import_local: false
allow_create_organization: true allow_create_organization: true
prohibit_login: false prohibit_login: false
avatar: avatar11 avatar: ""
avatar_email: user11@example.com avatar_email: user11@example.com
use_custom_avatar: false use_custom_avatar: true
num_followers: 0 num_followers: 0
num_following: 0 num_following: 0
num_stars: 0 num_stars: 0
@ -430,9 +432,9 @@
allow_import_local: false allow_import_local: false
allow_create_organization: true allow_create_organization: true
prohibit_login: false prohibit_login: false
avatar: avatar12 avatar: ""
avatar_email: user12@example.com avatar_email: user12@example.com
use_custom_avatar: false use_custom_avatar: true
num_followers: 0 num_followers: 0
num_following: 0 num_following: 0
num_stars: 0 num_stars: 0
@ -467,9 +469,9 @@
allow_import_local: false allow_import_local: false
allow_create_organization: true allow_create_organization: true
prohibit_login: false prohibit_login: false
avatar: avatar13 avatar: ""
avatar_email: user13@example.com avatar_email: user13@example.com
use_custom_avatar: false use_custom_avatar: true
num_followers: 0 num_followers: 0
num_following: 0 num_following: 0
num_stars: 0 num_stars: 0
@ -504,9 +506,9 @@
allow_import_local: false allow_import_local: false
allow_create_organization: true allow_create_organization: true
prohibit_login: false prohibit_login: false
avatar: avatar14 avatar: ""
avatar_email: user13@example.com avatar_email: user13@example.com
use_custom_avatar: false use_custom_avatar: true
num_followers: 0 num_followers: 0
num_following: 0 num_following: 0
num_stars: 0 num_stars: 0
@ -541,9 +543,9 @@
allow_import_local: false allow_import_local: false
allow_create_organization: true allow_create_organization: true
prohibit_login: false prohibit_login: false
avatar: avatar15 avatar: ""
avatar_email: user15@example.com avatar_email: user15@example.com
use_custom_avatar: false use_custom_avatar: true
num_followers: 0 num_followers: 0
num_following: 0 num_following: 0
num_stars: 0 num_stars: 0
@ -578,9 +580,9 @@
allow_import_local: false allow_import_local: false
allow_create_organization: true allow_create_organization: true
prohibit_login: false prohibit_login: false
avatar: avatar16 avatar: ""
avatar_email: user16@example.com avatar_email: user16@example.com
use_custom_avatar: false use_custom_avatar: true
num_followers: 0 num_followers: 0
num_following: 0 num_following: 0
num_stars: 0 num_stars: 0
@ -615,9 +617,9 @@
allow_import_local: false allow_import_local: false
allow_create_organization: true allow_create_organization: true
prohibit_login: false prohibit_login: false
avatar: avatar17 avatar: ""
avatar_email: org17@example.com avatar_email: org17@example.com
use_custom_avatar: false use_custom_avatar: true
num_followers: 0 num_followers: 0
num_following: 0 num_following: 0
num_stars: 0 num_stars: 0
@ -652,9 +654,9 @@
allow_import_local: false allow_import_local: false
allow_create_organization: true allow_create_organization: true
prohibit_login: false prohibit_login: false
avatar: avatar18 avatar: ""
avatar_email: user18@example.com avatar_email: user18@example.com
use_custom_avatar: false use_custom_avatar: true
num_followers: 0 num_followers: 0
num_following: 0 num_following: 0
num_stars: 0 num_stars: 0
@ -689,9 +691,9 @@
allow_import_local: false allow_import_local: false
allow_create_organization: true allow_create_organization: true
prohibit_login: false prohibit_login: false
avatar: avatar19 avatar: ""
avatar_email: org19@example.com avatar_email: org19@example.com
use_custom_avatar: false use_custom_avatar: true
num_followers: 0 num_followers: 0
num_following: 0 num_following: 0
num_stars: 0 num_stars: 0
@ -726,9 +728,9 @@
allow_import_local: false allow_import_local: false
allow_create_organization: true allow_create_organization: true
prohibit_login: false prohibit_login: false
avatar: avatar20 avatar: ""
avatar_email: user20@example.com avatar_email: user20@example.com
use_custom_avatar: false use_custom_avatar: true
num_followers: 0 num_followers: 0
num_following: 0 num_following: 0
num_stars: 0 num_stars: 0
@ -763,9 +765,9 @@
allow_import_local: false allow_import_local: false
allow_create_organization: true allow_create_organization: true
prohibit_login: false prohibit_login: false
avatar: avatar21 avatar: ""
avatar_email: user21@example.com avatar_email: user21@example.com
use_custom_avatar: false use_custom_avatar: true
num_followers: 0 num_followers: 0
num_following: 0 num_following: 0
num_stars: 0 num_stars: 0
@ -800,9 +802,9 @@
allow_import_local: false allow_import_local: false
allow_create_organization: true allow_create_organization: true
prohibit_login: false prohibit_login: false
avatar: avatar22 avatar: ""
avatar_email: limited_org@example.com avatar_email: limited_org@example.com
use_custom_avatar: false use_custom_avatar: true
num_followers: 0 num_followers: 0
num_following: 0 num_following: 0
num_stars: 0 num_stars: 0
@ -837,9 +839,9 @@
allow_import_local: false allow_import_local: false
allow_create_organization: true allow_create_organization: true
prohibit_login: false prohibit_login: false
avatar: avatar23 avatar: ""
avatar_email: privated_org@example.com avatar_email: privated_org@example.com
use_custom_avatar: false use_custom_avatar: true
num_followers: 0 num_followers: 0
num_following: 0 num_following: 0
num_stars: 0 num_stars: 0
@ -874,9 +876,9 @@
allow_import_local: false allow_import_local: false
allow_create_organization: true allow_create_organization: true
prohibit_login: false prohibit_login: false
avatar: avatar24 avatar: ""
avatar_email: user24@example.com avatar_email: user24@example.com
use_custom_avatar: false use_custom_avatar: true
num_followers: 0 num_followers: 0
num_following: 0 num_following: 0
num_stars: 0 num_stars: 0
@ -911,9 +913,9 @@
allow_import_local: false allow_import_local: false
allow_create_organization: true allow_create_organization: true
prohibit_login: false prohibit_login: false
avatar: avatar25 avatar: ""
avatar_email: org25@example.com avatar_email: org25@example.com
use_custom_avatar: false use_custom_avatar: true
num_followers: 0 num_followers: 0
num_following: 0 num_following: 0
num_stars: 0 num_stars: 0
@ -948,9 +950,9 @@
allow_import_local: false allow_import_local: false
allow_create_organization: true allow_create_organization: true
prohibit_login: false prohibit_login: false
avatar: avatar26 avatar: ""
avatar_email: org26@example.com avatar_email: org26@example.com
use_custom_avatar: false use_custom_avatar: true
num_followers: 0 num_followers: 0
num_following: 0 num_following: 0
num_stars: 0 num_stars: 0
@ -985,9 +987,9 @@
allow_import_local: false allow_import_local: false
allow_create_organization: true allow_create_organization: true
prohibit_login: false prohibit_login: false
avatar: avatar27 avatar: ""
avatar_email: user27@example.com avatar_email: user27@example.com
use_custom_avatar: false use_custom_avatar: true
num_followers: 0 num_followers: 0
num_following: 0 num_following: 0
num_stars: 0 num_stars: 0
@ -1022,9 +1024,9 @@
allow_import_local: false allow_import_local: false
allow_create_organization: true allow_create_organization: true
prohibit_login: false prohibit_login: false
avatar: avatar28 avatar: ""
avatar_email: user28@example.com avatar_email: user28@example.com
use_custom_avatar: false use_custom_avatar: true
num_followers: 0 num_followers: 0
num_following: 0 num_following: 0
num_stars: 0 num_stars: 0
@ -1059,9 +1061,9 @@
allow_import_local: false allow_import_local: false
allow_create_organization: true allow_create_organization: true
prohibit_login: false prohibit_login: false
avatar: avatar29 avatar: ""
avatar_email: user29@example.com avatar_email: user29@example.com
use_custom_avatar: false use_custom_avatar: true
num_followers: 0 num_followers: 0
num_following: 0 num_following: 0
num_stars: 0 num_stars: 0
@ -1096,9 +1098,9 @@
allow_import_local: false allow_import_local: false
allow_create_organization: true allow_create_organization: true
prohibit_login: false prohibit_login: false
avatar: avatar29 avatar: ""
avatar_email: user30@example.com avatar_email: user30@example.com
use_custom_avatar: false use_custom_avatar: true
num_followers: 0 num_followers: 0
num_following: 0 num_following: 0
num_stars: 0 num_stars: 0
@ -1133,9 +1135,9 @@
allow_import_local: false allow_import_local: false
allow_create_organization: true allow_create_organization: true
prohibit_login: false prohibit_login: false
avatar: avatar31 avatar: ""
avatar_email: user31@example.com avatar_email: user31@example.com
use_custom_avatar: false use_custom_avatar: true
num_followers: 0 num_followers: 0
num_following: 1 num_following: 1
num_stars: 0 num_stars: 0
@ -1170,9 +1172,9 @@
allow_import_local: false allow_import_local: false
allow_create_organization: true allow_create_organization: true
prohibit_login: false prohibit_login: false
avatar: avatar32 avatar: ""
avatar_email: user30@example.com avatar_email: user30@example.com
use_custom_avatar: false use_custom_avatar: true
num_followers: 0 num_followers: 0
num_following: 0 num_following: 0
num_stars: 0 num_stars: 0
@ -1207,9 +1209,9 @@
allow_import_local: false allow_import_local: false
allow_create_organization: true allow_create_organization: true
prohibit_login: false prohibit_login: false
avatar: avatar33 avatar: ""
avatar_email: user33@example.com avatar_email: user33@example.com
use_custom_avatar: false use_custom_avatar: true
num_followers: 1 num_followers: 1
num_following: 0 num_following: 0
num_stars: 0 num_stars: 0
@ -1245,7 +1247,7 @@
allow_import_local: false allow_import_local: false
allow_create_organization: false allow_create_organization: false
prohibit_login: false prohibit_login: false
avatar: avatar34 avatar: ""
avatar_email: user34@example.com avatar_email: user34@example.com
use_custom_avatar: true use_custom_avatar: true
num_followers: 0 num_followers: 0
@ -1282,9 +1284,9 @@
allow_import_local: false allow_import_local: false
allow_create_organization: true allow_create_organization: true
prohibit_login: false prohibit_login: false
avatar: avatar35 avatar: ""
avatar_email: private_org35@example.com avatar_email: private_org35@example.com
use_custom_avatar: false use_custom_avatar: true
num_followers: 0 num_followers: 0
num_following: 0 num_following: 0
num_stars: 0 num_stars: 0
@ -1319,9 +1321,9 @@
allow_import_local: false allow_import_local: false
allow_create_organization: true allow_create_organization: true
prohibit_login: false prohibit_login: false
avatar: avatar22 avatar: ""
avatar_email: abcde@gitea.com avatar_email: abcde@gitea.com
use_custom_avatar: false use_custom_avatar: true
num_followers: 0 num_followers: 0
num_following: 0 num_following: 0
num_stars: 0 num_stars: 0
@ -1356,9 +1358,9 @@
allow_import_local: false allow_import_local: false
allow_create_organization: true allow_create_organization: true
prohibit_login: true prohibit_login: true
avatar: avatar29 avatar: ""
avatar_email: user37@example.com avatar_email: user37@example.com
use_custom_avatar: false use_custom_avatar: true
num_followers: 0 num_followers: 0
num_following: 0 num_following: 0
num_stars: 0 num_stars: 0
@ -1393,9 +1395,9 @@
allow_import_local: false allow_import_local: false
allow_create_organization: true allow_create_organization: true
prohibit_login: false prohibit_login: false
avatar: avatar38 avatar: ""
avatar_email: user38@example.com avatar_email: user38@example.com
use_custom_avatar: false use_custom_avatar: true
num_followers: 0 num_followers: 0
num_following: 0 num_following: 0
num_stars: 0 num_stars: 0
@ -1430,9 +1432,9 @@
allow_import_local: false allow_import_local: false
allow_create_organization: true allow_create_organization: true
prohibit_login: false prohibit_login: false
avatar: avatar39 avatar: ""
avatar_email: user39@example.com avatar_email: user39@example.com
use_custom_avatar: false use_custom_avatar: true
num_followers: 0 num_followers: 0
num_following: 0 num_following: 0
num_stars: 0 num_stars: 0
@ -1467,9 +1469,9 @@
allow_import_local: false allow_import_local: false
allow_create_organization: true allow_create_organization: true
prohibit_login: false prohibit_login: false
avatar: avatar40 avatar: ""
avatar_email: user40@example.com avatar_email: user40@example.com
use_custom_avatar: false use_custom_avatar: true
num_followers: 0 num_followers: 0
num_following: 0 num_following: 0
num_stars: 0 num_stars: 0
@ -1504,9 +1506,9 @@
allow_import_local: false allow_import_local: false
allow_create_organization: true allow_create_organization: true
prohibit_login: false prohibit_login: false
avatar: avatar41 avatar: ""
avatar_email: org41@example.com avatar_email: org41@example.com
use_custom_avatar: false use_custom_avatar: true
num_followers: 0 num_followers: 0
num_following: 0 num_following: 0
num_stars: 0 num_stars: 0

View file

@ -50,19 +50,19 @@ const (
UserTypeIndividual UserType = iota // Historic reason to make it starts at 0. UserTypeIndividual UserType = iota // Historic reason to make it starts at 0.
// UserTypeOrganization defines an organization // 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 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 reserves a (non-existing) organization, to be used in combination with UserTypeUserReserved
UserTypeOrganizationReserved UserTypeOrganizationReserved // 3
// UserTypeBot defines a bot user // UserTypeBot defines a bot user
UserTypeBot UserTypeBot // 4
// UserTypeRemoteUser defines a remote user for federated users // UserTypeRemoteUser defines a remote user for federated users
UserTypeRemoteUser UserTypeRemoteUser // 5
) )
const ( const (
@ -919,7 +919,13 @@ func UpdateUserCols(ctx context.Context, u *User, cols ...string) error {
// GetInactiveUsers gets all inactive users // GetInactiveUsers gets all inactive users
func GetInactiveUsers(ctx context.Context, olderThan time.Duration) ([]*User, error) { 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 { if olderThan > 0 {
cond = cond.And(builder.Lt{"created_unix": time.Now().Add(-olderThan).Unix()}) cond = cond.And(builder.Lt{"created_unix": time.Now().Add(-olderThan).Unix()})

View file

@ -766,3 +766,17 @@ func TestVerifyUserAuthorizationToken(t *testing.T) {
assert.Nil(t, authToken) 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)
}

View file

@ -17,6 +17,8 @@ import (
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"github.com/go-git/go-git/v5/config"
) )
// Commit represents a git commit. // Commit represents a git commit.
@ -365,37 +367,32 @@ func (c *Commit) GetSubModules() (*ObjectCache, error) {
return nil, err return nil, err
} }
rd, err := entry.Blob().DataAsync() content, err := entry.Blob().GetBlobContent(10 * 1024)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer rd.Close() c.submoduleCache, err = parseSubmoduleContent([]byte(content))
scanner := bufio.NewScanner(rd) if err != nil {
c.submoduleCache = newObjectCache() return nil, err
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
}
}
} }
if err = scanner.Err(); err != nil { return c.submoduleCache, nil
return nil, fmt.Errorf("GetSubModules scan: %w", err) }
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 // GetSubModule get the sub module according entryname

View file

@ -369,3 +369,33 @@ func TestParseCommitRenames(t *testing.T) {
assert.Equal(t, testcase.renames, renames) 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)
}
}

View file

@ -4,8 +4,6 @@
package repository package repository
import ( import (
"crypto/md5"
"fmt"
"strconv" "strconv"
"testing" "testing"
"time" "time"
@ -126,15 +124,12 @@ func TestPushCommits_AvatarLink(t *testing.T) {
}, },
} }
setting.GravatarSource = "https://secure.gravatar.com/avatar"
setting.OfflineMode = true
assert.Equal(t, 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")) pushCommits.AvatarLink(db.DefaultContext, "user2@example.com"))
assert.Equal(t, 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")) pushCommits.AvatarLink(db.DefaultContext, "nonexistent@example.com"))
} }

4
release-notes/6062.md Normal file
View file

@ -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

View file

@ -133,11 +133,6 @@ func DeleteBranch(ctx *context.APIContext) {
branchName := ctx.Params("*") 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 // check whether branches of this repository has been synced
totalNumOfBranches, err := db.Count[git_model.Branch](ctx, git_model.FindBranchOptions{ totalNumOfBranches, err := db.Count[git_model.Branch](ctx, git_model.FindBranchOptions{
RepoID: ctx.Repo.Repository.ID, RepoID: ctx.Repo.Repository.ID,

View file

@ -530,7 +530,7 @@ func registerRoutes(m *web.Route) {
m.Post("/authorize", web.Bind(forms.AuthorizationForm{}), auth.AuthorizeOAuth) m.Post("/authorize", web.Bind(forms.AuthorizationForm{}), auth.AuthorizeOAuth)
}, ignSignInAndCsrf, reqSignIn) }, 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("POST, OPTIONS", "/access_token", optionsCorsHandler(), web.Bind(forms.AccessTokenForm{}), ignSignInAndCsrf, auth.AccessTokenOAuth)
m.Methods("GET, OPTIONS", "/keys", optionsCorsHandler(), ignSignInAndCsrf, auth.OIDCKeys) m.Methods("GET, OPTIONS", "/keys", optionsCorsHandler(), ignSignInAndCsrf, auth.OIDCKeys)
m.Methods("POST, OPTIONS", "/introspect", optionsCorsHandler(), web.Bind(forms.IntrospectTokenForm{}), ignSignInAndCsrf, auth.IntrospectOAuth) m.Methods("POST, OPTIONS", "/introspect", optionsCorsHandler(), web.Bind(forms.IntrospectTokenForm{}), ignSignInAndCsrf, auth.IntrospectOAuth)

View file

@ -83,7 +83,12 @@ func ParseAuthorizationToken(req *http.Request) (int64, error) {
return 0, fmt.Errorf("split token failed") 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 { if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"]) 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 return 0, err
} }
c, ok := token.Claims.(*actionsClaims) c, ok := parsedToken.Claims.(*actionsClaims)
if !token.Valid || !ok { if !parsedToken.Valid || !ok {
return 0, fmt.Errorf("invalid token claim") return 0, fmt.Errorf("invalid token claim")
} }

View file

@ -18,6 +18,7 @@ import (
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/web/middleware" "code.gitea.io/gitea/modules/web/middleware"
"code.gitea.io/gitea/services/actions"
"code.gitea.io/gitea/services/auth/source/oauth2" "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 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 // OAuth2 implements the Auth interface and authenticates requests
// (API requests only) by looking for an OAuth token in query parameters or the // (API requests only) by looking for an OAuth token in query parameters or the
// "Authorization" header. // "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 { func (o *OAuth2) userIDFromToken(ctx context.Context, tokenSHA string, store DataStore) int64 {
// Let's see if token is valid. // Let's see if token is valid.
if strings.Contains(tokenSHA, ".") { 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 { if uid != 0 {
store.GetData()["IsApiToken"] = true store.GetData()["IsApiToken"] = true
if grantScopes != "" { if grantScopes != "" {

View file

@ -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)
})
}
}

View file

@ -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.Repo.Repository = repo
ctx.Data["PushMirrors"] = pushMirrors
ctx.Data["RepoName"] = ctx.Repo.Repository.Name ctx.Data["RepoName"] = ctx.Repo.Repository.Name
ctx.Data["IsEmptyRepo"] = ctx.Repo.Repository.IsEmpty ctx.Data["IsEmptyRepo"] = ctx.Repo.Repository.IsEmpty
ctx.Data["DefaultWikiBranchName"] = setting.Repository.DefaultBranch ctx.Data["DefaultWikiBranchName"] = setting.Repository.DefaultBranch

View file

@ -43,7 +43,7 @@ func TestRepository_ContributorsGraph(t *testing.T) {
dataString, isData := mockCache.Get("key2").(string) dataString, isData := mockCache.Get("key2").(string)
assert.True(t, isData) assert.True(t, isData)
// Verify that JSON is actually stored in the cache. // 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 var data map[string]*ContributorData
require.NoError(t, json.Unmarshal([]byte(dataString), &data)) require.NoError(t, json.Unmarshal([]byte(dataString), &data))
@ -62,7 +62,7 @@ func TestRepository_ContributorsGraph(t *testing.T) {
assert.EqualValues(t, &ContributorData{ assert.EqualValues(t, &ContributorData{
Name: "Ethan Koenig", Name: "Ethan Koenig",
AvatarLink: "https://secure.gravatar.com/avatar/b42fb195faa8c61b8d88abfefe30e9e3?d=identicon", AvatarLink: "/assets/img/avatar_default.png",
TotalCommits: 1, TotalCommits: 1,
Weeks: map[int64]*WeekData{ Weeks: map[int64]*WeekData{
1511654400000: { 1511654400000: {

View file

@ -5,6 +5,7 @@ package integration
import ( import (
"net/http" "net/http"
"strings"
"testing" "testing"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
@ -42,7 +43,7 @@ func TestOpenGraphProperties(t *testing.T) {
"og:title": "User Thirty", "og:title": "User Thirty",
"og:url": setting.AppURL + "user30", "og:url": setting.AppURL + "user30",
"og:type": "profile", "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, "og:site_name": siteName,
}, },
}, },
@ -54,7 +55,7 @@ func TestOpenGraphProperties(t *testing.T) {
"og:url": setting.AppURL + "the_34-user.with.all.allowedChars", "og:url": setting.AppURL + "the_34-user.with.all.allowedChars",
"og:description": "some [commonmark](https://commonmark.org/)!", "og:description": "some [commonmark](https://commonmark.org/)!",
"og:type": "profile", "og:type": "profile",
"og:image": setting.AppURL + "avatars/avatar34", "og:image": "http://localhost:3003/assets/img/avatar_default.png",
"og:site_name": siteName, "og:site_name": siteName,
}, },
}, },
@ -66,7 +67,7 @@ func TestOpenGraphProperties(t *testing.T) {
"og:url": setting.AppURL + "user2/repo1/issues/1", "og:url": setting.AppURL + "user2/repo1/issues/1",
"og:description": "content for the first issue", "og:description": "content for the first issue",
"og:type": "object", "og:type": "object",
"og:image": "https://secure.gravatar.com/avatar/ab53a2911ddf9b4817ac01ddcd3d975f?d=identicon", "og:image": "http://localhost:3003/avatars/ab53a2911ddf9b4817ac01ddcd3d975f",
"og:site_name": siteName, "og:site_name": siteName,
}, },
}, },
@ -78,7 +79,7 @@ func TestOpenGraphProperties(t *testing.T) {
"og:url": setting.AppURL + "user2/repo1/pulls/2", "og:url": setting.AppURL + "user2/repo1/pulls/2",
"og:description": "content for the second issue", "og:description": "content for the second issue",
"og:type": "object", "og:type": "object",
"og:image": "https://secure.gravatar.com/avatar/ab53a2911ddf9b4817ac01ddcd3d975f?d=identicon", "og:image": "http://localhost:3003/avatars/ab53a2911ddf9b4817ac01ddcd3d975f",
"og:site_name": siteName, "og:site_name": siteName,
}, },
}, },
@ -89,7 +90,7 @@ func TestOpenGraphProperties(t *testing.T) {
"og:title": "repo49/test/test.txt at master", "og:title": "repo49/test/test.txt at master",
"og:url": setting.AppURL + "/user27/repo49/src/branch/master/test/test.txt", "og:url": setting.AppURL + "/user27/repo49/src/branch/master/test/test.txt",
"og:type": "object", "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, "og:site_name": siteName,
}, },
}, },
@ -100,7 +101,7 @@ func TestOpenGraphProperties(t *testing.T) {
"og:title": "Page With Spaced Name", "og:title": "Page With Spaced Name",
"og:url": setting.AppURL + "/user2/repo1/wiki/Page-With-Spaced-Name", "og:url": setting.AppURL + "/user2/repo1/wiki/Page-With-Spaced-Name",
"og:type": "object", "og:type": "object",
"og:image": "https://secure.gravatar.com/avatar/ab53a2911ddf9b4817ac01ddcd3d975f?d=identicon", "og:image": "http://localhost:3003/avatars/ab53a2911ddf9b4817ac01ddcd3d975f",
"og:site_name": siteName, "og:site_name": siteName,
}, },
}, },
@ -111,7 +112,7 @@ func TestOpenGraphProperties(t *testing.T) {
"og:title": "repo1", "og:title": "repo1",
"og:url": setting.AppURL + "user2/repo1", "og:url": setting.AppURL + "user2/repo1",
"og:type": "object", "og:type": "object",
"og:image": "https://secure.gravatar.com/avatar/ab53a2911ddf9b4817ac01ddcd3d975f?d=identicon", "og:image": "http://localhost:3003/avatars/ab53a2911ddf9b4817ac01ddcd3d975f",
"og:site_name": siteName, "og:site_name": siteName,
}, },
}, },
@ -123,7 +124,7 @@ func TestOpenGraphProperties(t *testing.T) {
"og:url": setting.AppURL + "user27/repo49", "og:url": setting.AppURL + "user27/repo49",
"og:description": "A wonderful repository with more than just a README.md", "og:description": "A wonderful repository with more than just a README.md",
"og:type": "object", "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, "og:site_name": siteName,
}, },
}, },
@ -141,6 +142,10 @@ func TestOpenGraphProperties(t *testing.T) {
assert.True(t, foundProp) assert.True(t, foundProp)
content, foundContent := selection.Attr("content") content, foundContent := selection.Attr("content")
assert.True(t, foundContent, "opengraph meta tag without a content property") 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 foundProps[prop] = content
}) })