Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add scopes to API to create token and display them #22989

Merged
merged 9 commits into from
Feb 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 22 additions & 3 deletions models/auth/token_scope.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,10 +168,23 @@ 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 {

// 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, ',')
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
Expand All @@ -187,9 +200,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()
Expand Down
14 changes: 8 additions & 6 deletions modules/structs/user_app.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,22 @@ 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"`
Scopes []string `json:"scopes"`
}

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

// CreateAccessTokenOption options when create access token
// swagger:parameters userCreateToken
type CreateAccessTokenOption struct {
Name string `json:"name" binding:"Required"`
// required: true
Name string `json:"name" binding:"Required"`
Scopes []string `json:"scopes"`
}

// CreateOAuth2ApplicationOptions holds options to create an oauth2 application
Expand Down
1 change: 1 addition & 0 deletions options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 11 additions & 2 deletions routers/api/v1/user/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"fmt"
"net/http"
"strconv"
"strings"

auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/modules/context"
Expand Down Expand Up @@ -62,6 +63,7 @@ func ListAccessTokens(ctx *context.APIContext) {
ID: tokens[i].ID,
Name: tokens[i].Name,
TokenLastEight: tokens[i].TokenLastEight,
Scopes: tokens[i].Scope.StringSlice(),
}
}

Expand All @@ -82,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"
Expand All @@ -111,6 +113,13 @@ func CreateAccessToken(ctx *context.APIContext) {
return
}

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
}
t.Scope = scope

if err := auth_model.NewAccessToken(t); err != nil {
ctx.Error(http.StatusInternalServerError, "NewAccessToken", err)
return
Expand Down
20 changes: 18 additions & 2 deletions templates/swagger/v1_json.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -14084,14 +14084,13 @@
"parameters": [
{
"type": "string",
"x-go-name": "Name",
"description": "username of user",
"name": "username",
"in": "path",
"required": true
},
{
"name": "userCreateToken",
"name": "body",
"in": "body",
"schema": {
"$ref": "#/definitions/CreateAccessTokenOption"
Expand Down Expand Up @@ -14194,6 +14193,13 @@
"type": "string",
"x-go-name": "Name"
},
"scopes": {
"type": "array",
"items": {
"type": "string"
},
"x-go-name": "Scopes"
},
"sha1": {
"type": "string",
"x-go-name": "Token"
Expand Down Expand Up @@ -14925,10 +14931,20 @@
"CreateAccessTokenOption": {
"description": "CreateAccessTokenOption options when create access token",
"type": "object",
"required": [
"name"
],
"properties": {
"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"
Expand Down
9 changes: 8 additions & 1 deletion templates/user/settings/applications.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,14 @@
</div>
<i class="icon tooltip{{if .HasRecentActivity}} green{{end}}" {{if .HasRecentActivity}}data-content="{{$.locale.Tr "settings.token_state_desc"}}"{{end}}>{{svg "fontawesome-send" 36}}</i>
<div class="content">
<strong>{{.Name}}</strong>
<details><summary><strong>{{.Name}}</strong></summary>
<p class="gt-my-2">{{$.locale.Tr "settings.scopes_list"}}</p>
<ul class="gt-my-2">
{{range .Scope.StringSlice}}
<li>{{.}}</li>
{{end}}
</ul>
</details>
<div class="activity meta">
<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>
</div>
Expand Down