Skip to content

Commit 17b4c93

Browse files
authored
Merge branch 'main' into rb/validate-redirect-uri
2 parents e536a9d + 846f618 commit 17b4c93

Some content is hidden

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

73 files changed

+2123
-840
lines changed

models/auth/webauthn.go

+21-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"code.gitea.io/gitea/modules/timeutil"
1313
"code.gitea.io/gitea/modules/util"
1414

15+
"github.com/go-webauthn/webauthn/protocol"
1516
"github.com/go-webauthn/webauthn/webauthn"
1617
)
1718

@@ -89,14 +90,33 @@ func (cred *WebAuthnCredential) AfterLoad() {
8990
// WebAuthnCredentialList is a list of *WebAuthnCredential
9091
type WebAuthnCredentialList []*WebAuthnCredential
9192

93+
// newCredentialFlagsFromAuthenticatorFlags is copied from https://github.com/go-webauthn/webauthn/pull/337
94+
// to convert protocol.AuthenticatorFlags to webauthn.CredentialFlags
95+
func newCredentialFlagsFromAuthenticatorFlags(flags protocol.AuthenticatorFlags) webauthn.CredentialFlags {
96+
return webauthn.CredentialFlags{
97+
UserPresent: flags.HasUserPresent(),
98+
UserVerified: flags.HasUserVerified(),
99+
BackupEligible: flags.HasBackupEligible(),
100+
BackupState: flags.HasBackupState(),
101+
}
102+
}
103+
92104
// ToCredentials will convert all WebAuthnCredentials to webauthn.Credentials
93-
func (list WebAuthnCredentialList) ToCredentials() []webauthn.Credential {
105+
func (list WebAuthnCredentialList) ToCredentials(defaultAuthFlags ...protocol.AuthenticatorFlags) []webauthn.Credential {
106+
// TODO: at the moment, Gitea doesn't store or check the flags
107+
// so we need to use the default flags from the authenticator to make the login validation pass
108+
// In the future, we should:
109+
// 1. store the flags when registering the credential
110+
// 2. provide the stored flags when converting the credentials (for login)
111+
// 3. for old users, still use this fallback to the default flags
112+
defAuthFlags := util.OptionalArg(defaultAuthFlags)
94113
creds := make([]webauthn.Credential, 0, len(list))
95114
for _, cred := range list {
96115
creds = append(creds, webauthn.Credential{
97116
ID: cred.CredentialID,
98117
PublicKey: cred.PublicKey,
99118
AttestationType: cred.AttestationType,
119+
Flags: newCredentialFlagsFromAuthenticatorFlags(defAuthFlags),
100120
Authenticator: webauthn.Authenticator{
101121
AAGUID: cred.AAGUID,
102122
SignCount: cred.SignCount,

models/db/engine.go

+3
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,9 @@ func SyncAllTables() error {
134134
func InitEngine(ctx context.Context) error {
135135
xormEngine, err := newXORMEngine()
136136
if err != nil {
137+
if strings.Contains(err.Error(), "SQLite3 support") {
138+
return fmt.Errorf(`sqlite3 requires: -tags sqlite,sqlite_unlock_notify%s%w`, "\n", err)
139+
}
137140
return fmt.Errorf("failed to connect to database: %w", err)
138141
}
139142

models/git/protected_branch.go

+33-1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ type ProtectedBranch struct {
3434
RepoID int64 `xorm:"UNIQUE(s)"`
3535
Repo *repo_model.Repository `xorm:"-"`
3636
RuleName string `xorm:"'branch_name' UNIQUE(s)"` // a branch name or a glob match to branch name
37+
Priority int64 `xorm:"NOT NULL DEFAULT 0"`
3738
globRule glob.Glob `xorm:"-"`
3839
isPlainName bool `xorm:"-"`
3940
CanPush bool `xorm:"NOT NULL DEFAULT false"`
@@ -413,21 +414,52 @@ func UpdateProtectBranch(ctx context.Context, repo *repo_model.Repository, prote
413414
}
414415
protectBranch.ApprovalsWhitelistTeamIDs = whitelist
415416

416-
// Make sure protectBranch.ID is not 0 for whitelists
417+
// Looks like it's a new rule
417418
if protectBranch.ID == 0 {
419+
// as it's a new rule and if priority was not set, we need to calc it.
420+
if protectBranch.Priority == 0 {
421+
var lowestPrio int64
422+
// because of mssql we can not use builder or save xorm syntax, so raw sql it is
423+
if _, err := db.GetEngine(ctx).SQL(`SELECT MAX(priority) FROM protected_branch WHERE repo_id = ?`, protectBranch.RepoID).
424+
Get(&lowestPrio); err != nil {
425+
return err
426+
}
427+
log.Trace("Create new ProtectedBranch at repo[%d] and detect current lowest priority '%d'", protectBranch.RepoID, lowestPrio)
428+
protectBranch.Priority = lowestPrio + 1
429+
}
430+
418431
if _, err = db.GetEngine(ctx).Insert(protectBranch); err != nil {
419432
return fmt.Errorf("Insert: %v", err)
420433
}
421434
return nil
422435
}
423436

437+
// update the rule
424438
if _, err = db.GetEngine(ctx).ID(protectBranch.ID).AllCols().Update(protectBranch); err != nil {
425439
return fmt.Errorf("Update: %v", err)
426440
}
427441

428442
return nil
429443
}
430444

445+
func UpdateProtectBranchPriorities(ctx context.Context, repo *repo_model.Repository, ids []int64) error {
446+
prio := int64(1)
447+
return db.WithTx(ctx, func(ctx context.Context) error {
448+
for _, id := range ids {
449+
if _, err := db.GetEngine(ctx).
450+
ID(id).Where("repo_id = ?", repo.ID).
451+
Cols("priority").
452+
Update(&ProtectedBranch{
453+
Priority: prio,
454+
}); err != nil {
455+
return err
456+
}
457+
prio++
458+
}
459+
return nil
460+
})
461+
}
462+
431463
// updateApprovalWhitelist checks whether the user whitelist changed and returns a whitelist with
432464
// the users from newWhitelist which have explicit read or write access to the repo.
433465
func updateApprovalWhitelist(ctx context.Context, repo *repo_model.Repository, currentWhitelist, newWhitelist []int64) (whitelist []int64, err error) {

models/git/protected_branch_list.go

+7
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ func (rules ProtectedBranchRules) sort() {
2828
sort.Slice(rules, func(i, j int) bool {
2929
rules[i].loadGlob()
3030
rules[j].loadGlob()
31+
32+
// if priority differ, use that to sort
33+
if rules[i].Priority != rules[j].Priority {
34+
return rules[i].Priority < rules[j].Priority
35+
}
36+
37+
// now we sort the old way
3138
if rules[i].isPlainName != rules[j].isPlainName {
3239
return rules[i].isPlainName // plain name comes first, so plain name means "less"
3340
}

models/git/protected_branch_list_test.go

+35-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ func TestBranchRuleMatchPriority(t *testing.T) {
7575
}
7676
}
7777

78-
func TestBranchRuleSort(t *testing.T) {
78+
func TestBranchRuleSortLegacy(t *testing.T) {
7979
in := []*ProtectedBranch{{
8080
RuleName: "b",
8181
CreatedUnix: 1,
@@ -103,3 +103,37 @@ func TestBranchRuleSort(t *testing.T) {
103103
}
104104
assert.Equal(t, expect, got)
105105
}
106+
107+
func TestBranchRuleSortPriority(t *testing.T) {
108+
in := []*ProtectedBranch{{
109+
RuleName: "b",
110+
CreatedUnix: 1,
111+
Priority: 4,
112+
}, {
113+
RuleName: "b/*",
114+
CreatedUnix: 3,
115+
Priority: 2,
116+
}, {
117+
RuleName: "a/*",
118+
CreatedUnix: 2,
119+
Priority: 1,
120+
}, {
121+
RuleName: "c",
122+
CreatedUnix: 0,
123+
Priority: 0,
124+
}, {
125+
RuleName: "a",
126+
CreatedUnix: 4,
127+
Priority: 3,
128+
}}
129+
expect := []string{"c", "a/*", "b/*", "a", "b"}
130+
131+
pbr := ProtectedBranchRules(in)
132+
pbr.sort()
133+
134+
var got []string
135+
for i := range pbr {
136+
got = append(got, pbr[i].RuleName)
137+
}
138+
assert.Equal(t, expect, got)
139+
}

models/git/protected_branch_test.go

+78
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ import (
77
"fmt"
88
"testing"
99

10+
"code.gitea.io/gitea/models/db"
11+
repo_model "code.gitea.io/gitea/models/repo"
12+
"code.gitea.io/gitea/models/unittest"
13+
1014
"github.com/stretchr/testify/assert"
1115
)
1216

@@ -76,3 +80,77 @@ func TestBranchRuleMatch(t *testing.T) {
7680
)
7781
}
7882
}
83+
84+
func TestUpdateProtectBranchPriorities(t *testing.T) {
85+
assert.NoError(t, unittest.PrepareTestDatabase())
86+
87+
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
88+
89+
// Create some test protected branches with initial priorities
90+
protectedBranches := []*ProtectedBranch{
91+
{
92+
RepoID: repo.ID,
93+
RuleName: "master",
94+
Priority: 1,
95+
},
96+
{
97+
RepoID: repo.ID,
98+
RuleName: "develop",
99+
Priority: 2,
100+
},
101+
{
102+
RepoID: repo.ID,
103+
RuleName: "feature/*",
104+
Priority: 3,
105+
},
106+
}
107+
108+
for _, pb := range protectedBranches {
109+
_, err := db.GetEngine(db.DefaultContext).Insert(pb)
110+
assert.NoError(t, err)
111+
}
112+
113+
// Test updating priorities
114+
newPriorities := []int64{protectedBranches[2].ID, protectedBranches[0].ID, protectedBranches[1].ID}
115+
err := UpdateProtectBranchPriorities(db.DefaultContext, repo, newPriorities)
116+
assert.NoError(t, err)
117+
118+
// Verify new priorities
119+
pbs, err := FindRepoProtectedBranchRules(db.DefaultContext, repo.ID)
120+
assert.NoError(t, err)
121+
122+
expectedPriorities := map[string]int64{
123+
"feature/*": 1,
124+
"master": 2,
125+
"develop": 3,
126+
}
127+
128+
for _, pb := range pbs {
129+
assert.Equal(t, expectedPriorities[pb.RuleName], pb.Priority)
130+
}
131+
}
132+
133+
func TestNewProtectBranchPriority(t *testing.T) {
134+
assert.NoError(t, unittest.PrepareTestDatabase())
135+
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
136+
137+
err := UpdateProtectBranch(db.DefaultContext, repo, &ProtectedBranch{
138+
RepoID: repo.ID,
139+
RuleName: "branch-1",
140+
Priority: 1,
141+
}, WhitelistOptions{})
142+
assert.NoError(t, err)
143+
144+
newPB := &ProtectedBranch{
145+
RepoID: repo.ID,
146+
RuleName: "branch-2",
147+
// Priority intentionally omitted
148+
}
149+
150+
err = UpdateProtectBranch(db.DefaultContext, repo, newPB, WhitelistOptions{})
151+
assert.NoError(t, err)
152+
153+
savedPB2, err := GetFirstMatchProtectedBranchRule(db.DefaultContext, repo.ID, "branch-2")
154+
assert.NoError(t, err)
155+
assert.Equal(t, int64(2), savedPB2.Priority)
156+
}

models/migrations/base/tests.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import (
1818
"code.gitea.io/gitea/modules/setting"
1919
"code.gitea.io/gitea/modules/testlogger"
2020

21-
"github.com/stretchr/testify/assert"
21+
"github.com/stretchr/testify/require"
2222
"xorm.io/xorm"
2323
)
2424

@@ -33,15 +33,15 @@ func PrepareTestEnv(t *testing.T, skip int, syncModels ...any) (*xorm.Engine, fu
3333
ourSkip := 2
3434
ourSkip += skip
3535
deferFn := testlogger.PrintCurrentTest(t, ourSkip)
36-
assert.NoError(t, unittest.SyncDirs(filepath.Join(filepath.Dir(setting.AppPath), "tests/gitea-repositories-meta"), setting.RepoRootPath))
36+
require.NoError(t, unittest.SyncDirs(filepath.Join(filepath.Dir(setting.AppPath), "tests/gitea-repositories-meta"), setting.RepoRootPath))
3737

3838
if err := deleteDB(); err != nil {
39-
t.Errorf("unable to reset database: %v", err)
39+
t.Fatalf("unable to reset database: %v", err)
4040
return nil, deferFn
4141
}
4242

4343
x, err := newXORMEngine()
44-
assert.NoError(t, err)
44+
require.NoError(t, err)
4545
if x != nil {
4646
oldDefer := deferFn
4747
deferFn = func() {

models/migrations/migrations.go

+1
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,7 @@ func prepareMigrationTasks() []*migration {
367367
newMigration(307, "Fix milestone deadline_unix when there is no due date", v1_23.FixMilestoneNoDueDate),
368368
newMigration(308, "Add index(user_id, is_deleted) for action table", v1_23.AddNewIndexForUserDashboard),
369369
newMigration(309, "Improve Notification table indices", v1_23.ImproveNotificationTableIndices),
370+
newMigration(310, "Add Priority to ProtectedBranch", v1_23.AddPriorityToProtectedBranch),
370371
}
371372
return preparedMigrations
372373
}

models/migrations/v1_23/v310.go

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2024 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package v1_23 //nolint
5+
6+
import (
7+
"xorm.io/xorm"
8+
)
9+
10+
func AddPriorityToProtectedBranch(x *xorm.Engine) error {
11+
type ProtectedBranch struct {
12+
Priority int64 `xorm:"NOT NULL DEFAULT 0"`
13+
}
14+
15+
return x.Sync(new(ProtectedBranch))
16+
}

models/organization/org_list.go

+25
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,31 @@ import (
1616
"xorm.io/builder"
1717
)
1818

19+
type OrgList []*Organization
20+
21+
func (orgs OrgList) LoadTeams(ctx context.Context) (map[int64]TeamList, error) {
22+
if len(orgs) == 0 {
23+
return map[int64]TeamList{}, nil
24+
}
25+
26+
orgIDs := make([]int64, len(orgs))
27+
for i, org := range orgs {
28+
orgIDs[i] = org.ID
29+
}
30+
31+
teams, err := GetTeamsByOrgIDs(ctx, orgIDs)
32+
if err != nil {
33+
return nil, err
34+
}
35+
36+
teamMap := make(map[int64]TeamList, len(orgs))
37+
for _, team := range teams {
38+
teamMap[team.OrgID] = append(teamMap[team.OrgID], team)
39+
}
40+
41+
return teamMap, nil
42+
}
43+
1944
// SearchOrganizationsOptions options to filter organizations
2045
type SearchOrganizationsOptions struct {
2146
db.ListOptions

models/organization/org_list_test.go

+11
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,14 @@ func TestGetUserOrgsList(t *testing.T) {
6060
assert.EqualValues(t, 2, orgs[0].NumRepos)
6161
}
6262
}
63+
64+
func TestLoadOrgListTeams(t *testing.T) {
65+
assert.NoError(t, unittest.PrepareTestDatabase())
66+
orgs, err := organization.GetUserOrgsList(db.DefaultContext, &user_model.User{ID: 4})
67+
assert.NoError(t, err)
68+
assert.Len(t, orgs, 1)
69+
teamsMap, err := organization.OrgList(orgs).LoadTeams(db.DefaultContext)
70+
assert.NoError(t, err)
71+
assert.Len(t, teamsMap, 1)
72+
assert.Len(t, teamsMap[3], 5)
73+
}

models/organization/team_list.go

+5
Original file line numberDiff line numberDiff line change
@@ -126,3 +126,8 @@ func GetUserRepoTeams(ctx context.Context, orgID, userID, repoID int64) (teams T
126126
And("team_repo.repo_id=?", repoID).
127127
Find(&teams)
128128
}
129+
130+
func GetTeamsByOrgIDs(ctx context.Context, orgIDs []int64) (TeamList, error) {
131+
teams := make([]*Team, 0, 10)
132+
return teams, db.GetEngine(ctx).Where(builder.In("org_id", orgIDs)).Find(&teams)
133+
}

0 commit comments

Comments
 (0)