Skip to content

Commit d2128b4

Browse files
authored
Add scopes to API to create token and display them (#22989)
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 <[email protected]>
1 parent 330b166 commit d2128b4

File tree

6 files changed

+68
-14
lines changed

6 files changed

+68
-14
lines changed

models/auth/token_scope.go

+22-3
Original file line numberDiff line numberDiff line change
@@ -168,10 +168,23 @@ var allAccessTokenScopeBits = map[AccessTokenScope]AccessTokenScopeBitmap{
168168

169169
// Parse parses the scope string into a bitmap, thus removing possible duplicates.
170170
func (s AccessTokenScope) Parse() (AccessTokenScopeBitmap, error) {
171-
list := strings.Split(string(s), ",")
172-
173171
var bitmap AccessTokenScopeBitmap
174-
for _, v := range list {
172+
173+
// The following is the more performant equivalent of 'for _, v := range strings.Split(remainingScope, ",")' as this is hot code
174+
remainingScopes := string(s)
175+
for len(remainingScopes) > 0 {
176+
i := strings.IndexByte(remainingScopes, ',')
177+
var v string
178+
if i < 0 {
179+
v = remainingScopes
180+
remainingScopes = ""
181+
} else if i+1 >= len(remainingScopes) {
182+
v = remainingScopes[:i]
183+
remainingScopes = ""
184+
} else {
185+
v = remainingScopes[:i]
186+
remainingScopes = remainingScopes[i+1:]
187+
}
175188
singleScope := AccessTokenScope(v)
176189
if singleScope == "" {
177190
continue
@@ -187,9 +200,15 @@ func (s AccessTokenScope) Parse() (AccessTokenScopeBitmap, error) {
187200
}
188201
bitmap |= bits
189202
}
203+
190204
return bitmap, nil
191205
}
192206

207+
// StringSlice returns the AccessTokenScope as a []string
208+
func (s AccessTokenScope) StringSlice() []string {
209+
return strings.Split(string(s), ",")
210+
}
211+
193212
// Normalize returns a normalized scope string without any duplicates.
194213
func (s AccessTokenScope) Normalize() (AccessTokenScope, error) {
195214
bitmap, err := s.Parse()

modules/structs/user_app.go

+8-6
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,22 @@ import (
1111
// AccessToken represents an API access token.
1212
// swagger:response AccessToken
1313
type AccessToken struct {
14-
ID int64 `json:"id"`
15-
Name string `json:"name"`
16-
Token string `json:"sha1"`
17-
TokenLastEight string `json:"token_last_eight"`
14+
ID int64 `json:"id"`
15+
Name string `json:"name"`
16+
Token string `json:"sha1"`
17+
TokenLastEight string `json:"token_last_eight"`
18+
Scopes []string `json:"scopes"`
1819
}
1920

2021
// AccessTokenList represents a list of API access token.
2122
// swagger:response AccessTokenList
2223
type AccessTokenList []*AccessToken
2324

2425
// CreateAccessTokenOption options when create access token
25-
// swagger:parameters userCreateToken
2626
type CreateAccessTokenOption struct {
27-
Name string `json:"name" binding:"Required"`
27+
// required: true
28+
Name string `json:"name" binding:"Required"`
29+
Scopes []string `json:"scopes"`
2830
}
2931

3032
// CreateOAuth2ApplicationOptions holds options to create an oauth2 application

options/locale/locale_en-US.ini

+1
Original file line numberDiff line numberDiff line change
@@ -757,6 +757,7 @@ access_token_deletion_confirm_action = Delete
757757
access_token_deletion_desc = Deleting a token will revoke access to your account for applications using it. This cannot be undone. Continue?
758758
delete_token_success = The token has been deleted. Applications using it no longer have access to your account.
759759
select_scopes = Select scopes
760+
scopes_list = Scopes:
760761
761762
manage_oauth2_applications = Manage OAuth2 Applications
762763
edit_oauth2_application = Edit OAuth2 Application

routers/api/v1/user/app.go

+11-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"fmt"
1010
"net/http"
1111
"strconv"
12+
"strings"
1213

1314
auth_model "code.gitea.io/gitea/models/auth"
1415
"code.gitea.io/gitea/modules/context"
@@ -62,6 +63,7 @@ func ListAccessTokens(ctx *context.APIContext) {
6263
ID: tokens[i].ID,
6364
Name: tokens[i].Name,
6465
TokenLastEight: tokens[i].TokenLastEight,
66+
Scopes: tokens[i].Scope.StringSlice(),
6567
}
6668
}
6769

@@ -82,9 +84,9 @@ func CreateAccessToken(ctx *context.APIContext) {
8284
// - name: username
8385
// in: path
8486
// description: username of user
85-
// type: string
8687
// required: true
87-
// - name: userCreateToken
88+
// type: string
89+
// - name: body
8890
// in: body
8991
// schema:
9092
// "$ref": "#/definitions/CreateAccessTokenOption"
@@ -111,6 +113,13 @@ func CreateAccessToken(ctx *context.APIContext) {
111113
return
112114
}
113115

116+
scope, err := auth_model.AccessTokenScope(strings.Join(form.Scopes, ",")).Normalize()
117+
if err != nil {
118+
ctx.Error(http.StatusBadRequest, "AccessTokenScope.Normalize", fmt.Errorf("invalid access token scope provided: %w", err))
119+
return
120+
}
121+
t.Scope = scope
122+
114123
if err := auth_model.NewAccessToken(t); err != nil {
115124
ctx.Error(http.StatusInternalServerError, "NewAccessToken", err)
116125
return

templates/swagger/v1_json.tmpl

+18-2
Original file line numberDiff line numberDiff line change
@@ -14084,14 +14084,13 @@
1408414084
"parameters": [
1408514085
{
1408614086
"type": "string",
14087-
"x-go-name": "Name",
1408814087
"description": "username of user",
1408914088
"name": "username",
1409014089
"in": "path",
1409114090
"required": true
1409214091
},
1409314092
{
14094-
"name": "userCreateToken",
14093+
"name": "body",
1409514094
"in": "body",
1409614095
"schema": {
1409714096
"$ref": "#/definitions/CreateAccessTokenOption"
@@ -14194,6 +14193,13 @@
1419414193
"type": "string",
1419514194
"x-go-name": "Name"
1419614195
},
14196+
"scopes": {
14197+
"type": "array",
14198+
"items": {
14199+
"type": "string"
14200+
},
14201+
"x-go-name": "Scopes"
14202+
},
1419714203
"sha1": {
1419814204
"type": "string",
1419914205
"x-go-name": "Token"
@@ -14925,10 +14931,20 @@
1492514931
"CreateAccessTokenOption": {
1492614932
"description": "CreateAccessTokenOption options when create access token",
1492714933
"type": "object",
14934+
"required": [
14935+
"name"
14936+
],
1492814937
"properties": {
1492914938
"name": {
1493014939
"type": "string",
1493114940
"x-go-name": "Name"
14941+
},
14942+
"scopes": {
14943+
"type": "array",
14944+
"items": {
14945+
"type": "string"
14946+
},
14947+
"x-go-name": "Scopes"
1493214948
}
1493314949
},
1493414950
"x-go-package": "code.gitea.io/gitea/modules/structs"

templates/user/settings/applications.tmpl

+8-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,14 @@
2121
</div>
2222
<i class="icon tooltip{{if .HasRecentActivity}} green{{end}}" {{if .HasRecentActivity}}data-content="{{$.locale.Tr "settings.token_state_desc"}}"{{end}}>{{svg "fontawesome-send" 36}}</i>
2323
<div class="content">
24-
<strong>{{.Name}}</strong>
24+
<details><summary><strong>{{.Name}}</strong></summary>
25+
<p class="gt-my-2">{{$.locale.Tr "settings.scopes_list"}}</p>
26+
<ul class="gt-my-2">
27+
{{range .Scope.StringSlice}}
28+
<li>{{.}}</li>
29+
{{end}}
30+
</ul>
31+
</details>
2532
<div class="activity meta">
2633
<i>{{$.locale.Tr "settings.add_on"}} <span><time data-format="short-date" datetime="{{.CreatedUnix.FormatLong}}">{{.CreatedUnix.FormatShort}}</time></span> — {{svg "octicon-info"}} {{if .HasUsed}}{{$.locale.Tr "settings.last_used"}} <span {{if .HasRecentActivity}}class="green"{{end}}><time data-format="short-date" datetime="{{.UpdatedUnix.FormatLong}}">{{.UpdatedUnix.FormatShort}}</time></span>{{else}}{{$.locale.Tr "settings.no_activity"}}{{end}}</i>
2734
</div>

0 commit comments

Comments
 (0)