From 2a5ce255d84fbd7b5a5f130db44449ccc8c015e3 Mon Sep 17 00:00:00 2001 From: Andrew Thornton Date: Sun, 19 Feb 2023 14:36:44 +0000 Subject: [PATCH 1/4] Add scopes to API to create token and display them The API to create tokens is missing the ability to set the required scopes for tokens, and to show them on the API and on the UI. This PR adds this functionality. Signed-off-by: Andrew Thornton --- models/auth/token_scope.go | 24 ++++++++++++++++++++--- modules/structs/user_app.go | 12 +++++++----- options/locale/locale_en-US.ini | 1 + routers/api/v1/user/app.go | 9 +++++++++ templates/swagger/v1_json.tmpl | 17 ++++++++++++++++ templates/user/settings/applications.tmpl | 9 ++++++++- 6 files changed, 63 insertions(+), 9 deletions(-) diff --git a/models/auth/token_scope.go b/models/auth/token_scope.go index c61c306496b49..489c1af8f7144 100644 --- a/models/auth/token_scope.go +++ b/models/auth/token_scope.go @@ -168,10 +168,22 @@ var allAccessTokenScopeBits = map[AccessTokenScope]AccessTokenScopeBitmap{ // Parse parses the scope string into a bitmap, thus removing possible duplicates. func (s AccessTokenScope) Parse() (AccessTokenScopeBitmap, error) { - list := strings.Split(string(s), ",") - var bitmap AccessTokenScopeBitmap - for _, v := range list { + + remainingScopes := string(s) + for len(remainingScopes) > 0 { + i := strings.IndexByte(remainingScopes, ',') + var v string + if i < 0 { + v = remainingScopes + remainingScopes = "" + } else if i+1 >= len(remainingScopes) { + v = remainingScopes[:i] + remainingScopes = "" + } else { + v = remainingScopes[:i] + remainingScopes = remainingScopes[i+1:] + } singleScope := AccessTokenScope(v) if singleScope == "" { continue @@ -187,9 +199,15 @@ func (s AccessTokenScope) Parse() (AccessTokenScopeBitmap, error) { } bitmap |= bits } + return bitmap, nil } +// StringSlice returns the AccessTokenScope as a []string +func (s AccessTokenScope) StringSlice() []string { + return strings.Split(string(s), ",") +} + // Normalize returns a normalized scope string without any duplicates. func (s AccessTokenScope) Normalize() (AccessTokenScope, error) { bitmap, err := s.Parse() diff --git a/modules/structs/user_app.go b/modules/structs/user_app.go index 3a5ae34df19a8..eb2deecf73ded 100644 --- a/modules/structs/user_app.go +++ b/modules/structs/user_app.go @@ -11,10 +11,11 @@ import ( // AccessToken represents an API access token. // swagger:response AccessToken type AccessToken struct { - ID int64 `json:"id"` - Name string `json:"name"` - Token string `json:"sha1"` - TokenLastEight string `json:"token_last_eight"` + ID int64 `json:"id"` + Name string `json:"name"` + Token string `json:"sha1"` + TokenLastEight string `json:"token_last_eight"` + Scope []string `json:"scope"` } // AccessTokenList represents a list of API access token. @@ -24,7 +25,8 @@ type AccessTokenList []*AccessToken // CreateAccessTokenOption options when create access token // swagger:parameters userCreateToken type CreateAccessTokenOption struct { - Name string `json:"name" binding:"Required"` + Name string `json:"name" binding:"Required"` + Scope []string } // CreateOAuth2ApplicationOptions holds options to create an oauth2 application diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 411a585c81d60..6a9d3ccf8e070 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -757,6 +757,7 @@ access_token_deletion_confirm_action = Delete access_token_deletion_desc = Deleting a token will revoke access to your account for applications using it. This cannot be undone. Continue? delete_token_success = The token has been deleted. Applications using it no longer have access to your account. select_scopes = Select scopes +scopes_list = Scopes: manage_oauth2_applications = Manage OAuth2 Applications edit_oauth2_application = Edit OAuth2 Application diff --git a/routers/api/v1/user/app.go b/routers/api/v1/user/app.go index 7b2f0d8c30d2e..22009ef4dcd06 100644 --- a/routers/api/v1/user/app.go +++ b/routers/api/v1/user/app.go @@ -9,6 +9,7 @@ import ( "fmt" "net/http" "strconv" + "strings" auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/modules/context" @@ -62,6 +63,7 @@ func ListAccessTokens(ctx *context.APIContext) { ID: tokens[i].ID, Name: tokens[i].Name, TokenLastEight: tokens[i].TokenLastEight, + Scope: tokens[i].Scope.StringSlice(), } } @@ -111,6 +113,13 @@ func CreateAccessToken(ctx *context.APIContext) { return } + scope, err := auth_model.AccessTokenScope(strings.Join(form.Scope, ",")).Normalize() + if err != nil { + ctx.Error(http.StatusBadRequest, "AccessTokenScope.Normalize", fmt.Errorf("invalid access token scope provided: %w", err)) + return + } + t.Scope = scope + if err := auth_model.NewAccessToken(t); err != nil { ctx.Error(http.StatusInternalServerError, "NewAccessToken", err) return diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 2a675766abc92..11939b86644e3 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -14091,6 +14091,10 @@ "required": true }, { + "type": "array", + "items": { + "type": "string" + }, "name": "userCreateToken", "in": "body", "schema": { @@ -14194,6 +14198,13 @@ "type": "string", "x-go-name": "Name" }, + "scope": { + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "Scope" + }, "sha1": { "type": "string", "x-go-name": "Token" @@ -14926,6 +14937,12 @@ "description": "CreateAccessTokenOption options when create access token", "type": "object", "properties": { + "Scope": { + "type": "array", + "items": { + "type": "string" + } + }, "name": { "type": "string", "x-go-name": "Name" diff --git a/templates/user/settings/applications.tmpl b/templates/user/settings/applications.tmpl index 439ed5e1483fd..ef9ac9a97784c 100644 --- a/templates/user/settings/applications.tmpl +++ b/templates/user/settings/applications.tmpl @@ -21,7 +21,14 @@ {{svg "fontawesome-send" 36}}
- {{.Name}} +
{{.Name}} +

{{$.locale.Tr "settings.scopes_list"}}

+
    + {{range .Scope.StringSlice}} +
  • {{.}}
  • + {{end}} +
+
{{$.locale.Tr "settings.add_on"}} — {{svg "octicon-info"}} {{if .HasUsed}}{{$.locale.Tr "settings.last_used"}} {{else}}{{$.locale.Tr "settings.no_activity"}}{{end}}
From 4c792bad78a09c4ad5ade733d059401fcca3f904 Mon Sep 17 00:00:00 2001 From: Andrew Thornton Date: Sun, 19 Feb 2023 16:37:54 +0000 Subject: [PATCH 2/4] as per reviews Signed-off-by: Andrew Thornton --- modules/structs/user_app.go | 6 +++--- routers/api/v1/user/app.go | 4 ++-- templates/swagger/v1_json.tmpl | 18 ++++++++++-------- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/modules/structs/user_app.go b/modules/structs/user_app.go index eb2deecf73ded..da4959dcf720a 100644 --- a/modules/structs/user_app.go +++ b/modules/structs/user_app.go @@ -15,7 +15,7 @@ type AccessToken struct { Name string `json:"name"` Token string `json:"sha1"` TokenLastEight string `json:"token_last_eight"` - Scope []string `json:"scope"` + Scopes []string `json:"scopes"` } // AccessTokenList represents a list of API access token. @@ -25,8 +25,8 @@ type AccessTokenList []*AccessToken // CreateAccessTokenOption options when create access token // swagger:parameters userCreateToken type CreateAccessTokenOption struct { - Name string `json:"name" binding:"Required"` - Scope []string + Name string `json:"name" binding:"Required"` + Scopes []string `json:"scopes"` } // CreateOAuth2ApplicationOptions holds options to create an oauth2 application diff --git a/routers/api/v1/user/app.go b/routers/api/v1/user/app.go index 22009ef4dcd06..b2acc73355a6c 100644 --- a/routers/api/v1/user/app.go +++ b/routers/api/v1/user/app.go @@ -63,7 +63,7 @@ func ListAccessTokens(ctx *context.APIContext) { ID: tokens[i].ID, Name: tokens[i].Name, TokenLastEight: tokens[i].TokenLastEight, - Scope: tokens[i].Scope.StringSlice(), + Scopes: tokens[i].Scope.StringSlice(), } } @@ -113,7 +113,7 @@ func CreateAccessToken(ctx *context.APIContext) { return } - scope, err := auth_model.AccessTokenScope(strings.Join(form.Scope, ",")).Normalize() + scope, err := auth_model.AccessTokenScope(strings.Join(form.Scopes, ",")).Normalize() if err != nil { ctx.Error(http.StatusBadRequest, "AccessTokenScope.Normalize", fmt.Errorf("invalid access token scope provided: %w", err)) return diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 11939b86644e3..6d02c34f2f023 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -14095,6 +14095,7 @@ "items": { "type": "string" }, + "x-go-name": "Scopes", "name": "userCreateToken", "in": "body", "schema": { @@ -14198,12 +14199,12 @@ "type": "string", "x-go-name": "Name" }, - "scope": { + "scopes": { "type": "array", "items": { "type": "string" }, - "x-go-name": "Scope" + "x-go-name": "Scopes" }, "sha1": { "type": "string", @@ -14937,15 +14938,16 @@ "description": "CreateAccessTokenOption options when create access token", "type": "object", "properties": { - "Scope": { - "type": "array", - "items": { - "type": "string" - } - }, "name": { "type": "string", "x-go-name": "Name" + }, + "scopes": { + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "Scopes" } }, "x-go-package": "code.gitea.io/gitea/modules/structs" From 734b60bae4bb32b865c061cc7331e331c7aa121e Mon Sep 17 00:00:00 2001 From: Andrew Thornton Date: Sun, 19 Feb 2023 16:41:51 +0000 Subject: [PATCH 3/4] try-to-validate Signed-off-by: Andrew Thornton --- modules/structs/user_app.go | 2 +- routers/api/v1/user/app.go | 4 ++-- templates/swagger/v1_json.tmpl | 11 ++++------- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/modules/structs/user_app.go b/modules/structs/user_app.go index da4959dcf720a..7f78fbd495e24 100644 --- a/modules/structs/user_app.go +++ b/modules/structs/user_app.go @@ -23,8 +23,8 @@ type AccessToken struct { type AccessTokenList []*AccessToken // CreateAccessTokenOption options when create access token -// swagger:parameters userCreateToken type CreateAccessTokenOption struct { + // required: true Name string `json:"name" binding:"Required"` Scopes []string `json:"scopes"` } diff --git a/routers/api/v1/user/app.go b/routers/api/v1/user/app.go index b2acc73355a6c..f89d53945fa0b 100644 --- a/routers/api/v1/user/app.go +++ b/routers/api/v1/user/app.go @@ -84,9 +84,9 @@ func CreateAccessToken(ctx *context.APIContext) { // - name: username // in: path // description: username of user - // type: string // required: true - // - name: userCreateToken + // type: string + // - name: body // in: body // schema: // "$ref": "#/definitions/CreateAccessTokenOption" diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 6d02c34f2f023..de774deaed13a 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -14084,19 +14084,13 @@ "parameters": [ { "type": "string", - "x-go-name": "Name", "description": "username of user", "name": "username", "in": "path", "required": true }, { - "type": "array", - "items": { - "type": "string" - }, - "x-go-name": "Scopes", - "name": "userCreateToken", + "name": "body", "in": "body", "schema": { "$ref": "#/definitions/CreateAccessTokenOption" @@ -14937,6 +14931,9 @@ "CreateAccessTokenOption": { "description": "CreateAccessTokenOption options when create access token", "type": "object", + "required": [ + "name" + ], "properties": { "name": { "type": "string", From f595901344187286aa73bbee8aa9c984818d0cb5 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Mon, 20 Feb 2023 12:09:16 +0800 Subject: [PATCH 4/4] Update models/auth/token_scope.go Co-authored-by: delvh --- models/auth/token_scope.go | 1 + 1 file changed, 1 insertion(+) diff --git a/models/auth/token_scope.go b/models/auth/token_scope.go index 489c1af8f7144..38733a1c8f305 100644 --- a/models/auth/token_scope.go +++ b/models/auth/token_scope.go @@ -170,6 +170,7 @@ var allAccessTokenScopeBits = map[AccessTokenScope]AccessTokenScopeBitmap{ func (s AccessTokenScope) Parse() (AccessTokenScopeBitmap, error) { var bitmap AccessTokenScopeBitmap + // The following is the more performant equivalent of 'for _, v := range strings.Split(remainingScope, ",")' as this is hot code remainingScopes := string(s) for len(remainingScopes) > 0 { i := strings.IndexByte(remainingScopes, ',')