diff options
author | Jack Hay <jack@allspice.io> | 2023-06-04 14:57:16 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-06-04 20:57:16 +0200 |
commit | 18de83b2a3fc120922096b7348d6375094ae1532 (patch) | |
tree | a9724bcb6f00a040e5a16970ce56931cd1aa3d51 /templates | |
parent | 520eb57d7642a5fca3df319e5b5d1c7c9018087c (diff) | |
download | gitea-18de83b2a3fc120922096b7348d6375094ae1532.tar.gz gitea-18de83b2a3fc120922096b7348d6375094ae1532.zip |
Redesign Scoped Access Tokens (#24767)
## Changes
- Adds the following high level access scopes, each with `read` and
`write` levels:
- `activitypub`
- `admin` (hidden if user is not a site admin)
- `misc`
- `notification`
- `organization`
- `package`
- `issue`
- `repository`
- `user`
- Adds new middleware function `tokenRequiresScopes()` in addition to
`reqToken()`
- `tokenRequiresScopes()` is used for each high-level api section
- _if_ a scoped token is present, checks that the required scope is
included based on the section and HTTP method
- `reqToken()` is used for individual routes
- checks that required authentication is present (but does not check
scope levels as this will already have been handled by
`tokenRequiresScopes()`
- Adds migration to convert old scoped access tokens to the new set of
scopes
- Updates the user interface for scope selection
### User interface example
<img width="903" alt="Screen Shot 2023-05-31 at 1 56 55 PM"
src="https://github.com/go-gitea/gitea/assets/23248839/654766ec-2143-4f59-9037-3b51600e32f3">
<img width="917" alt="Screen Shot 2023-05-31 at 1 56 43 PM"
src="https://github.com/go-gitea/gitea/assets/23248839/1ad64081-012c-4a73-b393-66b30352654c">
## tokenRequiresScopes Design Decision
- `tokenRequiresScopes()` was added to more reliably cover api routes.
For an incoming request, this function uses the given scope category
(say `AccessTokenScopeCategoryOrganization`) and the HTTP method (say
`DELETE`) and verifies that any scoped tokens in use include
`delete:organization`.
- `reqToken()` is used to enforce auth for individual routes that
require it. If a scoped token is not present for a request,
`tokenRequiresScopes()` will not return an error
## TODO
- [x] Alphabetize scope categories
- [x] Change 'public repos only' to a radio button (private vs public).
Also expand this to organizations
- [X] Disable token creation if no scopes selected. Alternatively, show
warning
- [x] `reqToken()` is missing from many `POST/DELETE` routes in the api.
`tokenRequiresScopes()` only checks that a given token has the correct
scope, `reqToken()` must be used to check that a token (or some other
auth) is present.
- _This should be addressed in this PR_
- [x] The migration should be reviewed very carefully in order to
minimize access changes to existing user tokens.
- _This should be addressed in this PR_
- [x] Link to api to swagger documentation, clarify what
read/write/delete levels correspond to
- [x] Review cases where more than one scope is needed as this directly
deviates from the api definition.
- _This should be addressed in this PR_
- For example:
```go
m.Group("/users/{username}/orgs", func() {
m.Get("", reqToken(), org.ListUserOrgs)
m.Get("/{org}/permissions", reqToken(), org.GetUserOrgsPermissions)
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser,
auth_model.AccessTokenScopeCategoryOrganization),
context_service.UserAssignmentAPI())
```
## Future improvements
- [ ] Add required scopes to swagger documentation
- [ ] Redesign `reqToken()` to be opt-out rather than opt-in
- [ ] Subdivide scopes like `repository`
- [ ] Once a token is created, if it has no scopes, we should display
text instead of an empty bullet point
- [ ] If the 'public repos only' option is selected, should read
categories be selected by default
Closes #24501
Closes #24799
Co-authored-by: Jonathan Tran <jon@allspice.io>
Co-authored-by: Kyle D <kdumontnu@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Diffstat (limited to 'templates')
-rw-r--r-- | templates/user/settings/applications.tmpl | 260 |
1 files changed, 46 insertions, 214 deletions
diff --git a/templates/user/settings/applications.tmpl b/templates/user/settings/applications.tmpl index 1c12a0a2b7..2b7db82dae 100644 --- a/templates/user/settings/applications.tmpl +++ b/templates/user/settings/applications.tmpl @@ -18,13 +18,21 @@ </div> <i class="text {{if .HasRecentActivity}}green{{end}}" {{if .HasRecentActivity}}data-tooltip-content="{{$.locale.Tr "settings.token_state_desc"}}"{{end}}>{{svg "fontawesome-send" 36}}</i> <div class="content"> - <!--Temporarily disable--> - <strong>{{.Name}}</strong> - <details class="gt-hidden"><summary><strong>{{.Name}}</strong></summary> - <p class="gt-my-2">{{$.locale.Tr "settings.scopes_list"}}</p> + <details><summary><strong>{{.Name}}</strong></summary> + <p class="gt-my-2"> + {{$.locale.Tr "settings.repo_and_org_access"}}: + {{if .DisplayPublicOnly}} + {{$.locale.Tr "settings.permissions_public_only"}} + {{else}} + {{$.locale.Tr "settings.permissions_access_all"}} + {{end}} + </p> + <p class="gt-my-2">{{$.locale.Tr "settings.permissions_list"}}</p> <ul class="gt-my-2"> {{range .Scope.StringSlice}} - <li>{{.}}</li> + {{if (ne . $.AccessTokenScopePublicOnly)}} + <li>{{.}}</li> + {{end}} {{end}} </ul> </details> @@ -40,222 +48,46 @@ <h5 class="ui top header"> {{.locale.Tr "settings.generate_new_token"}} </h5> - <p>{{.locale.Tr "settings.new_token_desc"}}</p> - <form class="ui form ignore-dirty" action="{{.Link}}" method="post"> + <form id="scoped-access-form" class="ui form ignore-dirty" action="{{.Link}}" method="post"> {{.CsrfTokenHtml}} <div class="field {{if .Err_Name}}error{{end}}"> <label for="name">{{.locale.Tr "settings.token_name"}}</label> <input id="name" name="name" value="{{.name}}" autofocus required maxlength="255"> </div> - <!--Temporarily disable--> - <details class="gt-hidden ui optional field"> - <summary class="gt-p-2"> - {{.locale.Tr "settings.select_scopes"}} + <div class="field"> + <label>{{.locale.Tr "settings.repo_and_org_access"}}</label> + <label class="gt-cursor-pointer"> + <input class="enable-system gt-mt-2 gt-mr-2" type="radio" name="scope" value="{{$.AccessTokenScopePublicOnly}}"> + {{.locale.Tr "settings.permissions_public_only"}} + </label> + <label class="gt-cursor-pointer"> + <input class="enable-system gt-mt-2 gt-mr-2" type="radio" name="scope" value="" checked> + {{.locale.Tr "settings.permissions_access_all"}} + </label> + </div> + <details class="ui optional field"> + <summary class="gt-pb-4 gt-pl-2"> + {{.locale.Tr "settings.select_permissions"}} </summary> - <div class="field gt-pl-2"> - <div class="ui checkbox"> - <input class="enable-system" type="checkbox" name="scope" value="repo"> - <label>repo</label> - </div> - </div> - <div class="field gt-pl-4"> - <div class="field"> - <div class="ui checkbox"> - <input class="enable-system" type="checkbox" name="scope" value="repo:status"> - <label>repo:status</label> - </div> - </div> - <div class="field"> - <div class="ui checkbox"> - <input class="enable-system" type="checkbox" name="scope" value="public_repo"> - <label>public_repo</label> - </div> - </div> - </div> - <div class="field"> - <div class="ui checkbox"> - <input class="enable-system" type="checkbox" name="scope" value="admin:org"> - <label>admin:org</label> - </div> - </div> - <div class="field gt-pl-4"> - <div class="field"> - <div class="ui checkbox"> - <input class="enable-system" type="checkbox" name="scope" value="write:org"> - <label>write:org</label> - </div> - </div> - <div class="field"> - <div class="ui checkbox"> - <input class="enable-system" type="checkbox" name="scope" value="read:org"> - <label>read:org</label> - </div> - </div> - </div> - <div class="field"> - <div class="ui checkbox"> - <input class="enable-system" type="checkbox" name="scope" value="admin:public_key"> - <label>admin:public_key</label> - </div> - </div> - <div class="field gt-pl-4"> - <div class="field"> - <div class="ui checkbox"> - <input class="enable-system" type="checkbox" name="scope" value="write:public_key"> - <label>write:public_key</label> - </div> - </div> - <div class="field"> - <div class="ui checkbox"> - <input class="enable-system" type="checkbox" name="scope" value="read:public_key"> - <label>read:public_key</label> - </div> - </div> - </div> - <div class="field"> - <div class="ui checkbox"> - <input class="enable-system" type="checkbox" name="scope" value="admin:repo_hook"> - <label>admin:repo_hook</label> - </div> - </div> - <div class="field gt-pl-4"> - <div class="field"> - <div class="ui checkbox"> - <input class="enable-system" type="checkbox" name="scope" value="write:repo_hook"> - <label>write:repo_hook</label> - </div> - </div> - <div class="field"> - <div class="ui checkbox"> - <input class="enable-system" type="checkbox" name="scope" value="read:repo_hook"> - <label>read:repo_hook</label> - </div> - </div> - </div> - <div class="field"> - <div class="ui checkbox"> - <input class="enable-system" type="checkbox" name="scope" value="admin:org_hook"> - <label>admin:org_hook</label> - </div> - </div> - <div class="field"> - <div class="ui checkbox"> - <input class="enable-system" type="checkbox" name="scope" value="admin:user_hook"> - <label>admin:user_hook</label> - </div> - </div> - <div class="field"> - <div class="ui checkbox"> - <input class="enable-system" type="checkbox" name="scope" value="notification"> - <label>notification</label> - </div> - </div> - <div class="field"> - <div class="ui checkbox"> - <input class="enable-system" type="checkbox" name="scope" value="user"> - <label>user</label> - </div> - </div> - <div class="field gt-pl-4"> - <div class="field"> - <div class="ui checkbox"> - <input class="enable-system" type="checkbox" name="scope" value="read:user"> - <label>read:user</label> - </div> - </div> - <div class="field"> - <div class="ui checkbox"> - <input class="enable-system" type="checkbox" name="scope" value="user:email"> - <label>user:email</label> - </div> - </div> - <div class="field"> - <div class="ui checkbox"> - <input class="enable-system" type="checkbox" name="scope" value="user:follow"> - <label>user:follow</label> - </div> - </div> - </div> - <div class="field"> - <div class="ui checkbox"> - <input class="enable-system" type="checkbox" name="scope" value="delete_repo"> - <label>delete_repo</label> - </div> - </div> - <div class="field"> - <div class="ui checkbox"> - <input class="enable-system" type="checkbox" name="scope" value="package"> - <label>package</label> - </div> - </div> - <div class="field gt-pl-4"> - <div class="field"> - <div class="ui checkbox"> - <input class="enable-system" type="checkbox" name="scope" value="write:package"> - <label>write:package</label> - </div> - </div> - <div class="field"> - <div class="ui checkbox"> - <input class="enable-system" type="checkbox" name="scope" value="read:package"> - <label>read:package</label> - </div> - </div> - <div class="field"> - <div class="ui checkbox"> - <input class="enable-system" type="checkbox" name="scope" value="delete:package"> - <label>delete:package</label> - </div> - </div> - </div> - <div class="field"> - <div class="ui checkbox"> - <input class="enable-system" type="checkbox" name="scope" value="admin:gpg_key"> - <label>admin:gpg_key</label> - </div> - </div> - <div class="field gt-pl-4"> - <div class="field"> - <div class="ui checkbox"> - <input class="enable-system" type="checkbox" name="scope" value="write:gpg_key"> - <label>write:gpg_key</label> - </div> - </div> - <div class="field"> - <div class="ui checkbox"> - <input class="enable-system" type="checkbox" name="scope" value="read:gpg_key"> - <label>read:gpg_key</label> - </div> - </div> - </div> - <div class="field"> - <div class="ui checkbox"> - <input class="enable-system" type="checkbox" name="scope" value="admin:application"> - <label>admin:application</label> - </div> - </div> - <div class="field gt-pl-4"> - <div class="field"> - <div class="ui checkbox"> - <input class="enable-system" type="checkbox" name="scope" value="write:application"> - <label>write:application</label> - </div> - </div> - <div class="field"> - <div class="ui checkbox"> - <input class="enable-system" type="checkbox" name="scope" value="read:application"> - <label>read:application</label> - </div> - </div> - </div> - <div class="field"> - <div class="ui checkbox"> - <input class="enable-system" type="checkbox" name="scope" value="sudo"> - <label>sudo</label> - </div> - </div> + <div class="activity meta"> + <i>{{$.locale.Tr "settings.scoped_token_desc" (printf `href="/api/swagger" target="_blank"`) (printf `href="https://docs.gitea.com/development/oauth2-provider#scopes" target="_blank"`) | Str2html}}</i> + </div> + <scoped-access-token-category category="activitypub"></scoped-access-token-category> + {{if .IsAdmin}} + <scoped-access-token-category category="admin"></scoped-access-token-category> + {{end}} + <scoped-access-token-category category="issue"></scoped-access-token-category> + <scoped-access-token-category category="misc"></scoped-access-token-category> + <scoped-access-token-category category="notification"></scoped-access-token-category> + <scoped-access-token-category category="organization"></scoped-access-token-category> + <scoped-access-token-category category="package"></scoped-access-token-category> + <scoped-access-token-category category="repository"></scoped-access-token-category> + <scoped-access-token-category category="user"></scoped-access-token-category> </details> - <button class="ui green button"> + <div id="scoped-access-warning" class="ui warning message center gt-db gt-hidden"> + {{.locale.Tr "settings.at_least_one_permission"}} + </div> + <button id="scoped-access-submit" class="ui green button"> {{.locale.Tr "settings.generate_token"}} </button> </form> |