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 1 commit
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
24 changes: 21 additions & 3 deletions models/auth/token_scope.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
lunny marked this conversation as resolved.
Show resolved Hide resolved
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:]
}
lunny marked this conversation as resolved.
Show resolved Hide resolved
singleScope := AccessTokenScope(v)
if singleScope == "" {
continue
Expand All @@ -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()
Expand Down
12 changes: 7 additions & 5 deletions modules/structs/user_app.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
lunny marked this conversation as resolved.
Show resolved Hide resolved
}

// AccessTokenList represents a list of API access token.
Expand All @@ -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
lunny marked this conversation as resolved.
Show resolved Hide resolved
}

// 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
9 changes: 9 additions & 0 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,
Scope: tokens[i].Scope.StringSlice(),
}
}

Expand Down Expand Up @@ -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
Expand Down
17 changes: 17 additions & 0 deletions templates/swagger/v1_json.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -14091,6 +14091,10 @@
"required": true
},
{
"type": "array",
"items": {
"type": "string"
},
"name": "userCreateToken",
"in": "body",
"schema": {
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -14926,6 +14937,12 @@
"description": "CreateAccessTokenOption options when create access token",
"type": "object",
"properties": {
"Scope": {
lunny marked this conversation as resolved.
Show resolved Hide resolved
"type": "array",
"items": {
"type": "string"
}
},
"name": {
"type": "string",
"x-go-name": "Name"
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