aboutsummaryrefslogtreecommitdiffstats
path: root/models/auth
diff options
context:
space:
mode:
Diffstat (limited to 'models/auth')
-rw-r--r--models/auth/access_token_scope.go25
-rw-r--r--models/auth/access_token_scope_test.go31
-rw-r--r--models/auth/auth_token.go2
-rw-r--r--models/auth/oauth2.go8
-rw-r--r--models/auth/oauth2_test.go4
-rw-r--r--models/auth/source.go43
-rw-r--r--models/auth/source_test.go2
-rw-r--r--models/auth/twofactor.go10
8 files changed, 74 insertions, 51 deletions
diff --git a/models/auth/access_token_scope.go b/models/auth/access_token_scope.go
index 897ff3fc9e..3eae19b2a5 100644
--- a/models/auth/access_token_scope.go
+++ b/models/auth/access_token_scope.go
@@ -5,6 +5,7 @@ package auth
import (
"fmt"
+ "slices"
"strings"
"code.gitea.io/gitea/models/perm"
@@ -14,7 +15,7 @@ import (
type AccessTokenScopeCategory int
const (
- AccessTokenScopeCategoryActivityPub = iota
+ AccessTokenScopeCategoryActivityPub AccessTokenScopeCategory = iota
AccessTokenScopeCategoryAdmin
AccessTokenScopeCategoryMisc // WARN: this is now just a placeholder, don't remove it which will change the following values
AccessTokenScopeCategoryNotification
@@ -193,6 +194,14 @@ var accessTokenScopes = map[AccessTokenScopeLevel]map[AccessTokenScopeCategory]A
},
}
+func GetAccessTokenCategories() (res []string) {
+ for _, cat := range accessTokenScopes[Read] {
+ res = append(res, strings.TrimPrefix(string(cat), "read:"))
+ }
+ slices.Sort(res)
+ return res
+}
+
// GetRequiredScopes gets the specific scopes for a given level and categories
func GetRequiredScopes(level AccessTokenScopeLevel, scopeCategories ...AccessTokenScopeCategory) []AccessTokenScope {
scopes := make([]AccessTokenScope, 0, len(scopeCategories))
@@ -204,12 +213,7 @@ func GetRequiredScopes(level AccessTokenScopeLevel, scopeCategories ...AccessTok
// ContainsCategory checks if a list of categories contains a specific category
func ContainsCategory(categories []AccessTokenScopeCategory, category AccessTokenScopeCategory) bool {
- for _, c := range categories {
- if c == category {
- return true
- }
- }
- return false
+ return slices.Contains(categories, category)
}
// GetScopeLevelFromAccessMode converts permission access mode to scope level
@@ -270,6 +274,9 @@ func (s AccessTokenScope) parse() (accessTokenScopeBitmap, error) {
// StringSlice returns the AccessTokenScope as a []string
func (s AccessTokenScope) StringSlice() []string {
+ if s == "" {
+ return nil
+ }
return strings.Split(string(s), ",")
}
@@ -283,6 +290,10 @@ func (s AccessTokenScope) Normalize() (AccessTokenScope, error) {
return bitmap.toScope(), nil
}
+func (s AccessTokenScope) HasPermissionScope() bool {
+ return s != "" && s != AccessTokenScopePublicOnly
+}
+
// PublicOnly checks if this token scope is limited to public resources
func (s AccessTokenScope) PublicOnly() (bool, error) {
bitmap, err := s.parse()
diff --git a/models/auth/access_token_scope_test.go b/models/auth/access_token_scope_test.go
index a6097e45d7..b93c25528f 100644
--- a/models/auth/access_token_scope_test.go
+++ b/models/auth/access_token_scope_test.go
@@ -17,6 +17,7 @@ type scopeTestNormalize struct {
}
func TestAccessTokenScope_Normalize(t *testing.T) {
+ assert.Equal(t, []string{"activitypub", "admin", "issue", "misc", "notification", "organization", "package", "repository", "user"}, GetAccessTokenCategories())
tests := []scopeTestNormalize{
{"", "", nil},
{"write:misc,write:notification,read:package,write:notification,public-only", "public-only,write:misc,write:notification,read:package", nil},
@@ -25,13 +26,13 @@ func TestAccessTokenScope_Normalize(t *testing.T) {
{"write:activitypub,write:admin,write:misc,write:notification,write:organization,write:package,write:issue,write:repository,write:user,public-only", "public-only,all", nil},
}
- for _, scope := range []string{"activitypub", "admin", "misc", "notification", "organization", "package", "issue", "repository", "user"} {
+ for _, scope := range GetAccessTokenCategories() {
tests = append(tests,
- scopeTestNormalize{AccessTokenScope(fmt.Sprintf("read:%s", scope)), AccessTokenScope(fmt.Sprintf("read:%s", scope)), nil},
- scopeTestNormalize{AccessTokenScope(fmt.Sprintf("write:%s", scope)), AccessTokenScope(fmt.Sprintf("write:%s", scope)), nil},
- scopeTestNormalize{AccessTokenScope(fmt.Sprintf("write:%[1]s,read:%[1]s", scope)), AccessTokenScope(fmt.Sprintf("write:%s", scope)), nil},
- scopeTestNormalize{AccessTokenScope(fmt.Sprintf("read:%[1]s,write:%[1]s", scope)), AccessTokenScope(fmt.Sprintf("write:%s", scope)), nil},
- scopeTestNormalize{AccessTokenScope(fmt.Sprintf("read:%[1]s,write:%[1]s,write:%[1]s", scope)), AccessTokenScope(fmt.Sprintf("write:%s", scope)), nil},
+ scopeTestNormalize{AccessTokenScope("read:" + scope), AccessTokenScope("read:" + scope), nil},
+ scopeTestNormalize{AccessTokenScope("write:" + scope), AccessTokenScope("write:" + scope), nil},
+ scopeTestNormalize{AccessTokenScope(fmt.Sprintf("write:%[1]s,read:%[1]s", scope)), AccessTokenScope("write:" + scope), nil},
+ scopeTestNormalize{AccessTokenScope(fmt.Sprintf("read:%[1]s,write:%[1]s", scope)), AccessTokenScope("write:" + scope), nil},
+ scopeTestNormalize{AccessTokenScope(fmt.Sprintf("read:%[1]s,write:%[1]s,write:%[1]s", scope)), AccessTokenScope("write:" + scope), nil},
)
}
@@ -59,23 +60,23 @@ func TestAccessTokenScope_HasScope(t *testing.T) {
{"public-only", "read:issue", false, nil},
}
- for _, scope := range []string{"activitypub", "admin", "misc", "notification", "organization", "package", "issue", "repository", "user"} {
+ for _, scope := range GetAccessTokenCategories() {
tests = append(tests,
scopeTestHasScope{
- AccessTokenScope(fmt.Sprintf("read:%s", scope)),
- AccessTokenScope(fmt.Sprintf("read:%s", scope)), true, nil,
+ AccessTokenScope("read:" + scope),
+ AccessTokenScope("read:" + scope), true, nil,
},
scopeTestHasScope{
- AccessTokenScope(fmt.Sprintf("write:%s", scope)),
- AccessTokenScope(fmt.Sprintf("write:%s", scope)), true, nil,
+ AccessTokenScope("write:" + scope),
+ AccessTokenScope("write:" + scope), true, nil,
},
scopeTestHasScope{
- AccessTokenScope(fmt.Sprintf("write:%s", scope)),
- AccessTokenScope(fmt.Sprintf("read:%s", scope)), true, nil,
+ AccessTokenScope("write:" + scope),
+ AccessTokenScope("read:" + scope), true, nil,
},
scopeTestHasScope{
- AccessTokenScope(fmt.Sprintf("read:%s", scope)),
- AccessTokenScope(fmt.Sprintf("write:%s", scope)), false, nil,
+ AccessTokenScope("read:" + scope),
+ AccessTokenScope("write:" + scope), false, nil,
},
)
}
diff --git a/models/auth/auth_token.go b/models/auth/auth_token.go
index 81f07d1a83..54ff5a0d75 100644
--- a/models/auth/auth_token.go
+++ b/models/auth/auth_token.go
@@ -15,7 +15,7 @@ import (
var ErrAuthTokenNotExist = util.NewNotExistErrorf("auth token does not exist")
-type AuthToken struct { //nolint:revive
+type AuthToken struct { //nolint:revive // export stutter
ID string `xorm:"pk"`
TokenHash string
UserID int64 `xorm:"INDEX"`
diff --git a/models/auth/oauth2.go b/models/auth/oauth2.go
index c270e4856e..c2b6690116 100644
--- a/models/auth/oauth2.go
+++ b/models/auth/oauth2.go
@@ -12,6 +12,7 @@ import (
"fmt"
"net"
"net/url"
+ "slices"
"strings"
"code.gitea.io/gitea/models/db"
@@ -511,12 +512,7 @@ func (grant *OAuth2Grant) IncreaseCounter(ctx context.Context) error {
// ScopeContains returns true if the grant scope contains the specified scope
func (grant *OAuth2Grant) ScopeContains(scope string) bool {
- for _, currentScope := range strings.Split(grant.Scope, " ") {
- if scope == currentScope {
- return true
- }
- }
- return false
+ return slices.Contains(strings.Split(grant.Scope, " "), scope)
}
// SetNonce updates the current nonce value of a grant
diff --git a/models/auth/oauth2_test.go b/models/auth/oauth2_test.go
index 43daa0b5ec..c6626b283e 100644
--- a/models/auth/oauth2_test.go
+++ b/models/auth/oauth2_test.go
@@ -25,7 +25,7 @@ func TestOAuth2Application_GenerateClientSecret(t *testing.T) {
func BenchmarkOAuth2Application_GenerateClientSecret(b *testing.B) {
assert.NoError(b, unittest.PrepareTestDatabase())
app := unittest.AssertExistsAndLoadBean(b, &auth_model.OAuth2Application{ID: 1})
- for i := 0; i < b.N; i++ {
+ for b.Loop() {
_, _ = app.GenerateClientSecret(db.DefaultContext)
}
}
@@ -126,7 +126,7 @@ func TestOAuth2Application_CreateGrant(t *testing.T) {
assert.NotNil(t, grant)
assert.Equal(t, int64(2), grant.UserID)
assert.Equal(t, int64(1), grant.ApplicationID)
- assert.Equal(t, "", grant.Scope)
+ assert.Empty(t, grant.Scope)
}
//////////////////// Grant
diff --git a/models/auth/source.go b/models/auth/source.go
index a3a250cd91..7d7bc0f03c 100644
--- a/models/auth/source.go
+++ b/models/auth/source.go
@@ -58,6 +58,15 @@ var Names = map[Type]string{
// Config represents login config as far as the db is concerned
type Config interface {
convert.Conversion
+ SetAuthSource(*Source)
+}
+
+type ConfigBase struct {
+ AuthSource *Source
+}
+
+func (p *ConfigBase) SetAuthSource(s *Source) {
+ p.AuthSource = s
}
// SkipVerifiable configurations provide a IsSkipVerify to check if SkipVerify is set
@@ -104,19 +113,15 @@ func RegisterTypeConfig(typ Type, exemplar Config) {
}
}
-// SourceSettable configurations can have their authSource set on them
-type SourceSettable interface {
- SetAuthSource(*Source)
-}
-
// Source represents an external way for authorizing users.
type Source struct {
- ID int64 `xorm:"pk autoincr"`
- Type Type
- Name string `xorm:"UNIQUE"`
- IsActive bool `xorm:"INDEX NOT NULL DEFAULT false"`
- IsSyncEnabled bool `xorm:"INDEX NOT NULL DEFAULT false"`
- Cfg convert.Conversion `xorm:"TEXT"`
+ ID int64 `xorm:"pk autoincr"`
+ Type Type
+ Name string `xorm:"UNIQUE"`
+ IsActive bool `xorm:"INDEX NOT NULL DEFAULT false"`
+ IsSyncEnabled bool `xorm:"INDEX NOT NULL DEFAULT false"`
+ TwoFactorPolicy string `xorm:"two_factor_policy NOT NULL DEFAULT ''"`
+ Cfg Config `xorm:"TEXT"`
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
@@ -140,9 +145,7 @@ func (source *Source) BeforeSet(colName string, val xorm.Cell) {
return
}
source.Cfg = constructor()
- if settable, ok := source.Cfg.(SourceSettable); ok {
- settable.SetAuthSource(source)
- }
+ source.Cfg.SetAuthSource(source)
}
}
@@ -200,6 +203,10 @@ func (source *Source) SkipVerify() bool {
return ok && skipVerifiable.IsSkipVerify()
}
+func (source *Source) TwoFactorShouldSkip() bool {
+ return source.TwoFactorPolicy == "skip"
+}
+
// CreateSource inserts a AuthSource in the DB if not already
// existing with the given name.
func CreateSource(ctx context.Context, source *Source) error {
@@ -223,9 +230,7 @@ func CreateSource(ctx context.Context, source *Source) error {
return nil
}
- if settable, ok := source.Cfg.(SourceSettable); ok {
- settable.SetAuthSource(source)
- }
+ source.Cfg.SetAuthSource(source)
registerableSource, ok := source.Cfg.(RegisterableSource)
if !ok {
@@ -320,9 +325,7 @@ func UpdateSource(ctx context.Context, source *Source) error {
return nil
}
- if settable, ok := source.Cfg.(SourceSettable); ok {
- settable.SetAuthSource(source)
- }
+ source.Cfg.SetAuthSource(source)
registerableSource, ok := source.Cfg.(RegisterableSource)
if !ok {
diff --git a/models/auth/source_test.go b/models/auth/source_test.go
index 84aede0a6b..64c7460b64 100644
--- a/models/auth/source_test.go
+++ b/models/auth/source_test.go
@@ -19,6 +19,8 @@ import (
)
type TestSource struct {
+ auth_model.ConfigBase
+
Provider string
ClientID string
ClientSecret string
diff --git a/models/auth/twofactor.go b/models/auth/twofactor.go
index d0c341a192..200ce7c7c0 100644
--- a/models/auth/twofactor.go
+++ b/models/auth/twofactor.go
@@ -164,3 +164,13 @@ func DeleteTwoFactorByID(ctx context.Context, id, userID int64) error {
}
return nil
}
+
+func HasTwoFactorOrWebAuthn(ctx context.Context, id int64) (bool, error) {
+ has, err := HasTwoFactorByUID(ctx, id)
+ if err != nil {
+ return false, err
+ } else if has {
+ return true, nil
+ }
+ return HasWebAuthnRegistrationsByUID(ctx, id)
+}