## 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>tags/v1.20.0-rc0
@@ -44,42 +44,43 @@ To use the Authorization Code Grant as a third party application it is required | |||
## Scopes | |||
Gitea supports the following scopes for tokens: | |||
| Name | Description | | |||
| ---- | ----------- | | |||
| **(no scope)** | Grants read-only access to public user profile and public repositories. | | |||
| **repo** | Full control over all repositories. | | |||
| **repo:status** | Grants read/write access to commit status in all repositories. | | |||
| **public_repo** | Grants read/write access to public repositories only. | | |||
| **admin:repo_hook** | Grants access to repository hooks of all repositories. This is included in the `repo` scope. | | |||
| **write:repo_hook** | Grants read/write access to repository hooks | | |||
| **read:repo_hook** | Grants read-only access to repository hooks | | |||
| **admin:org** | Grants full access to organization settings | | |||
| **write:org** | Grants read/write access to organization settings | | |||
| **read:org** | Grants read-only access to organization settings | | |||
| **admin:public_key** | Grants full access for managing public keys | | |||
| **write:public_key** | Grant read/write access to public keys | | |||
| **read:public_key** | Grant read-only access to public keys | | |||
| **admin:org_hook** | Grants full access to organizational-level hooks | | |||
| **admin:user_hook** | Grants full access to user-level hooks | | |||
| **notification** | Grants full access to notifications | | |||
| **user** | Grants full access to user profile info | | |||
| **read:user** | Grants read access to user's profile | | |||
| **user:email** | Grants read access to user's email addresses | | |||
| **user:follow** | Grants access to follow/un-follow a user | | |||
| **delete_repo** | Grants access to delete repositories as an admin | | |||
| **package** | Grants full access to hosted packages | | |||
| **write:package** | Grants read/write access to packages | | |||
| **read:package** | Grants read access to packages | | |||
| **delete:package** | Grants delete access to packages | | |||
| **admin:gpg_key** | Grants full access for managing GPG keys | | |||
| **write:gpg_key** | Grants read/write access to GPG keys | | |||
| **read:gpg_key** | Grants read-only access to GPG keys | | |||
| **admin:application** | Grants full access to manage applications | | |||
| **write:application** | Grants read/write access for managing applications | | |||
| **read:application** | Grants read access for managing applications | | |||
| **sudo** | Allows to perform actions as the site admin. | | |||
Gitea supports scoped access tokens, which allow users the ability to restrict tokens to operate only on selected url routes. Scopes are grouped by high-level API routes, and further refined to the following: | |||
- `read`: `GET` routes | |||
- `write`: `POST`, `PUT`, `PATCH`, and `DELETE` routes (in addition to `GET`) | |||
Gitea token scopes are as follows: | |||
| Name | Description | | |||
| ---- |--------------------------------------------------------------------------------------------------------------------------------------------------| | |||
| **(no scope)** | Not supported. A scope is required even for public repositories. | | |||
| **activitypub** | `activitypub` API routes: ActivityPub related operations. | | |||
| **read:activitypub** | Grants read access for ActivityPub operations. | | |||
| **write:activitypub** | Grants read/write/delete access for ActivityPub operations. | | |||
| **admin** | `/admin/*` API routes: Site-wide administrative operations (hidden for non-admin accounts). | | |||
| **read:admin** | Grants read access for admin operations, such as getting cron jobs or registered user emails. | | |||
| **write:admin** | Grants read/write/delete access for admin operations, such as running cron jobs or updating user accounts. | | | |||
| **issue** | `issues/*`, `labels/*`, `milestones/*` API routes: Issue-related operations. | | |||
| **read:issue** | Grants read access for issues operations, such as getting issue comments, issue attachments, and milestones. | | |||
| **write:issue** | Grants read/write/delete access for issues operations, such as posting or editing an issue comment or attachment, and updating milestones. | | |||
| **misc** | miscellaneous and settings top-level API routes. | | |||
| **read:misc** | Grants read access to miscellaneous operations, such as getting label and gitignore templates. | | |||
| **write:misc** | Grants read/write/delete access to miscellaneous operations, such as markup utility operations. | | |||
| **notification** | `notification/*` API routes: user notification operations. | | |||
| **read:notification** | Grants read access to user notifications, such as which notifications users are subscribed to and read new notifications. | | |||
| **write:notification** | Grants read/write/delete access to user notifications, such as marking notifications as read. | | |||
| **organization** | `orgs/*` and `teams/*` API routes: Organization and team management operations. | | |||
| **read:organization** | Grants read access to org and team status, such as listing all orgs a user has visibility to, teams, and team members. | | |||
| **write:organization** | Grants read/write/delete access to org and team status, such as creating and updating teams and updating org settings. | | |||
| **package** | `/packages/*` API routes: Packages operations | | |||
| **read:package** | Grants read access to package operations, such as reading and downloading available packages. | | |||
| **write:package** | Grants read/write/delete access to package operations. Currently the same as `read:package`. | | |||
| **repository** | `/repos/*` API routes except `/repos/issues/*`: Repository file, pull-request, and release operations. | | |||
| **read:repository** | Grants read access to repository operations, such as getting repository files, releases, collaborators. | | |||
| **write:repository** | Grants read/write/delete access to repository operations, such as getting updating repository files, creating pull requests, updating collaborators. | | |||
| **user** | `/user/*` and `/users/*` API routes: User-related operations. | | |||
| **read:user** | Grants read access to user operations, such as getting user repo subscriptions and user settings. | | |||
| **write:user** | Grants read/write/delete access to user operations, such as updating user repo subscriptions, followed users, and user settings. | | |||
## Client types | |||
@@ -112,6 +112,15 @@ func NewAccessToken(t *AccessToken) error { | |||
return err | |||
} | |||
// DisplayPublicOnly whether to display this as a public-only token. | |||
func (t *AccessToken) DisplayPublicOnly() bool { | |||
publicOnly, err := t.Scope.PublicOnly() | |||
if err != nil { | |||
return false | |||
} | |||
return publicOnly | |||
} | |||
func getAccessTokenIDFromCache(token string) int64 { | |||
if successfulAccessTokenCache == nil { | |||
return 0 |
@@ -6,113 +6,122 @@ package auth | |||
import ( | |||
"fmt" | |||
"strings" | |||
"code.gitea.io/gitea/models/perm" | |||
) | |||
// AccessTokenScope represents the scope for an access token. | |||
type AccessTokenScope string | |||
// AccessTokenScopeCategory represents the scope category for an access token | |||
type AccessTokenScopeCategory int | |||
const ( | |||
AccessTokenScopeAll AccessTokenScope = "all" | |||
AccessTokenScopeCategoryActivityPub = iota | |||
AccessTokenScopeCategoryAdmin | |||
AccessTokenScopeCategoryMisc | |||
AccessTokenScopeCategoryNotification | |||
AccessTokenScopeCategoryOrganization | |||
AccessTokenScopeCategoryPackage | |||
AccessTokenScopeCategoryIssue | |||
AccessTokenScopeCategoryRepository | |||
AccessTokenScopeCategoryUser | |||
) | |||
AccessTokenScopeRepo AccessTokenScope = "repo" | |||
AccessTokenScopeRepoStatus AccessTokenScope = "repo:status" | |||
AccessTokenScopePublicRepo AccessTokenScope = "public_repo" | |||
// AllAccessTokenScopeCategories contains all access token scope categories | |||
var AllAccessTokenScopeCategories = []AccessTokenScopeCategory{ | |||
AccessTokenScopeCategoryActivityPub, | |||
AccessTokenScopeCategoryAdmin, | |||
AccessTokenScopeCategoryMisc, | |||
AccessTokenScopeCategoryNotification, | |||
AccessTokenScopeCategoryOrganization, | |||
AccessTokenScopeCategoryPackage, | |||
AccessTokenScopeCategoryIssue, | |||
AccessTokenScopeCategoryRepository, | |||
AccessTokenScopeCategoryUser, | |||
} | |||
AccessTokenScopeAdminOrg AccessTokenScope = "admin:org" | |||
AccessTokenScopeWriteOrg AccessTokenScope = "write:org" | |||
AccessTokenScopeReadOrg AccessTokenScope = "read:org" | |||
// AccessTokenScopeLevel represents the access levels without a given scope category | |||
type AccessTokenScopeLevel int | |||
AccessTokenScopeAdminPublicKey AccessTokenScope = "admin:public_key" | |||
AccessTokenScopeWritePublicKey AccessTokenScope = "write:public_key" | |||
AccessTokenScopeReadPublicKey AccessTokenScope = "read:public_key" | |||
const ( | |||
NoAccess AccessTokenScopeLevel = iota | |||
Read | |||
Write | |||
) | |||
// AccessTokenScope represents the scope for an access token. | |||
type AccessTokenScope string | |||
AccessTokenScopeAdminRepoHook AccessTokenScope = "admin:repo_hook" | |||
AccessTokenScopeWriteRepoHook AccessTokenScope = "write:repo_hook" | |||
AccessTokenScopeReadRepoHook AccessTokenScope = "read:repo_hook" | |||
// for all categories, write implies read | |||
const ( | |||
AccessTokenScopeAll AccessTokenScope = "all" | |||
AccessTokenScopePublicOnly AccessTokenScope = "public-only" // limited to public orgs/repos | |||
AccessTokenScopeAdminOrgHook AccessTokenScope = "admin:org_hook" | |||
AccessTokenScopeReadActivityPub AccessTokenScope = "read:activitypub" | |||
AccessTokenScopeWriteActivityPub AccessTokenScope = "write:activitypub" | |||
AccessTokenScopeAdminUserHook AccessTokenScope = "admin:user_hook" | |||
AccessTokenScopeReadAdmin AccessTokenScope = "read:admin" | |||
AccessTokenScopeWriteAdmin AccessTokenScope = "write:admin" | |||
AccessTokenScopeNotification AccessTokenScope = "notification" | |||
AccessTokenScopeReadMisc AccessTokenScope = "read:misc" | |||
AccessTokenScopeWriteMisc AccessTokenScope = "write:misc" | |||
AccessTokenScopeUser AccessTokenScope = "user" | |||
AccessTokenScopeReadUser AccessTokenScope = "read:user" | |||
AccessTokenScopeUserEmail AccessTokenScope = "user:email" | |||
AccessTokenScopeUserFollow AccessTokenScope = "user:follow" | |||
AccessTokenScopeReadNotification AccessTokenScope = "read:notification" | |||
AccessTokenScopeWriteNotification AccessTokenScope = "write:notification" | |||
AccessTokenScopeDeleteRepo AccessTokenScope = "delete_repo" | |||
AccessTokenScopeReadOrganization AccessTokenScope = "read:organization" | |||
AccessTokenScopeWriteOrganization AccessTokenScope = "write:organization" | |||
AccessTokenScopePackage AccessTokenScope = "package" | |||
AccessTokenScopeWritePackage AccessTokenScope = "write:package" | |||
AccessTokenScopeReadPackage AccessTokenScope = "read:package" | |||
AccessTokenScopeDeletePackage AccessTokenScope = "delete:package" | |||
AccessTokenScopeReadPackage AccessTokenScope = "read:package" | |||
AccessTokenScopeWritePackage AccessTokenScope = "write:package" | |||
AccessTokenScopeAdminGPGKey AccessTokenScope = "admin:gpg_key" | |||
AccessTokenScopeWriteGPGKey AccessTokenScope = "write:gpg_key" | |||
AccessTokenScopeReadGPGKey AccessTokenScope = "read:gpg_key" | |||
AccessTokenScopeReadIssue AccessTokenScope = "read:issue" | |||
AccessTokenScopeWriteIssue AccessTokenScope = "write:issue" | |||
AccessTokenScopeAdminApplication AccessTokenScope = "admin:application" | |||
AccessTokenScopeWriteApplication AccessTokenScope = "write:application" | |||
AccessTokenScopeReadApplication AccessTokenScope = "read:application" | |||
AccessTokenScopeReadRepository AccessTokenScope = "read:repository" | |||
AccessTokenScopeWriteRepository AccessTokenScope = "write:repository" | |||
AccessTokenScopeSudo AccessTokenScope = "sudo" | |||
AccessTokenScopeReadUser AccessTokenScope = "read:user" | |||
AccessTokenScopeWriteUser AccessTokenScope = "write:user" | |||
) | |||
// AccessTokenScopeBitmap represents a bitmap of access token scopes. | |||
type AccessTokenScopeBitmap uint64 | |||
// accessTokenScopeBitmap represents a bitmap of access token scopes. | |||
type accessTokenScopeBitmap uint64 | |||
// Bitmap of each scope, including the child scopes. | |||
const ( | |||
// AccessTokenScopeAllBits is the bitmap of all access token scopes, except `sudo`. | |||
AccessTokenScopeAllBits AccessTokenScopeBitmap = AccessTokenScopeRepoBits | | |||
AccessTokenScopeAdminOrgBits | AccessTokenScopeAdminPublicKeyBits | AccessTokenScopeAdminOrgHookBits | AccessTokenScopeAdminUserHookBits | | |||
AccessTokenScopeNotificationBits | AccessTokenScopeUserBits | AccessTokenScopeDeleteRepoBits | | |||
AccessTokenScopePackageBits | AccessTokenScopeAdminGPGKeyBits | AccessTokenScopeAdminApplicationBits | |||
AccessTokenScopeRepoBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeRepoStatusBits | AccessTokenScopePublicRepoBits | AccessTokenScopeAdminRepoHookBits | |||
AccessTokenScopeRepoStatusBits AccessTokenScopeBitmap = 1 << iota | |||
AccessTokenScopePublicRepoBits AccessTokenScopeBitmap = 1 << iota | |||
AccessTokenScopeAdminOrgBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeWriteOrgBits | |||
AccessTokenScopeWriteOrgBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeReadOrgBits | |||
AccessTokenScopeReadOrgBits AccessTokenScopeBitmap = 1 << iota | |||
AccessTokenScopeAdminPublicKeyBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeWritePublicKeyBits | |||
AccessTokenScopeWritePublicKeyBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeReadPublicKeyBits | |||
AccessTokenScopeReadPublicKeyBits AccessTokenScopeBitmap = 1 << iota | |||
// AccessTokenScopeAllBits is the bitmap of all access token scopes | |||
accessTokenScopeAllBits accessTokenScopeBitmap = accessTokenScopeWriteActivityPubBits | | |||
accessTokenScopeWriteAdminBits | accessTokenScopeWriteMiscBits | accessTokenScopeWriteNotificationBits | | |||
accessTokenScopeWriteOrganizationBits | accessTokenScopeWritePackageBits | accessTokenScopeWriteIssueBits | | |||
accessTokenScopeWriteRepositoryBits | accessTokenScopeWriteUserBits | |||
AccessTokenScopeAdminRepoHookBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeWriteRepoHookBits | |||
AccessTokenScopeWriteRepoHookBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeReadRepoHookBits | |||
AccessTokenScopeReadRepoHookBits AccessTokenScopeBitmap = 1 << iota | |||
accessTokenScopePublicOnlyBits accessTokenScopeBitmap = 1 << iota | |||
AccessTokenScopeAdminOrgHookBits AccessTokenScopeBitmap = 1 << iota | |||
accessTokenScopeReadActivityPubBits accessTokenScopeBitmap = 1 << iota | |||
accessTokenScopeWriteActivityPubBits accessTokenScopeBitmap = 1<<iota | accessTokenScopeReadActivityPubBits | |||
AccessTokenScopeAdminUserHookBits AccessTokenScopeBitmap = 1 << iota | |||
accessTokenScopeReadAdminBits accessTokenScopeBitmap = 1 << iota | |||
accessTokenScopeWriteAdminBits accessTokenScopeBitmap = 1<<iota | accessTokenScopeReadAdminBits | |||
AccessTokenScopeNotificationBits AccessTokenScopeBitmap = 1 << iota | |||
accessTokenScopeReadMiscBits accessTokenScopeBitmap = 1 << iota | |||
accessTokenScopeWriteMiscBits accessTokenScopeBitmap = 1<<iota | accessTokenScopeReadMiscBits | |||
AccessTokenScopeUserBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeReadUserBits | AccessTokenScopeUserEmailBits | AccessTokenScopeUserFollowBits | |||
AccessTokenScopeReadUserBits AccessTokenScopeBitmap = 1 << iota | |||
AccessTokenScopeUserEmailBits AccessTokenScopeBitmap = 1 << iota | |||
AccessTokenScopeUserFollowBits AccessTokenScopeBitmap = 1 << iota | |||
accessTokenScopeReadNotificationBits accessTokenScopeBitmap = 1 << iota | |||
accessTokenScopeWriteNotificationBits accessTokenScopeBitmap = 1<<iota | accessTokenScopeReadNotificationBits | |||
AccessTokenScopeDeleteRepoBits AccessTokenScopeBitmap = 1 << iota | |||
accessTokenScopeReadOrganizationBits accessTokenScopeBitmap = 1 << iota | |||
accessTokenScopeWriteOrganizationBits accessTokenScopeBitmap = 1<<iota | accessTokenScopeReadOrganizationBits | |||
AccessTokenScopePackageBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeWritePackageBits | AccessTokenScopeDeletePackageBits | |||
AccessTokenScopeWritePackageBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeReadPackageBits | |||
AccessTokenScopeReadPackageBits AccessTokenScopeBitmap = 1 << iota | |||
AccessTokenScopeDeletePackageBits AccessTokenScopeBitmap = 1 << iota | |||
accessTokenScopeReadPackageBits accessTokenScopeBitmap = 1 << iota | |||
accessTokenScopeWritePackageBits accessTokenScopeBitmap = 1<<iota | accessTokenScopeReadPackageBits | |||
AccessTokenScopeAdminGPGKeyBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeWriteGPGKeyBits | |||
AccessTokenScopeWriteGPGKeyBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeReadGPGKeyBits | |||
AccessTokenScopeReadGPGKeyBits AccessTokenScopeBitmap = 1 << iota | |||
accessTokenScopeReadIssueBits accessTokenScopeBitmap = 1 << iota | |||
accessTokenScopeWriteIssueBits accessTokenScopeBitmap = 1<<iota | accessTokenScopeReadIssueBits | |||
AccessTokenScopeAdminApplicationBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeWriteApplicationBits | |||
AccessTokenScopeWriteApplicationBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeReadApplicationBits | |||
AccessTokenScopeReadApplicationBits AccessTokenScopeBitmap = 1 << iota | |||
accessTokenScopeReadRepositoryBits accessTokenScopeBitmap = 1 << iota | |||
accessTokenScopeWriteRepositoryBits accessTokenScopeBitmap = 1<<iota | accessTokenScopeReadRepositoryBits | |||
AccessTokenScopeSudoBits AccessTokenScopeBitmap = 1 << iota | |||
accessTokenScopeReadUserBits accessTokenScopeBitmap = 1 << iota | |||
accessTokenScopeWriteUserBits accessTokenScopeBitmap = 1<<iota | accessTokenScopeReadUserBits | |||
// The current implementation only supports up to 64 token scopes. | |||
// If we need to support > 64 scopes, | |||
@@ -120,61 +129,110 @@ const ( | |||
) | |||
// allAccessTokenScopes contains all access token scopes. | |||
// The order is important: parent scope must precedes child scopes. | |||
// The order is important: parent scope must precede child scopes. | |||
var allAccessTokenScopes = []AccessTokenScope{ | |||
AccessTokenScopeRepo, AccessTokenScopeRepoStatus, AccessTokenScopePublicRepo, | |||
AccessTokenScopeAdminOrg, AccessTokenScopeWriteOrg, AccessTokenScopeReadOrg, | |||
AccessTokenScopeAdminPublicKey, AccessTokenScopeWritePublicKey, AccessTokenScopeReadPublicKey, | |||
AccessTokenScopeAdminRepoHook, AccessTokenScopeWriteRepoHook, AccessTokenScopeReadRepoHook, | |||
AccessTokenScopeAdminOrgHook, | |||
AccessTokenScopeAdminUserHook, | |||
AccessTokenScopeNotification, | |||
AccessTokenScopeUser, AccessTokenScopeReadUser, AccessTokenScopeUserEmail, AccessTokenScopeUserFollow, | |||
AccessTokenScopeDeleteRepo, | |||
AccessTokenScopePackage, AccessTokenScopeWritePackage, AccessTokenScopeReadPackage, AccessTokenScopeDeletePackage, | |||
AccessTokenScopeAdminGPGKey, AccessTokenScopeWriteGPGKey, AccessTokenScopeReadGPGKey, | |||
AccessTokenScopeAdminApplication, AccessTokenScopeWriteApplication, AccessTokenScopeReadApplication, | |||
AccessTokenScopeSudo, | |||
AccessTokenScopePublicOnly, | |||
AccessTokenScopeWriteActivityPub, AccessTokenScopeReadActivityPub, | |||
AccessTokenScopeWriteAdmin, AccessTokenScopeReadAdmin, | |||
AccessTokenScopeWriteMisc, AccessTokenScopeReadMisc, | |||
AccessTokenScopeWriteNotification, AccessTokenScopeReadNotification, | |||
AccessTokenScopeWriteOrganization, AccessTokenScopeReadOrganization, | |||
AccessTokenScopeWritePackage, AccessTokenScopeReadPackage, | |||
AccessTokenScopeWriteIssue, AccessTokenScopeReadIssue, | |||
AccessTokenScopeWriteRepository, AccessTokenScopeReadRepository, | |||
AccessTokenScopeWriteUser, AccessTokenScopeReadUser, | |||
} | |||
// allAccessTokenScopeBits contains all access token scopes. | |||
var allAccessTokenScopeBits = map[AccessTokenScope]AccessTokenScopeBitmap{ | |||
AccessTokenScopeRepo: AccessTokenScopeRepoBits, | |||
AccessTokenScopeRepoStatus: AccessTokenScopeRepoStatusBits, | |||
AccessTokenScopePublicRepo: AccessTokenScopePublicRepoBits, | |||
AccessTokenScopeAdminOrg: AccessTokenScopeAdminOrgBits, | |||
AccessTokenScopeWriteOrg: AccessTokenScopeWriteOrgBits, | |||
AccessTokenScopeReadOrg: AccessTokenScopeReadOrgBits, | |||
AccessTokenScopeAdminPublicKey: AccessTokenScopeAdminPublicKeyBits, | |||
AccessTokenScopeWritePublicKey: AccessTokenScopeWritePublicKeyBits, | |||
AccessTokenScopeReadPublicKey: AccessTokenScopeReadPublicKeyBits, | |||
AccessTokenScopeAdminRepoHook: AccessTokenScopeAdminRepoHookBits, | |||
AccessTokenScopeWriteRepoHook: AccessTokenScopeWriteRepoHookBits, | |||
AccessTokenScopeReadRepoHook: AccessTokenScopeReadRepoHookBits, | |||
AccessTokenScopeAdminOrgHook: AccessTokenScopeAdminOrgHookBits, | |||
AccessTokenScopeAdminUserHook: AccessTokenScopeAdminUserHookBits, | |||
AccessTokenScopeNotification: AccessTokenScopeNotificationBits, | |||
AccessTokenScopeUser: AccessTokenScopeUserBits, | |||
AccessTokenScopeReadUser: AccessTokenScopeReadUserBits, | |||
AccessTokenScopeUserEmail: AccessTokenScopeUserEmailBits, | |||
AccessTokenScopeUserFollow: AccessTokenScopeUserFollowBits, | |||
AccessTokenScopeDeleteRepo: AccessTokenScopeDeleteRepoBits, | |||
AccessTokenScopePackage: AccessTokenScopePackageBits, | |||
AccessTokenScopeWritePackage: AccessTokenScopeWritePackageBits, | |||
AccessTokenScopeReadPackage: AccessTokenScopeReadPackageBits, | |||
AccessTokenScopeDeletePackage: AccessTokenScopeDeletePackageBits, | |||
AccessTokenScopeAdminGPGKey: AccessTokenScopeAdminGPGKeyBits, | |||
AccessTokenScopeWriteGPGKey: AccessTokenScopeWriteGPGKeyBits, | |||
AccessTokenScopeReadGPGKey: AccessTokenScopeReadGPGKeyBits, | |||
AccessTokenScopeAdminApplication: AccessTokenScopeAdminApplicationBits, | |||
AccessTokenScopeWriteApplication: AccessTokenScopeWriteApplicationBits, | |||
AccessTokenScopeReadApplication: AccessTokenScopeReadApplicationBits, | |||
AccessTokenScopeSudo: AccessTokenScopeSudoBits, | |||
var allAccessTokenScopeBits = map[AccessTokenScope]accessTokenScopeBitmap{ | |||
AccessTokenScopeAll: accessTokenScopeAllBits, | |||
AccessTokenScopePublicOnly: accessTokenScopePublicOnlyBits, | |||
AccessTokenScopeReadActivityPub: accessTokenScopeReadActivityPubBits, | |||
AccessTokenScopeWriteActivityPub: accessTokenScopeWriteActivityPubBits, | |||
AccessTokenScopeReadAdmin: accessTokenScopeReadAdminBits, | |||
AccessTokenScopeWriteAdmin: accessTokenScopeWriteAdminBits, | |||
AccessTokenScopeReadMisc: accessTokenScopeReadMiscBits, | |||
AccessTokenScopeWriteMisc: accessTokenScopeWriteMiscBits, | |||
AccessTokenScopeReadNotification: accessTokenScopeReadNotificationBits, | |||
AccessTokenScopeWriteNotification: accessTokenScopeWriteNotificationBits, | |||
AccessTokenScopeReadOrganization: accessTokenScopeReadOrganizationBits, | |||
AccessTokenScopeWriteOrganization: accessTokenScopeWriteOrganizationBits, | |||
AccessTokenScopeReadPackage: accessTokenScopeReadPackageBits, | |||
AccessTokenScopeWritePackage: accessTokenScopeWritePackageBits, | |||
AccessTokenScopeReadIssue: accessTokenScopeReadIssueBits, | |||
AccessTokenScopeWriteIssue: accessTokenScopeWriteIssueBits, | |||
AccessTokenScopeReadRepository: accessTokenScopeReadRepositoryBits, | |||
AccessTokenScopeWriteRepository: accessTokenScopeWriteRepositoryBits, | |||
AccessTokenScopeReadUser: accessTokenScopeReadUserBits, | |||
AccessTokenScopeWriteUser: accessTokenScopeWriteUserBits, | |||
} | |||
// readAccessTokenScopes maps a scope category to the read permission scope | |||
var accessTokenScopes = map[AccessTokenScopeLevel]map[AccessTokenScopeCategory]AccessTokenScope{ | |||
Read: { | |||
AccessTokenScopeCategoryActivityPub: AccessTokenScopeReadActivityPub, | |||
AccessTokenScopeCategoryAdmin: AccessTokenScopeReadAdmin, | |||
AccessTokenScopeCategoryMisc: AccessTokenScopeReadMisc, | |||
AccessTokenScopeCategoryNotification: AccessTokenScopeReadNotification, | |||
AccessTokenScopeCategoryOrganization: AccessTokenScopeReadOrganization, | |||
AccessTokenScopeCategoryPackage: AccessTokenScopeReadPackage, | |||
AccessTokenScopeCategoryIssue: AccessTokenScopeReadIssue, | |||
AccessTokenScopeCategoryRepository: AccessTokenScopeReadRepository, | |||
AccessTokenScopeCategoryUser: AccessTokenScopeReadUser, | |||
}, | |||
Write: { | |||
AccessTokenScopeCategoryActivityPub: AccessTokenScopeWriteActivityPub, | |||
AccessTokenScopeCategoryAdmin: AccessTokenScopeWriteAdmin, | |||
AccessTokenScopeCategoryMisc: AccessTokenScopeWriteMisc, | |||
AccessTokenScopeCategoryNotification: AccessTokenScopeWriteNotification, | |||
AccessTokenScopeCategoryOrganization: AccessTokenScopeWriteOrganization, | |||
AccessTokenScopeCategoryPackage: AccessTokenScopeWritePackage, | |||
AccessTokenScopeCategoryIssue: AccessTokenScopeWriteIssue, | |||
AccessTokenScopeCategoryRepository: AccessTokenScopeWriteRepository, | |||
AccessTokenScopeCategoryUser: AccessTokenScopeWriteUser, | |||
}, | |||
} | |||
// GetRequiredScopes gets the specific scopes for a given level and categories | |||
func GetRequiredScopes(level AccessTokenScopeLevel, scopeCategories ...AccessTokenScopeCategory) []AccessTokenScope { | |||
scopes := make([]AccessTokenScope, 0, len(scopeCategories)) | |||
for _, cat := range scopeCategories { | |||
scopes = append(scopes, accessTokenScopes[level][cat]) | |||
} | |||
return scopes | |||
} | |||
// Parse parses the scope string into a bitmap, thus removing possible duplicates. | |||
func (s AccessTokenScope) Parse() (AccessTokenScopeBitmap, error) { | |||
var bitmap AccessTokenScopeBitmap | |||
// ContainsCategory checks if a list of categories contains a specific category | |||
func ContainsCategory(categories []AccessTokenScopeCategory, category AccessTokenScopeCategory) bool { | |||
for _, c := range categories { | |||
if c == category { | |||
return true | |||
} | |||
} | |||
return false | |||
} | |||
// GetScopeLevelFromAccessMode converts permission access mode to scope level | |||
func GetScopeLevelFromAccessMode(mode perm.AccessMode) AccessTokenScopeLevel { | |||
switch mode { | |||
case perm.AccessModeNone: | |||
return NoAccess | |||
case perm.AccessModeRead: | |||
return Read | |||
case perm.AccessModeWrite: | |||
return Write | |||
case perm.AccessModeAdmin: | |||
return Write | |||
case perm.AccessModeOwner: | |||
return Write | |||
default: | |||
return NoAccess | |||
} | |||
} | |||
// parse the scope string into a bitmap, thus removing possible duplicates. | |||
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) | |||
@@ -196,7 +254,7 @@ func (s AccessTokenScope) Parse() (AccessTokenScopeBitmap, error) { | |||
continue | |||
} | |||
if singleScope == AccessTokenScopeAll { | |||
bitmap |= AccessTokenScopeAllBits | |||
bitmap |= accessTokenScopeAllBits | |||
continue | |||
} | |||
@@ -217,26 +275,42 @@ func (s AccessTokenScope) StringSlice() []string { | |||
// Normalize returns a normalized scope string without any duplicates. | |||
func (s AccessTokenScope) Normalize() (AccessTokenScope, error) { | |||
bitmap, err := s.Parse() | |||
bitmap, err := s.parse() | |||
if err != nil { | |||
return "", err | |||
} | |||
return bitmap.ToScope(), nil | |||
return bitmap.toScope(), nil | |||
} | |||
// HasScope returns true if the string has the given scope | |||
func (s AccessTokenScope) HasScope(scope AccessTokenScope) (bool, error) { | |||
bitmap, err := s.Parse() | |||
// PublicOnly checks if this token scope is limited to public resources | |||
func (s AccessTokenScope) PublicOnly() (bool, error) { | |||
bitmap, err := s.parse() | |||
if err != nil { | |||
return false, err | |||
} | |||
return bitmap.HasScope(scope) | |||
return bitmap.hasScope(AccessTokenScopePublicOnly) | |||
} | |||
// HasScope returns true if the string has the given scope | |||
func (bitmap AccessTokenScopeBitmap) HasScope(scope AccessTokenScope) (bool, error) { | |||
func (s AccessTokenScope) HasScope(scopes ...AccessTokenScope) (bool, error) { | |||
bitmap, err := s.parse() | |||
if err != nil { | |||
return false, err | |||
} | |||
for _, s := range scopes { | |||
if has, err := bitmap.hasScope(s); !has || err != nil { | |||
return has, err | |||
} | |||
} | |||
return true, nil | |||
} | |||
// hasScope returns true if the string has the given scope | |||
func (bitmap accessTokenScopeBitmap) hasScope(scope AccessTokenScope) (bool, error) { | |||
expectedBits, ok := allAccessTokenScopeBits[scope] | |||
if !ok { | |||
return false, fmt.Errorf("invalid access token scope: %s", scope) | |||
@@ -245,17 +319,17 @@ func (bitmap AccessTokenScopeBitmap) HasScope(scope AccessTokenScope) (bool, err | |||
return bitmap&expectedBits == expectedBits, nil | |||
} | |||
// ToScope returns a normalized scope string without any duplicates. | |||
func (bitmap AccessTokenScopeBitmap) ToScope() AccessTokenScope { | |||
// toScope returns a normalized scope string without any duplicates. | |||
func (bitmap accessTokenScopeBitmap) toScope() AccessTokenScope { | |||
var scopes []string | |||
// iterate over all scopes, and reconstruct the bitmap | |||
// if the reconstructed bitmap doesn't change, then the scope is already included | |||
var reconstruct AccessTokenScopeBitmap | |||
var reconstruct accessTokenScopeBitmap | |||
for _, singleScope := range allAccessTokenScopes { | |||
// no need for error checking here, since we know the scope is valid | |||
if ok, _ := bitmap.HasScope(singleScope); ok { | |||
if ok, _ := bitmap.hasScope(singleScope); ok { | |||
current := reconstruct | allAccessTokenScopeBits[singleScope] | |||
if current == reconstruct { | |||
continue | |||
@@ -269,7 +343,7 @@ func (bitmap AccessTokenScopeBitmap) ToScope() AccessTokenScope { | |||
scope := AccessTokenScope(strings.Join(scopes, ",")) | |||
scope = AccessTokenScope(strings.ReplaceAll( | |||
string(scope), | |||
"repo,admin:org,admin:public_key,admin:org_hook,admin:user_hook,notification,user,delete_repo,package,admin:gpg_key,admin:application", | |||
"write:activitypub,write:admin,write:misc,write:notification,write:organization,write:package,write:issue,write:repository,write:user", | |||
"all", | |||
)) | |||
return scope |
@@ -4,44 +4,35 @@ | |||
package auth | |||
import ( | |||
"fmt" | |||
"testing" | |||
"github.com/stretchr/testify/assert" | |||
) | |||
type scopeTestNormalize struct { | |||
in AccessTokenScope | |||
out AccessTokenScope | |||
err error | |||
} | |||
func TestAccessTokenScope_Normalize(t *testing.T) { | |||
tests := []struct { | |||
in AccessTokenScope | |||
out AccessTokenScope | |||
err error | |||
}{ | |||
tests := []scopeTestNormalize{ | |||
{"", "", nil}, | |||
{"repo", "repo", nil}, | |||
{"repo,repo:status", "repo", nil}, | |||
{"repo,public_repo", "repo", nil}, | |||
{"admin:public_key,write:public_key", "admin:public_key", nil}, | |||
{"admin:public_key,read:public_key", "admin:public_key", nil}, | |||
{"write:public_key,read:public_key", "write:public_key", nil}, // read is include in write | |||
{"admin:repo_hook,write:repo_hook", "admin:repo_hook", nil}, | |||
{"admin:repo_hook,read:repo_hook", "admin:repo_hook", nil}, | |||
{"repo,admin:repo_hook,read:repo_hook", "repo", nil}, // admin:repo_hook is a child scope of repo | |||
{"repo,read:repo_hook", "repo", nil}, // read:repo_hook is a child scope of repo | |||
{"user", "user", nil}, | |||
{"user,read:user", "user", nil}, | |||
{"user,admin:org,write:org", "admin:org,user", nil}, | |||
{"admin:org,write:org,user", "admin:org,user", nil}, | |||
{"package", "package", nil}, | |||
{"package,write:package", "package", nil}, | |||
{"package,write:package,delete:package", "package", nil}, | |||
{"write:package,read:package", "write:package", nil}, // read is include in write | |||
{"write:package,delete:package", "write:package,delete:package", nil}, // write and delete are not include in each other | |||
{"admin:gpg_key", "admin:gpg_key", nil}, | |||
{"admin:gpg_key,write:gpg_key", "admin:gpg_key", nil}, | |||
{"admin:gpg_key,write:gpg_key,user", "user,admin:gpg_key", nil}, | |||
{"admin:application,write:application,user", "user,admin:application", nil}, | |||
{"write:misc,write:notification,read:package,write:notification,public-only", "public-only,write:misc,write:notification,read:package", nil}, | |||
{"all", "all", nil}, | |||
{"repo,admin:org,admin:public_key,admin:repo_hook,admin:org_hook,admin:user_hook,notification,user,delete_repo,package,admin:gpg_key,admin:application", "all", nil}, | |||
{"repo,admin:org,admin:public_key,admin:repo_hook,admin:org_hook,admin:user_hook,notification,user,delete_repo,package,admin:gpg_key,admin:application,sudo", "all,sudo", nil}, | |||
{"write:activitypub,write:admin,write:misc,write:notification,write:organization,write:package,write:issue,write:repository,write:user", "all", nil}, | |||
{"write:activitypub,write:admin,write:misc,write:notification,write:organization,write:package,write:issue,write:repository,write:user,public-only", "public-only,all", nil}, | |||
} | |||
for _, scope := range []string{"activitypub", "admin", "misc", "notification", "organization", "package", "issue", "repository", "user"} { | |||
tests = append(tests, | |||
scopeTestNormalize{AccessTokenScope(fmt.Sprintf("read:%s", scope)), AccessTokenScope(fmt.Sprintf("read:%s", scope)), nil}, | |||
scopeTestNormalize{AccessTokenScope(fmt.Sprintf("write:%s", scope)), AccessTokenScope(fmt.Sprintf("write:%s", scope)), nil}, | |||
scopeTestNormalize{AccessTokenScope(fmt.Sprintf("write:%[1]s,read:%[1]s", scope)), AccessTokenScope(fmt.Sprintf("write:%s", scope)), nil}, | |||
scopeTestNormalize{AccessTokenScope(fmt.Sprintf("read:%[1]s,write:%[1]s", scope)), AccessTokenScope(fmt.Sprintf("write:%s", scope)), nil}, | |||
scopeTestNormalize{AccessTokenScope(fmt.Sprintf("read:%[1]s,write:%[1]s,write:%[1]s", scope)), AccessTokenScope(fmt.Sprintf("write:%s", scope)), nil}, | |||
) | |||
} | |||
for _, test := range tests { | |||
@@ -53,31 +44,46 @@ func TestAccessTokenScope_Normalize(t *testing.T) { | |||
} | |||
} | |||
type scopeTestHasScope struct { | |||
in AccessTokenScope | |||
scope AccessTokenScope | |||
out bool | |||
err error | |||
} | |||
func TestAccessTokenScope_HasScope(t *testing.T) { | |||
tests := []struct { | |||
in AccessTokenScope | |||
scope AccessTokenScope | |||
out bool | |||
err error | |||
}{ | |||
{"repo", "repo", true, nil}, | |||
{"repo", "repo:status", true, nil}, | |||
{"repo", "public_repo", true, nil}, | |||
{"repo", "admin:org", false, nil}, | |||
{"repo", "admin:public_key", false, nil}, | |||
{"repo:status", "repo", false, nil}, | |||
{"repo:status", "public_repo", false, nil}, | |||
{"admin:org", "write:org", true, nil}, | |||
{"admin:org", "read:org", true, nil}, | |||
{"admin:org", "admin:org", true, nil}, | |||
{"user", "read:user", true, nil}, | |||
{"package", "write:package", true, nil}, | |||
tests := []scopeTestHasScope{ | |||
{"read:admin", "write:package", false, nil}, | |||
{"all", "write:package", true, nil}, | |||
{"write:package", "all", false, nil}, | |||
{"public-only", "read:issue", false, nil}, | |||
} | |||
for _, scope := range []string{"activitypub", "admin", "misc", "notification", "organization", "package", "issue", "repository", "user"} { | |||
tests = append(tests, | |||
scopeTestHasScope{ | |||
AccessTokenScope(fmt.Sprintf("read:%s", scope)), | |||
AccessTokenScope(fmt.Sprintf("read:%s", scope)), true, nil, | |||
}, | |||
scopeTestHasScope{ | |||
AccessTokenScope(fmt.Sprintf("write:%s", scope)), | |||
AccessTokenScope(fmt.Sprintf("write:%s", scope)), true, nil, | |||
}, | |||
scopeTestHasScope{ | |||
AccessTokenScope(fmt.Sprintf("write:%s", scope)), | |||
AccessTokenScope(fmt.Sprintf("read:%s", scope)), true, nil, | |||
}, | |||
scopeTestHasScope{ | |||
AccessTokenScope(fmt.Sprintf("read:%s", scope)), | |||
AccessTokenScope(fmt.Sprintf("write:%s", scope)), false, nil, | |||
}, | |||
) | |||
} | |||
for _, test := range tests { | |||
t.Run(string(test.in), func(t *testing.T) { | |||
scope, err := test.in.HasScope(test.scope) | |||
assert.Equal(t, test.out, scope) | |||
hasScope, err := test.in.HasScope(test.scope) | |||
assert.Equal(t, test.out, hasScope) | |||
assert.Equal(t, test.err, err) | |||
}) | |||
} |
@@ -495,6 +495,8 @@ var migrations = []Migration{ | |||
NewMigration("Add Actions Artifact table", v1_20.CreateActionArtifactTable), | |||
// v258 -> 259 | |||
NewMigration("Add PinOrder Column", v1_20.AddPinOrderToIssue), | |||
// v259 -> 260 | |||
NewMigration("Convert scoped access tokens", v1_20.ConvertScopedAccessTokens), | |||
} | |||
// GetCurrentDBVersion returns the current db version |
@@ -0,0 +1,14 @@ | |||
// Copyright 2023 The Gitea Authors. All rights reserved. | |||
// SPDX-License-Identifier: MIT | |||
package v1_20 //nolint | |||
import ( | |||
"testing" | |||
"code.gitea.io/gitea/models/migrations/base" | |||
) | |||
func TestMain(m *testing.M) { | |||
base.MainTest(m) | |||
} |
@@ -0,0 +1,360 @@ | |||
// Copyright 2023 The Gitea Authors. All rights reserved. | |||
// SPDX-License-Identifier: MIT | |||
package v1_20 //nolint | |||
import ( | |||
"fmt" | |||
"strings" | |||
"code.gitea.io/gitea/modules/log" | |||
"xorm.io/xorm" | |||
) | |||
// unknownAccessTokenScope represents the scope for an access token that isn't | |||
// known be an old token or a new token. | |||
type unknownAccessTokenScope string | |||
// AccessTokenScope represents the scope for an access token. | |||
type AccessTokenScope string | |||
// for all categories, write implies read | |||
const ( | |||
AccessTokenScopeAll AccessTokenScope = "all" | |||
AccessTokenScopePublicOnly AccessTokenScope = "public-only" // limited to public orgs/repos | |||
AccessTokenScopeReadActivityPub AccessTokenScope = "read:activitypub" | |||
AccessTokenScopeWriteActivityPub AccessTokenScope = "write:activitypub" | |||
AccessTokenScopeReadAdmin AccessTokenScope = "read:admin" | |||
AccessTokenScopeWriteAdmin AccessTokenScope = "write:admin" | |||
AccessTokenScopeReadMisc AccessTokenScope = "read:misc" | |||
AccessTokenScopeWriteMisc AccessTokenScope = "write:misc" | |||
AccessTokenScopeReadNotification AccessTokenScope = "read:notification" | |||
AccessTokenScopeWriteNotification AccessTokenScope = "write:notification" | |||
AccessTokenScopeReadOrganization AccessTokenScope = "read:organization" | |||
AccessTokenScopeWriteOrganization AccessTokenScope = "write:organization" | |||
AccessTokenScopeReadPackage AccessTokenScope = "read:package" | |||
AccessTokenScopeWritePackage AccessTokenScope = "write:package" | |||
AccessTokenScopeReadIssue AccessTokenScope = "read:issue" | |||
AccessTokenScopeWriteIssue AccessTokenScope = "write:issue" | |||
AccessTokenScopeReadRepository AccessTokenScope = "read:repository" | |||
AccessTokenScopeWriteRepository AccessTokenScope = "write:repository" | |||
AccessTokenScopeReadUser AccessTokenScope = "read:user" | |||
AccessTokenScopeWriteUser AccessTokenScope = "write:user" | |||
) | |||
// accessTokenScopeBitmap represents a bitmap of access token scopes. | |||
type accessTokenScopeBitmap uint64 | |||
// Bitmap of each scope, including the child scopes. | |||
const ( | |||
// AccessTokenScopeAllBits is the bitmap of all access token scopes | |||
accessTokenScopeAllBits accessTokenScopeBitmap = accessTokenScopeWriteActivityPubBits | | |||
accessTokenScopeWriteAdminBits | accessTokenScopeWriteMiscBits | accessTokenScopeWriteNotificationBits | | |||
accessTokenScopeWriteOrganizationBits | accessTokenScopeWritePackageBits | accessTokenScopeWriteIssueBits | | |||
accessTokenScopeWriteRepositoryBits | accessTokenScopeWriteUserBits | |||
accessTokenScopePublicOnlyBits accessTokenScopeBitmap = 1 << iota | |||
accessTokenScopeReadActivityPubBits accessTokenScopeBitmap = 1 << iota | |||
accessTokenScopeWriteActivityPubBits accessTokenScopeBitmap = 1<<iota | accessTokenScopeReadActivityPubBits | |||
accessTokenScopeReadAdminBits accessTokenScopeBitmap = 1 << iota | |||
accessTokenScopeWriteAdminBits accessTokenScopeBitmap = 1<<iota | accessTokenScopeReadAdminBits | |||
accessTokenScopeReadMiscBits accessTokenScopeBitmap = 1 << iota | |||
accessTokenScopeWriteMiscBits accessTokenScopeBitmap = 1<<iota | accessTokenScopeReadMiscBits | |||
accessTokenScopeReadNotificationBits accessTokenScopeBitmap = 1 << iota | |||
accessTokenScopeWriteNotificationBits accessTokenScopeBitmap = 1<<iota | accessTokenScopeReadNotificationBits | |||
accessTokenScopeReadOrganizationBits accessTokenScopeBitmap = 1 << iota | |||
accessTokenScopeWriteOrganizationBits accessTokenScopeBitmap = 1<<iota | accessTokenScopeReadOrganizationBits | |||
accessTokenScopeReadPackageBits accessTokenScopeBitmap = 1 << iota | |||
accessTokenScopeWritePackageBits accessTokenScopeBitmap = 1<<iota | accessTokenScopeReadPackageBits | |||
accessTokenScopeReadIssueBits accessTokenScopeBitmap = 1 << iota | |||
accessTokenScopeWriteIssueBits accessTokenScopeBitmap = 1<<iota | accessTokenScopeReadIssueBits | |||
accessTokenScopeReadRepositoryBits accessTokenScopeBitmap = 1 << iota | |||
accessTokenScopeWriteRepositoryBits accessTokenScopeBitmap = 1<<iota | accessTokenScopeReadRepositoryBits | |||
accessTokenScopeReadUserBits accessTokenScopeBitmap = 1 << iota | |||
accessTokenScopeWriteUserBits accessTokenScopeBitmap = 1<<iota | accessTokenScopeReadUserBits | |||
// The current implementation only supports up to 64 token scopes. | |||
// If we need to support > 64 scopes, | |||
// refactoring the whole implementation in this file (and only this file) is needed. | |||
) | |||
// allAccessTokenScopes contains all access token scopes. | |||
// The order is important: parent scope must precede child scopes. | |||
var allAccessTokenScopes = []AccessTokenScope{ | |||
AccessTokenScopePublicOnly, | |||
AccessTokenScopeWriteActivityPub, AccessTokenScopeReadActivityPub, | |||
AccessTokenScopeWriteAdmin, AccessTokenScopeReadAdmin, | |||
AccessTokenScopeWriteMisc, AccessTokenScopeReadMisc, | |||
AccessTokenScopeWriteNotification, AccessTokenScopeReadNotification, | |||
AccessTokenScopeWriteOrganization, AccessTokenScopeReadOrganization, | |||
AccessTokenScopeWritePackage, AccessTokenScopeReadPackage, | |||
AccessTokenScopeWriteIssue, AccessTokenScopeReadIssue, | |||
AccessTokenScopeWriteRepository, AccessTokenScopeReadRepository, | |||
AccessTokenScopeWriteUser, AccessTokenScopeReadUser, | |||
} | |||
// allAccessTokenScopeBits contains all access token scopes. | |||
var allAccessTokenScopeBits = map[AccessTokenScope]accessTokenScopeBitmap{ | |||
AccessTokenScopeAll: accessTokenScopeAllBits, | |||
AccessTokenScopePublicOnly: accessTokenScopePublicOnlyBits, | |||
AccessTokenScopeReadActivityPub: accessTokenScopeReadActivityPubBits, | |||
AccessTokenScopeWriteActivityPub: accessTokenScopeWriteActivityPubBits, | |||
AccessTokenScopeReadAdmin: accessTokenScopeReadAdminBits, | |||
AccessTokenScopeWriteAdmin: accessTokenScopeWriteAdminBits, | |||
AccessTokenScopeReadMisc: accessTokenScopeReadMiscBits, | |||
AccessTokenScopeWriteMisc: accessTokenScopeWriteMiscBits, | |||
AccessTokenScopeReadNotification: accessTokenScopeReadNotificationBits, | |||
AccessTokenScopeWriteNotification: accessTokenScopeWriteNotificationBits, | |||
AccessTokenScopeReadOrganization: accessTokenScopeReadOrganizationBits, | |||
AccessTokenScopeWriteOrganization: accessTokenScopeWriteOrganizationBits, | |||
AccessTokenScopeReadPackage: accessTokenScopeReadPackageBits, | |||
AccessTokenScopeWritePackage: accessTokenScopeWritePackageBits, | |||
AccessTokenScopeReadIssue: accessTokenScopeReadIssueBits, | |||
AccessTokenScopeWriteIssue: accessTokenScopeWriteIssueBits, | |||
AccessTokenScopeReadRepository: accessTokenScopeReadRepositoryBits, | |||
AccessTokenScopeWriteRepository: accessTokenScopeWriteRepositoryBits, | |||
AccessTokenScopeReadUser: accessTokenScopeReadUserBits, | |||
AccessTokenScopeWriteUser: accessTokenScopeWriteUserBits, | |||
} | |||
// hasScope returns true if the string has the given scope | |||
func (bitmap accessTokenScopeBitmap) hasScope(scope AccessTokenScope) (bool, error) { | |||
expectedBits, ok := allAccessTokenScopeBits[scope] | |||
if !ok { | |||
return false, fmt.Errorf("invalid access token scope: %s", scope) | |||
} | |||
return bitmap&expectedBits == expectedBits, nil | |||
} | |||
// toScope returns a normalized scope string without any duplicates. | |||
func (bitmap accessTokenScopeBitmap) toScope(unknownScopes *[]unknownAccessTokenScope) AccessTokenScope { | |||
var scopes []string | |||
// Preserve unknown scopes, and put them at the beginning so that it's clear | |||
// when debugging. | |||
if unknownScopes != nil { | |||
for _, unknownScope := range *unknownScopes { | |||
scopes = append(scopes, string(unknownScope)) | |||
} | |||
} | |||
// iterate over all scopes, and reconstruct the bitmap | |||
// if the reconstructed bitmap doesn't change, then the scope is already included | |||
var reconstruct accessTokenScopeBitmap | |||
for _, singleScope := range allAccessTokenScopes { | |||
// no need for error checking here, since we know the scope is valid | |||
if ok, _ := bitmap.hasScope(singleScope); ok { | |||
current := reconstruct | allAccessTokenScopeBits[singleScope] | |||
if current == reconstruct { | |||
continue | |||
} | |||
reconstruct = current | |||
scopes = append(scopes, string(singleScope)) | |||
} | |||
} | |||
scope := AccessTokenScope(strings.Join(scopes, ",")) | |||
scope = AccessTokenScope(strings.ReplaceAll( | |||
string(scope), | |||
"write:activitypub,write:admin,write:misc,write:notification,write:organization,write:package,write:issue,write:repository,write:user", | |||
"all", | |||
)) | |||
return scope | |||
} | |||
// parse the scope string into a bitmap, thus removing possible duplicates. | |||
func (s AccessTokenScope) parse() (accessTokenScopeBitmap, *[]unknownAccessTokenScope) { | |||
var bitmap accessTokenScopeBitmap | |||
var unknownScopes []unknownAccessTokenScope | |||
// 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 | |||
} | |||
if singleScope == AccessTokenScopeAll { | |||
bitmap |= accessTokenScopeAllBits | |||
continue | |||
} | |||
bits, ok := allAccessTokenScopeBits[singleScope] | |||
if !ok { | |||
unknownScopes = append(unknownScopes, unknownAccessTokenScope(string(singleScope))) | |||
} | |||
bitmap |= bits | |||
} | |||
return bitmap, &unknownScopes | |||
} | |||
// NormalizePreservingUnknown returns a normalized scope string without any | |||
// duplicates. Unknown scopes are included. | |||
func (s AccessTokenScope) NormalizePreservingUnknown() AccessTokenScope { | |||
bitmap, unknownScopes := s.parse() | |||
return bitmap.toScope(unknownScopes) | |||
} | |||
// OldAccessTokenScope represents the scope for an access token. | |||
type OldAccessTokenScope string | |||
const ( | |||
OldAccessTokenScopeAll OldAccessTokenScope = "all" | |||
OldAccessTokenScopeRepo OldAccessTokenScope = "repo" | |||
OldAccessTokenScopeRepoStatus OldAccessTokenScope = "repo:status" | |||
OldAccessTokenScopePublicRepo OldAccessTokenScope = "public_repo" | |||
OldAccessTokenScopeAdminOrg OldAccessTokenScope = "admin:org" | |||
OldAccessTokenScopeWriteOrg OldAccessTokenScope = "write:org" | |||
OldAccessTokenScopeReadOrg OldAccessTokenScope = "read:org" | |||
OldAccessTokenScopeAdminPublicKey OldAccessTokenScope = "admin:public_key" | |||
OldAccessTokenScopeWritePublicKey OldAccessTokenScope = "write:public_key" | |||
OldAccessTokenScopeReadPublicKey OldAccessTokenScope = "read:public_key" | |||
OldAccessTokenScopeAdminRepoHook OldAccessTokenScope = "admin:repo_hook" | |||
OldAccessTokenScopeWriteRepoHook OldAccessTokenScope = "write:repo_hook" | |||
OldAccessTokenScopeReadRepoHook OldAccessTokenScope = "read:repo_hook" | |||
OldAccessTokenScopeAdminOrgHook OldAccessTokenScope = "admin:org_hook" | |||
OldAccessTokenScopeNotification OldAccessTokenScope = "notification" | |||
OldAccessTokenScopeUser OldAccessTokenScope = "user" | |||
OldAccessTokenScopeReadUser OldAccessTokenScope = "read:user" | |||
OldAccessTokenScopeUserEmail OldAccessTokenScope = "user:email" | |||
OldAccessTokenScopeUserFollow OldAccessTokenScope = "user:follow" | |||
OldAccessTokenScopeDeleteRepo OldAccessTokenScope = "delete_repo" | |||
OldAccessTokenScopePackage OldAccessTokenScope = "package" | |||
OldAccessTokenScopeWritePackage OldAccessTokenScope = "write:package" | |||
OldAccessTokenScopeReadPackage OldAccessTokenScope = "read:package" | |||
OldAccessTokenScopeDeletePackage OldAccessTokenScope = "delete:package" | |||
OldAccessTokenScopeAdminGPGKey OldAccessTokenScope = "admin:gpg_key" | |||
OldAccessTokenScopeWriteGPGKey OldAccessTokenScope = "write:gpg_key" | |||
OldAccessTokenScopeReadGPGKey OldAccessTokenScope = "read:gpg_key" | |||
OldAccessTokenScopeAdminApplication OldAccessTokenScope = "admin:application" | |||
OldAccessTokenScopeWriteApplication OldAccessTokenScope = "write:application" | |||
OldAccessTokenScopeReadApplication OldAccessTokenScope = "read:application" | |||
OldAccessTokenScopeSudo OldAccessTokenScope = "sudo" | |||
) | |||
var accessTokenScopeMap = map[OldAccessTokenScope][]AccessTokenScope{ | |||
OldAccessTokenScopeAll: {AccessTokenScopeAll}, | |||
OldAccessTokenScopeRepo: {AccessTokenScopeWriteRepository}, | |||
OldAccessTokenScopeRepoStatus: {AccessTokenScopeWriteRepository}, | |||
OldAccessTokenScopePublicRepo: {AccessTokenScopePublicOnly, AccessTokenScopeWriteRepository}, | |||
OldAccessTokenScopeAdminOrg: {AccessTokenScopeWriteOrganization}, | |||
OldAccessTokenScopeWriteOrg: {AccessTokenScopeWriteOrganization}, | |||
OldAccessTokenScopeReadOrg: {AccessTokenScopeReadOrganization}, | |||
OldAccessTokenScopeAdminPublicKey: {AccessTokenScopeWriteUser}, | |||
OldAccessTokenScopeWritePublicKey: {AccessTokenScopeWriteUser}, | |||
OldAccessTokenScopeReadPublicKey: {AccessTokenScopeReadUser}, | |||
OldAccessTokenScopeAdminRepoHook: {AccessTokenScopeWriteRepository}, | |||
OldAccessTokenScopeWriteRepoHook: {AccessTokenScopeWriteRepository}, | |||
OldAccessTokenScopeReadRepoHook: {AccessTokenScopeReadRepository}, | |||
OldAccessTokenScopeAdminOrgHook: {AccessTokenScopeWriteOrganization}, | |||
OldAccessTokenScopeNotification: {AccessTokenScopeWriteNotification}, | |||
OldAccessTokenScopeUser: {AccessTokenScopeWriteUser}, | |||
OldAccessTokenScopeReadUser: {AccessTokenScopeReadUser}, | |||
OldAccessTokenScopeUserEmail: {AccessTokenScopeWriteUser}, | |||
OldAccessTokenScopeUserFollow: {AccessTokenScopeWriteUser}, | |||
OldAccessTokenScopeDeleteRepo: {AccessTokenScopeWriteRepository}, | |||
OldAccessTokenScopePackage: {AccessTokenScopeWritePackage}, | |||
OldAccessTokenScopeWritePackage: {AccessTokenScopeWritePackage}, | |||
OldAccessTokenScopeReadPackage: {AccessTokenScopeReadPackage}, | |||
OldAccessTokenScopeDeletePackage: {AccessTokenScopeWritePackage}, | |||
OldAccessTokenScopeAdminGPGKey: {AccessTokenScopeWriteUser}, | |||
OldAccessTokenScopeWriteGPGKey: {AccessTokenScopeWriteUser}, | |||
OldAccessTokenScopeReadGPGKey: {AccessTokenScopeReadUser}, | |||
OldAccessTokenScopeAdminApplication: {AccessTokenScopeWriteUser}, | |||
OldAccessTokenScopeWriteApplication: {AccessTokenScopeWriteUser}, | |||
OldAccessTokenScopeReadApplication: {AccessTokenScopeReadUser}, | |||
OldAccessTokenScopeSudo: {AccessTokenScopeWriteAdmin}, | |||
} | |||
type AccessToken struct { | |||
ID int64 `xorm:"pk autoincr"` | |||
Scope string | |||
} | |||
func ConvertScopedAccessTokens(x *xorm.Engine) error { | |||
var tokens []*AccessToken | |||
if err := x.Find(&tokens); err != nil { | |||
return err | |||
} | |||
for _, token := range tokens { | |||
var scopes []string | |||
allNewScopesMap := make(map[AccessTokenScope]bool) | |||
for _, oldScope := range strings.Split(token.Scope, ",") { | |||
if newScopes, exists := accessTokenScopeMap[OldAccessTokenScope(oldScope)]; exists { | |||
for _, newScope := range newScopes { | |||
allNewScopesMap[newScope] = true | |||
} | |||
} else { | |||
log.Debug("access token scope not recognized as old token scope %s; preserving it", oldScope) | |||
scopes = append(scopes, oldScope) | |||
} | |||
} | |||
for s := range allNewScopesMap { | |||
scopes = append(scopes, string(s)) | |||
} | |||
scope := AccessTokenScope(strings.Join(scopes, ",")) | |||
// normalize the scope | |||
normScope := scope.NormalizePreservingUnknown() | |||
token.Scope = string(normScope) | |||
// update the db entry with the new scope | |||
if _, err := x.Cols("scope").ID(token.ID).Update(token); err != nil { | |||
return err | |||
} | |||
} | |||
return nil | |||
} |
@@ -0,0 +1,110 @@ | |||
// Copyright 2023 The Gitea Authors. All rights reserved. | |||
// SPDX-License-Identifier: MIT | |||
package v1_20 //nolint | |||
import ( | |||
"sort" | |||
"strings" | |||
"testing" | |||
"code.gitea.io/gitea/models/migrations/base" | |||
"github.com/stretchr/testify/assert" | |||
) | |||
type testCase struct { | |||
Old OldAccessTokenScope | |||
New AccessTokenScope | |||
} | |||
func createOldTokenScope(scopes ...OldAccessTokenScope) OldAccessTokenScope { | |||
s := make([]string, 0, len(scopes)) | |||
for _, os := range scopes { | |||
s = append(s, string(os)) | |||
} | |||
return OldAccessTokenScope(strings.Join(s, ",")) | |||
} | |||
func createNewTokenScope(scopes ...AccessTokenScope) AccessTokenScope { | |||
s := make([]string, 0, len(scopes)) | |||
for _, os := range scopes { | |||
s = append(s, string(os)) | |||
} | |||
return AccessTokenScope(strings.Join(s, ",")) | |||
} | |||
func Test_ConvertScopedAccessTokens(t *testing.T) { | |||
tests := []testCase{ | |||
{ | |||
createOldTokenScope(OldAccessTokenScopeRepo, OldAccessTokenScopeUserFollow), | |||
createNewTokenScope(AccessTokenScopeWriteRepository, AccessTokenScopeWriteUser), | |||
}, | |||
{ | |||
createOldTokenScope(OldAccessTokenScopeUser, OldAccessTokenScopeWritePackage, OldAccessTokenScopeSudo), | |||
createNewTokenScope(AccessTokenScopeWriteAdmin, AccessTokenScopeWritePackage, AccessTokenScopeWriteUser), | |||
}, | |||
{ | |||
createOldTokenScope(), | |||
createNewTokenScope(), | |||
}, | |||
{ | |||
createOldTokenScope(OldAccessTokenScopeReadGPGKey, OldAccessTokenScopeReadOrg, OldAccessTokenScopeAll), | |||
createNewTokenScope(AccessTokenScopeAll), | |||
}, | |||
{ | |||
createOldTokenScope(OldAccessTokenScopeReadGPGKey, "invalid"), | |||
createNewTokenScope("invalid", AccessTokenScopeReadUser), | |||
}, | |||
} | |||
// add a test for each individual mapping | |||
for oldScope, newScope := range accessTokenScopeMap { | |||
tests = append(tests, testCase{ | |||
oldScope, | |||
createNewTokenScope(newScope...), | |||
}) | |||
} | |||
x, deferable := base.PrepareTestEnv(t, 0, new(AccessToken)) | |||
defer deferable() | |||
if x == nil || t.Failed() { | |||
t.Skip() | |||
return | |||
} | |||
// verify that no fixtures were loaded | |||
count, err := x.Count(&AccessToken{}) | |||
assert.NoError(t, err) | |||
assert.Equal(t, int64(0), count) | |||
for _, tc := range tests { | |||
_, err = x.Insert(&AccessToken{ | |||
Scope: string(tc.Old), | |||
}) | |||
assert.NoError(t, err) | |||
} | |||
// migrate the scopes | |||
err = ConvertScopedAccessTokens(x) | |||
assert.NoError(t, err) | |||
// migrate the scopes again (migration should be idempotent) | |||
err = ConvertScopedAccessTokens(x) | |||
assert.NoError(t, err) | |||
tokens := make([]AccessToken, 0) | |||
err = x.Find(&tokens) | |||
assert.NoError(t, err) | |||
assert.Equal(t, len(tests), len(tokens)) | |||
// sort the tokens (insertion order by auto-incrementing primary key) | |||
sort.Slice(tokens, func(i, j int) bool { | |||
return tokens[i].ID < tokens[j].ID | |||
}) | |||
// verify that the converted scopes are equal to the expected test result | |||
for idx, newToken := range tokens { | |||
assert.Equal(t, string(tests[idx].New), newToken.Scope) | |||
} | |||
} |
@@ -111,28 +111,36 @@ func RequireRepoReaderOr(unitTypes ...unit.Type) func(ctx *Context) { | |||
} | |||
} | |||
// RequireRepoScopedToken check whether personal access token has repo scope | |||
func CheckRepoScopedToken(ctx *Context, repo *repo_model.Repository) { | |||
// CheckRepoScopedToken check whether personal access token has repo scope | |||
func CheckRepoScopedToken(ctx *Context, repo *repo_model.Repository, level auth_model.AccessTokenScopeLevel) { | |||
if !ctx.IsBasicAuth || ctx.Data["IsApiToken"] != true { | |||
return | |||
} | |||
var err error | |||
scope, ok := ctx.Data["ApiTokenScope"].(auth_model.AccessTokenScope) | |||
if ok { // it's a personal access token but not oauth2 token | |||
var scopeMatched bool | |||
scopeMatched, err = scope.HasScope(auth_model.AccessTokenScopeRepo) | |||
requiredScopes := auth_model.GetRequiredScopes(level, auth_model.AccessTokenScopeCategoryRepository) | |||
// check if scope only applies to public resources | |||
publicOnly, err := scope.PublicOnly() | |||
if err != nil { | |||
ctx.ServerError("HasScope", err) | |||
return | |||
} | |||
if !scopeMatched && !repo.IsPrivate { | |||
scopeMatched, err = scope.HasScope(auth_model.AccessTokenScopePublicRepo) | |||
if err != nil { | |||
ctx.ServerError("HasScope", err) | |||
return | |||
} | |||
if publicOnly && repo.IsPrivate { | |||
ctx.Error(http.StatusForbidden) | |||
return | |||
} | |||
scopeMatched, err = scope.HasScope(requiredScopes...) | |||
if err != nil { | |||
ctx.ServerError("HasScope", err) | |||
return | |||
} | |||
if !scopeMatched { | |||
ctx.Error(http.StatusForbidden) | |||
return |
@@ -796,7 +796,6 @@ unbind_success = The social account has been unlinked from your Gitea account. | |||
manage_access_token = Manage Access Tokens | |||
generate_new_token = Generate New Token | |||
tokens_desc = These tokens grant access to your account using the Gitea API. | |||
new_token_desc = Applications using a token have full access to your account. | |||
token_name = Token Name | |||
generate_token = Generate Token | |||
generate_token_success = Your new token has been generated. Copy it now as it will not be shown again. | |||
@@ -807,8 +806,13 @@ access_token_deletion_cancel_action = Cancel | |||
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: | |||
repo_and_org_access = Repository and Organization Access | |||
permissions_public_only = Public only | |||
permissions_access_all = All (public, private, and limited) | |||
select_permissions = Select permissions | |||
scoped_token_desc = Selected token scopes limit authentication only to the corresponding <a %s>API</a> routes. Read the <a %s>documentation</a> for more information. | |||
at_least_one_permission = You must select at least one permission to create a token | |||
permissions_list = Permissions: | |||
manage_oauth2_applications = Manage OAuth2 Applications | |||
edit_oauth2_application = Edit OAuth2 Application |
@@ -236,44 +236,85 @@ func reqPackageAccess(accessMode perm.AccessMode) func(ctx *context.APIContext) | |||
} | |||
} | |||
// if a token is being used for auth, we check that it contains the required scope | |||
// if a token is not being used, reqToken will enforce other sign in methods | |||
func tokenRequiresScopes(requiredScopeCategories ...auth_model.AccessTokenScopeCategory) func(ctx *context.APIContext) { | |||
return func(ctx *context.APIContext) { | |||
// no scope required | |||
if len(requiredScopeCategories) == 0 { | |||
return | |||
} | |||
// Need OAuth2 token to be present. | |||
scope, scopeExists := ctx.Data["ApiTokenScope"].(auth_model.AccessTokenScope) | |||
if ctx.Data["IsApiToken"] != true || !scopeExists { | |||
return | |||
} | |||
ctx.Data["ApiTokenScopePublicRepoOnly"] = false | |||
ctx.Data["ApiTokenScopePublicOrgOnly"] = false | |||
// use the http method to determine the access level | |||
requiredScopeLevel := auth_model.Read | |||
if ctx.Req.Method == "POST" || ctx.Req.Method == "PUT" || ctx.Req.Method == "PATCH" || ctx.Req.Method == "DELETE" { | |||
requiredScopeLevel = auth_model.Write | |||
} | |||
// get the required scope for the given access level and category | |||
requiredScopes := auth_model.GetRequiredScopes(requiredScopeLevel, requiredScopeCategories...) | |||
// check if scope only applies to public resources | |||
publicOnly, err := scope.PublicOnly() | |||
if err != nil { | |||
ctx.Error(http.StatusForbidden, "tokenRequiresScope", "parsing public resource scope failed: "+err.Error()) | |||
return | |||
} | |||
// this context is used by the middleware in the specific route | |||
ctx.Data["ApiTokenScopePublicRepoOnly"] = publicOnly && auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryRepository) | |||
ctx.Data["ApiTokenScopePublicOrgOnly"] = publicOnly && auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryOrganization) | |||
allow, err := scope.HasScope(requiredScopes...) | |||
if err != nil { | |||
ctx.Error(http.StatusForbidden, "tokenRequiresScope", "checking scope failed: "+err.Error()) | |||
return | |||
} | |||
if allow { | |||
return | |||
} | |||
ctx.Error(http.StatusForbidden, "tokenRequiresScope", fmt.Sprintf("token does not have at least one of required scope(s): %v", requiredScopes)) | |||
} | |||
} | |||
// Contexter middleware already checks token for user sign in process. | |||
func reqToken(requiredScope auth_model.AccessTokenScope) func(ctx *context.APIContext) { | |||
func reqToken() func(ctx *context.APIContext) { | |||
return func(ctx *context.APIContext) { | |||
// If actions token is present | |||
if true == ctx.Data["IsActionsToken"] { | |||
return | |||
} | |||
// If OAuth2 token is present | |||
if _, ok := ctx.Data["ApiTokenScope"]; ctx.Data["IsApiToken"] == true && ok { | |||
// no scope required | |||
if requiredScope == "" { | |||
return | |||
} | |||
if true == ctx.Data["IsApiToken"] { | |||
publicRepo, pubRepoExists := ctx.Data["ApiTokenScopePublicRepoOnly"] | |||
publicOrg, pubOrgExists := ctx.Data["ApiTokenScopePublicOrgOnly"] | |||
// check scope | |||
scope := ctx.Data["ApiTokenScope"].(auth_model.AccessTokenScope) | |||
allow, err := scope.HasScope(requiredScope) | |||
if err != nil { | |||
ctx.Error(http.StatusForbidden, "reqToken", "parsing token failed: "+err.Error()) | |||
return | |||
} | |||
if allow { | |||
if pubRepoExists && publicRepo.(bool) && | |||
ctx.Repo.Repository != nil && ctx.Repo.Repository.IsPrivate { | |||
ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public repos") | |||
return | |||
} | |||
// if requires 'repo' scope, but only has 'public_repo' scope, allow it only if the repo is public | |||
if requiredScope == auth_model.AccessTokenScopeRepo { | |||
if allowPublicRepo, err := scope.HasScope(auth_model.AccessTokenScopePublicRepo); err == nil && allowPublicRepo { | |||
if ctx.Repo.Repository != nil && !ctx.Repo.Repository.IsPrivate { | |||
return | |||
} | |||
} | |||
if pubOrgExists && publicOrg.(bool) && | |||
ctx.Org.Organization != nil && ctx.Org.Organization.Visibility != api.VisibleTypePublic { | |||
ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public orgs") | |||
return | |||
} | |||
ctx.Error(http.StatusForbidden, "reqToken", "token does not have required scope: "+requiredScope) | |||
return | |||
} | |||
if ctx.IsBasicAuth { | |||
ctx.CheckForOTP() | |||
return | |||
@@ -700,7 +741,7 @@ func Routes(ctx gocontext.Context) *web.Route { | |||
ctx.Redirect(setting.AppSubURL + "/api/swagger") | |||
}) | |||
} | |||
m.Get("/version", misc.Version) | |||
if setting.Federation.Enabled { | |||
m.Get("/nodeinfo", misc.NodeInfo) | |||
m.Group("/activitypub", func() { | |||
@@ -713,37 +754,43 @@ func Routes(ctx gocontext.Context) *web.Route { | |||
m.Get("", activitypub.Person) | |||
m.Post("/inbox", activitypub.ReqHTTPSignature(), activitypub.PersonInbox) | |||
}, context_service.UserIDAssignmentAPI()) | |||
}) | |||
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryActivityPub)) | |||
} | |||
m.Get("/signing-key.gpg", misc.SigningKey) | |||
m.Post("/markup", bind(api.MarkupOption{}), misc.Markup) | |||
m.Post("/markdown", bind(api.MarkdownOption{}), misc.Markdown) | |||
m.Post("/markdown/raw", misc.MarkdownRaw) | |||
m.Get("/gitignore/templates", misc.ListGitignoresTemplates) | |||
m.Get("/gitignore/templates/{name}", misc.GetGitignoreTemplateInfo) | |||
m.Get("/licenses", misc.ListLicenseTemplates) | |||
m.Get("/licenses/{name}", misc.GetLicenseTemplateInfo) | |||
m.Get("/label/templates", misc.ListLabelTemplates) | |||
m.Get("/label/templates/{name}", misc.GetLabelTemplate) | |||
m.Group("/settings", func() { | |||
m.Get("/ui", settings.GetGeneralUISettings) | |||
m.Get("/api", settings.GetGeneralAPISettings) | |||
m.Get("/attachment", settings.GetGeneralAttachmentSettings) | |||
m.Get("/repository", settings.GetGeneralRepoSettings) | |||
}) | |||
// Notifications (requires 'notification' scope) | |||
// Misc (requires 'misc' scope) | |||
m.Group("", func() { | |||
m.Get("/version", misc.Version) | |||
m.Get("/signing-key.gpg", misc.SigningKey) | |||
m.Post("/markup", reqToken(), bind(api.MarkupOption{}), misc.Markup) | |||
m.Post("/markdown", reqToken(), bind(api.MarkdownOption{}), misc.Markdown) | |||
m.Post("/markdown/raw", reqToken(), misc.MarkdownRaw) | |||
m.Get("/gitignore/templates", misc.ListGitignoresTemplates) | |||
m.Get("/gitignore/templates/{name}", misc.GetGitignoreTemplateInfo) | |||
m.Get("/licenses", misc.ListLicenseTemplates) | |||
m.Get("/licenses/{name}", misc.GetLicenseTemplateInfo) | |||
m.Get("/label/templates", misc.ListLabelTemplates) | |||
m.Get("/label/templates/{name}", misc.GetLabelTemplate) | |||
m.Group("/settings", func() { | |||
m.Get("/ui", settings.GetGeneralUISettings) | |||
m.Get("/api", settings.GetGeneralAPISettings) | |||
m.Get("/attachment", settings.GetGeneralAttachmentSettings) | |||
m.Get("/repository", settings.GetGeneralRepoSettings) | |||
}) | |||
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryMisc)) | |||
// Notifications (requires 'notifications' scope) | |||
m.Group("/notifications", func() { | |||
m.Combo(""). | |||
Get(notify.ListNotifications). | |||
Put(notify.ReadNotifications) | |||
Put(notify.ReadNotifications, reqToken()) | |||
m.Get("/new", notify.NewAvailable) | |||
m.Combo("/threads/{id}"). | |||
Get(notify.GetThread). | |||
Patch(notify.ReadThread) | |||
}, reqToken(auth_model.AccessTokenScopeNotification)) | |||
Patch(notify.ReadThread, reqToken()) | |||
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryNotification)) | |||
// Users (no scope required) | |||
// Users (requires user scope) | |||
m.Group("/users", func() { | |||
m.Get("/search", reqExploreSignIn(), user.Search) | |||
@@ -754,18 +801,18 @@ func Routes(ctx gocontext.Context) *web.Route { | |||
m.Get("/heatmap", user.GetUserHeatmapData) | |||
} | |||
m.Get("/repos", reqExploreSignIn(), user.ListUserRepos) | |||
m.Get("/repos", tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository), reqExploreSignIn(), user.ListUserRepos) | |||
m.Group("/tokens", func() { | |||
m.Combo("").Get(user.ListAccessTokens). | |||
Post(bind(api.CreateAccessTokenOption{}), user.CreateAccessToken) | |||
m.Combo("/{id}").Delete(user.DeleteAccessToken) | |||
Post(bind(api.CreateAccessTokenOption{}), reqToken(), user.CreateAccessToken) | |||
m.Combo("/{id}").Delete(reqToken(), user.DeleteAccessToken) | |||
}, reqBasicAuth()) | |||
m.Get("/activities/feeds", user.ListUserActivityFeeds) | |||
}, context_service.UserAssignmentAPI()) | |||
}) | |||
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser)) | |||
// (no scope required) | |||
// Users (requires user scope) | |||
m.Group("/users", func() { | |||
m.Group("/{username}", func() { | |||
m.Get("/keys", user.ListPublicKeys) | |||
@@ -781,59 +828,61 @@ func Routes(ctx gocontext.Context) *web.Route { | |||
m.Get("/subscriptions", user.GetWatchedRepos) | |||
}, context_service.UserAssignmentAPI()) | |||
}, reqToken("")) | |||
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser), reqToken()) | |||
// Users (requires user scope) | |||
m.Group("/user", func() { | |||
m.Get("", user.GetAuthenticatedUser) | |||
m.Group("/settings", func() { | |||
m.Get("", reqToken(auth_model.AccessTokenScopeReadUser), user.GetUserSettings) | |||
m.Patch("", reqToken(auth_model.AccessTokenScopeUser), bind(api.UserSettingsOptions{}), user.UpdateUserSettings) | |||
}) | |||
m.Combo("/emails").Get(reqToken(auth_model.AccessTokenScopeReadUser), user.ListEmails). | |||
Post(reqToken(auth_model.AccessTokenScopeUser), bind(api.CreateEmailOption{}), user.AddEmail). | |||
Delete(reqToken(auth_model.AccessTokenScopeUser), bind(api.DeleteEmailOption{}), user.DeleteEmail) | |||
m.Get("", user.GetUserSettings) | |||
m.Patch("", bind(api.UserSettingsOptions{}), user.UpdateUserSettings) | |||
}, reqToken()) | |||
m.Combo("/emails"). | |||
Get(user.ListEmails). | |||
Post(bind(api.CreateEmailOption{}), user.AddEmail). | |||
Delete(bind(api.DeleteEmailOption{}), user.DeleteEmail) | |||
m.Get("/followers", user.ListMyFollowers) | |||
m.Group("/following", func() { | |||
m.Get("", user.ListMyFollowing) | |||
m.Group("/{username}", func() { | |||
m.Get("", user.CheckMyFollowing) | |||
m.Put("", reqToken(auth_model.AccessTokenScopeUserFollow), user.Follow) // requires 'user:follow' scope | |||
m.Delete("", reqToken(auth_model.AccessTokenScopeUserFollow), user.Unfollow) // requires 'user:follow' scope | |||
m.Put("", user.Follow) | |||
m.Delete("", user.Unfollow) | |||
}, context_service.UserAssignmentAPI()) | |||
}) | |||
// (admin:public_key scope) | |||
m.Group("/keys", func() { | |||
m.Combo("").Get(reqToken(auth_model.AccessTokenScopeReadPublicKey), user.ListMyPublicKeys). | |||
Post(reqToken(auth_model.AccessTokenScopeWritePublicKey), bind(api.CreateKeyOption{}), user.CreatePublicKey) | |||
m.Combo("/{id}").Get(reqToken(auth_model.AccessTokenScopeReadPublicKey), user.GetPublicKey). | |||
Delete(reqToken(auth_model.AccessTokenScopeWritePublicKey), user.DeletePublicKey) | |||
m.Combo("").Get(user.ListMyPublicKeys). | |||
Post(bind(api.CreateKeyOption{}), user.CreatePublicKey) | |||
m.Combo("/{id}").Get(user.GetPublicKey). | |||
Delete(user.DeletePublicKey) | |||
}) | |||
// (admin:application scope) | |||
m.Group("/applications", func() { | |||
m.Combo("/oauth2"). | |||
Get(reqToken(auth_model.AccessTokenScopeReadApplication), user.ListOauth2Applications). | |||
Post(reqToken(auth_model.AccessTokenScopeWriteApplication), bind(api.CreateOAuth2ApplicationOptions{}), user.CreateOauth2Application) | |||
Get(user.ListOauth2Applications). | |||
Post(bind(api.CreateOAuth2ApplicationOptions{}), user.CreateOauth2Application) | |||
m.Combo("/oauth2/{id}"). | |||
Delete(reqToken(auth_model.AccessTokenScopeWriteApplication), user.DeleteOauth2Application). | |||
Patch(reqToken(auth_model.AccessTokenScopeWriteApplication), bind(api.CreateOAuth2ApplicationOptions{}), user.UpdateOauth2Application). | |||
Get(reqToken(auth_model.AccessTokenScopeReadApplication), user.GetOauth2Application) | |||
Delete(user.DeleteOauth2Application). | |||
Patch(bind(api.CreateOAuth2ApplicationOptions{}), user.UpdateOauth2Application). | |||
Get(user.GetOauth2Application) | |||
}) | |||
// (admin:gpg_key scope) | |||
m.Group("/gpg_keys", func() { | |||
m.Combo("").Get(reqToken(auth_model.AccessTokenScopeReadGPGKey), user.ListMyGPGKeys). | |||
Post(reqToken(auth_model.AccessTokenScopeWriteGPGKey), bind(api.CreateGPGKeyOption{}), user.CreateGPGKey) | |||
m.Combo("/{id}").Get(reqToken(auth_model.AccessTokenScopeReadGPGKey), user.GetGPGKey). | |||
Delete(reqToken(auth_model.AccessTokenScopeWriteGPGKey), user.DeleteGPGKey) | |||
m.Combo("").Get(user.ListMyGPGKeys). | |||
Post(bind(api.CreateGPGKeyOption{}), user.CreateGPGKey) | |||
m.Combo("/{id}").Get(user.GetGPGKey). | |||
Delete(user.DeleteGPGKey) | |||
}) | |||
m.Get("/gpg_key_token", reqToken(auth_model.AccessTokenScopeReadGPGKey), user.GetVerificationToken) | |||
m.Post("/gpg_key_verify", reqToken(auth_model.AccessTokenScopeReadGPGKey), bind(api.VerifyGPGKeyOption{}), user.VerifyUserGPGKey) | |||
m.Get("/gpg_key_token", user.GetVerificationToken) | |||
m.Post("/gpg_key_verify", bind(api.VerifyGPGKeyOption{}), user.VerifyUserGPGKey) | |||
// (repo scope) | |||
m.Combo("/repos", reqToken(auth_model.AccessTokenScopeRepo)).Get(user.ListMyRepos). | |||
m.Combo("/repos", tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository)).Get(user.ListMyRepos). | |||
Post(bind(api.CreateRepoOption{}), repo.Create) | |||
// (repo scope) | |||
@@ -844,64 +893,65 @@ func Routes(ctx gocontext.Context) *web.Route { | |||
m.Put("", user.Star) | |||
m.Delete("", user.Unstar) | |||
}, repoAssignment()) | |||
}, reqToken(auth_model.AccessTokenScopeRepo)) | |||
m.Get("/times", reqToken(auth_model.AccessTokenScopeRepo), repo.ListMyTrackedTimes) | |||
m.Get("/stopwatches", reqToken(auth_model.AccessTokenScopeRepo), repo.GetStopwatches) | |||
m.Get("/subscriptions", reqToken(auth_model.AccessTokenScopeRepo), user.GetMyWatchedRepos) | |||
m.Get("/teams", reqToken(auth_model.AccessTokenScopeRepo), org.ListUserTeams) | |||
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository)) | |||
m.Get("/times", repo.ListMyTrackedTimes) | |||
m.Get("/stopwatches", repo.GetStopwatches) | |||
m.Get("/subscriptions", user.GetMyWatchedRepos) | |||
m.Get("/teams", org.ListUserTeams) | |||
m.Group("/hooks", func() { | |||
m.Combo("").Get(user.ListHooks). | |||
Post(bind(api.CreateHookOption{}), user.CreateHook) | |||
m.Combo("/{id}").Get(user.GetHook). | |||
Patch(bind(api.EditHookOption{}), user.EditHook). | |||
Delete(user.DeleteHook) | |||
}, reqToken(auth_model.AccessTokenScopeAdminUserHook), reqWebhooksEnabled()) | |||
}, reqToken("")) | |||
}, reqWebhooksEnabled()) | |||
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser), reqToken()) | |||
// Repositories | |||
m.Post("/org/{org}/repos", reqToken(auth_model.AccessTokenScopeAdminOrg), bind(api.CreateRepoOption{}), repo.CreateOrgRepoDeprecated) | |||
// Repositories (requires repo scope, org scope) | |||
m.Post("/org/{org}/repos", | |||
tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization, auth_model.AccessTokenScopeCategoryRepository), | |||
reqToken(), | |||
bind(api.CreateRepoOption{}), | |||
repo.CreateOrgRepoDeprecated) | |||
m.Combo("/repositories/{id}", reqToken(auth_model.AccessTokenScopeRepo)).Get(repo.GetByID) | |||
// requires repo scope | |||
m.Combo("/repositories/{id}", reqToken(), tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository)).Get(repo.GetByID) | |||
// Repos (requires repo scope) | |||
m.Group("/repos", func() { | |||
m.Get("/search", repo.Search) | |||
m.Get("/issues/search", repo.SearchIssues) | |||
// (repo scope) | |||
m.Post("/migrate", reqToken(auth_model.AccessTokenScopeRepo), bind(api.MigrateRepoOptions{}), repo.Migrate) | |||
m.Post("/migrate", reqToken(), bind(api.MigrateRepoOptions{}), repo.Migrate) | |||
m.Group("/{username}/{reponame}", func() { | |||
m.Combo("").Get(reqAnyRepoReader(), repo.Get). | |||
Delete(reqToken(auth_model.AccessTokenScopeDeleteRepo), reqOwner(), repo.Delete). | |||
Patch(reqToken(auth_model.AccessTokenScopeRepo), reqAdmin(), bind(api.EditRepoOption{}), repo.Edit) | |||
m.Post("/generate", reqToken(auth_model.AccessTokenScopeRepo), reqRepoReader(unit.TypeCode), bind(api.GenerateRepoOption{}), repo.Generate) | |||
Delete(reqToken(), reqOwner(), repo.Delete). | |||
Patch(reqToken(), reqAdmin(), bind(api.EditRepoOption{}), repo.Edit) | |||
m.Post("/generate", reqToken(), reqRepoReader(unit.TypeCode), bind(api.GenerateRepoOption{}), repo.Generate) | |||
m.Group("/transfer", func() { | |||
m.Post("", reqOwner(), bind(api.TransferRepoOption{}), repo.Transfer) | |||
m.Post("/accept", repo.AcceptTransfer) | |||
m.Post("/reject", repo.RejectTransfer) | |||
}, reqToken(auth_model.AccessTokenScopeRepo)) | |||
m.Combo("/notifications", reqToken(auth_model.AccessTokenScopeNotification)). | |||
Get(notify.ListRepoNotifications). | |||
Put(notify.ReadRepoNotifications) | |||
}, reqToken()) | |||
m.Group("/hooks/git", func() { | |||
m.Combo("").Get(reqToken(auth_model.AccessTokenScopeReadRepoHook), repo.ListGitHooks) | |||
m.Combo("").Get(repo.ListGitHooks) | |||
m.Group("/{id}", func() { | |||
m.Combo("").Get(reqToken(auth_model.AccessTokenScopeReadRepoHook), repo.GetGitHook). | |||
Patch(reqToken(auth_model.AccessTokenScopeWriteRepoHook), bind(api.EditGitHookOption{}), repo.EditGitHook). | |||
Delete(reqToken(auth_model.AccessTokenScopeWriteRepoHook), repo.DeleteGitHook) | |||
m.Combo("").Get(repo.GetGitHook). | |||
Patch(bind(api.EditGitHookOption{}), repo.EditGitHook). | |||
Delete(repo.DeleteGitHook) | |||
}) | |||
}, reqAdmin(), reqGitHook(), context.ReferencesGitRepo(true)) | |||
}, reqToken(), reqAdmin(), reqGitHook(), context.ReferencesGitRepo(true)) | |||
m.Group("/hooks", func() { | |||
m.Combo("").Get(reqToken(auth_model.AccessTokenScopeReadRepoHook), repo.ListHooks). | |||
Post(reqToken(auth_model.AccessTokenScopeWriteRepoHook), bind(api.CreateHookOption{}), repo.CreateHook) | |||
m.Combo("").Get(repo.ListHooks). | |||
Post(bind(api.CreateHookOption{}), repo.CreateHook) | |||
m.Group("/{id}", func() { | |||
m.Combo("").Get(reqToken(auth_model.AccessTokenScopeReadRepoHook), repo.GetHook). | |||
Patch(reqToken(auth_model.AccessTokenScopeWriteRepoHook), bind(api.EditHookOption{}), repo.EditHook). | |||
Delete(reqToken(auth_model.AccessTokenScopeWriteRepoHook), repo.DeleteHook) | |||
m.Post("/tests", reqToken(auth_model.AccessTokenScopeReadRepoHook), context.ReferencesGitRepo(), context.RepoRefForAPI, repo.TestHook) | |||
m.Combo("").Get(repo.GetHook). | |||
Patch(bind(api.EditHookOption{}), repo.EditHook). | |||
Delete(repo.DeleteHook) | |||
m.Post("/tests", context.ReferencesGitRepo(), context.RepoRefForAPI, repo.TestHook) | |||
}) | |||
}, reqAdmin(), reqWebhooksEnabled()) | |||
}, reqToken(), reqAdmin(), reqWebhooksEnabled()) | |||
m.Group("/collaborators", func() { | |||
m.Get("", reqAnyRepoReader(), repo.ListCollaborators) | |||
m.Group("/{collaborator}", func() { | |||
@@ -910,25 +960,25 @@ func Routes(ctx gocontext.Context) *web.Route { | |||
Delete(reqAdmin(), repo.DeleteCollaborator) | |||
m.Get("/permission", repo.GetRepoPermissions) | |||
}) | |||
}, reqToken(auth_model.AccessTokenScopeRepo)) | |||
m.Get("/assignees", reqToken(auth_model.AccessTokenScopeRepo), reqAnyRepoReader(), repo.GetAssignees) | |||
m.Get("/reviewers", reqToken(auth_model.AccessTokenScopeRepo), reqAnyRepoReader(), repo.GetReviewers) | |||
}, reqToken()) | |||
m.Get("/assignees", reqToken(), reqAnyRepoReader(), repo.GetAssignees) | |||
m.Get("/reviewers", reqToken(), reqAnyRepoReader(), repo.GetReviewers) | |||
m.Group("/teams", func() { | |||
m.Get("", reqAnyRepoReader(), repo.ListTeams) | |||
m.Combo("/{team}").Get(reqAnyRepoReader(), repo.IsTeam). | |||
Put(reqAdmin(), repo.AddTeam). | |||
Delete(reqAdmin(), repo.DeleteTeam) | |||
}, reqToken(auth_model.AccessTokenScopeRepo)) | |||
}, reqToken()) | |||
m.Get("/raw/*", context.ReferencesGitRepo(), context.RepoRefForAPI, reqRepoReader(unit.TypeCode), repo.GetRawFile) | |||
m.Get("/media/*", context.ReferencesGitRepo(), context.RepoRefForAPI, reqRepoReader(unit.TypeCode), repo.GetRawFileOrLFS) | |||
m.Get("/archive/*", reqRepoReader(unit.TypeCode), repo.GetArchive) | |||
m.Combo("/forks").Get(repo.ListForks). | |||
Post(reqToken(auth_model.AccessTokenScopeRepo), reqRepoReader(unit.TypeCode), bind(api.CreateForkOption{}), repo.CreateFork) | |||
Post(reqToken(), reqRepoReader(unit.TypeCode), bind(api.CreateForkOption{}), repo.CreateFork) | |||
m.Group("/branches", func() { | |||
m.Get("", repo.ListBranches) | |||
m.Get("/*", repo.GetBranch) | |||
m.Delete("/*", reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeCode), repo.DeleteBranch) | |||
m.Post("", reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeCode), bind(api.CreateBranchRepoOption{}), repo.CreateBranch) | |||
m.Delete("/*", reqToken(), reqRepoWriter(unit.TypeCode), repo.DeleteBranch) | |||
m.Post("", reqToken(), reqRepoWriter(unit.TypeCode), bind(api.CreateBranchRepoOption{}), repo.CreateBranch) | |||
}, context.ReferencesGitRepo(), reqRepoReader(unit.TypeCode)) | |||
m.Group("/branch_protections", func() { | |||
m.Get("", repo.ListBranchProtections) | |||
@@ -938,218 +988,112 @@ func Routes(ctx gocontext.Context) *web.Route { | |||
m.Patch("", bind(api.EditBranchProtectionOption{}), repo.EditBranchProtection) | |||
m.Delete("", repo.DeleteBranchProtection) | |||
}) | |||
}, reqToken(auth_model.AccessTokenScopeRepo), reqAdmin()) | |||
}, reqToken(), reqAdmin()) | |||
m.Group("/tags", func() { | |||
m.Get("", repo.ListTags) | |||
m.Get("/*", repo.GetTag) | |||
m.Post("", reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeCode), bind(api.CreateTagOption{}), repo.CreateTag) | |||
m.Delete("/*", reqToken(auth_model.AccessTokenScopeRepo), repo.DeleteTag) | |||
m.Post("", reqToken(), reqRepoWriter(unit.TypeCode), bind(api.CreateTagOption{}), repo.CreateTag) | |||
m.Delete("/*", reqToken(), repo.DeleteTag) | |||
}, reqRepoReader(unit.TypeCode), context.ReferencesGitRepo(true)) | |||
m.Group("/keys", func() { | |||
m.Combo("").Get(repo.ListDeployKeys). | |||
Post(bind(api.CreateKeyOption{}), repo.CreateDeployKey) | |||
m.Combo("/{id}").Get(repo.GetDeployKey). | |||
Delete(repo.DeleteDeploykey) | |||
}, reqToken(auth_model.AccessTokenScopeRepo), reqAdmin()) | |||
}, reqToken(), reqAdmin()) | |||
m.Group("/times", func() { | |||
m.Combo("").Get(repo.ListTrackedTimesByRepository) | |||
m.Combo("/{timetrackingusername}").Get(repo.ListTrackedTimesByUser) | |||
}, mustEnableIssues, reqToken(auth_model.AccessTokenScopeRepo)) | |||
}, mustEnableIssues, reqToken()) | |||
m.Group("/wiki", func() { | |||
m.Combo("/page/{pageName}"). | |||
Get(repo.GetWikiPage). | |||
Patch(mustNotBeArchived, reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeWiki), bind(api.CreateWikiPageOptions{}), repo.EditWikiPage). | |||
Delete(mustNotBeArchived, reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeWiki), repo.DeleteWikiPage) | |||
Patch(mustNotBeArchived, reqToken(), reqRepoWriter(unit.TypeWiki), bind(api.CreateWikiPageOptions{}), repo.EditWikiPage). | |||
Delete(mustNotBeArchived, reqToken(), reqRepoWriter(unit.TypeWiki), repo.DeleteWikiPage) | |||
m.Get("/revisions/{pageName}", repo.ListPageRevisions) | |||
m.Post("/new", mustNotBeArchived, reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeWiki), bind(api.CreateWikiPageOptions{}), repo.NewWikiPage) | |||
m.Post("/new", reqToken(), mustNotBeArchived, reqRepoWriter(unit.TypeWiki), bind(api.CreateWikiPageOptions{}), repo.NewWikiPage) | |||
m.Get("/pages", repo.ListWikiPages) | |||
}, mustEnableWiki) | |||
m.Group("/issues", func() { | |||
m.Combo("").Get(repo.ListIssues). | |||
Post(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, bind(api.CreateIssueOption{}), repo.CreateIssue) | |||
m.Get("/pinned", repo.ListPinnedIssues) | |||
m.Group("/comments", func() { | |||
m.Get("", repo.ListRepoIssueComments) | |||
m.Group("/{id}", func() { | |||
m.Combo(""). | |||
Get(repo.GetIssueComment). | |||
Patch(mustNotBeArchived, reqToken(auth_model.AccessTokenScopeRepo), bind(api.EditIssueCommentOption{}), repo.EditIssueComment). | |||
Delete(reqToken(auth_model.AccessTokenScopeRepo), repo.DeleteIssueComment) | |||
m.Combo("/reactions"). | |||
Get(repo.GetIssueCommentReactions). | |||
Post(reqToken(auth_model.AccessTokenScopeRepo), bind(api.EditReactionOption{}), repo.PostIssueCommentReaction). | |||
Delete(reqToken(auth_model.AccessTokenScopeRepo), bind(api.EditReactionOption{}), repo.DeleteIssueCommentReaction) | |||
m.Group("/assets", func() { | |||
m.Combo(""). | |||
Get(repo.ListIssueCommentAttachments). | |||
Post(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, repo.CreateIssueCommentAttachment) | |||
m.Combo("/{asset}"). | |||
Get(repo.GetIssueCommentAttachment). | |||
Patch(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, bind(api.EditAttachmentOptions{}), repo.EditIssueCommentAttachment). | |||
Delete(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, repo.DeleteIssueCommentAttachment) | |||
}, mustEnableAttachments) | |||
}) | |||
}) | |||
m.Group("/{index}", func() { | |||
m.Combo("").Get(repo.GetIssue). | |||
Patch(reqToken(auth_model.AccessTokenScopeRepo), bind(api.EditIssueOption{}), repo.EditIssue). | |||
Delete(reqToken(auth_model.AccessTokenScopeRepo), reqAdmin(), context.ReferencesGitRepo(), repo.DeleteIssue) | |||
m.Group("/comments", func() { | |||
m.Combo("").Get(repo.ListIssueComments). | |||
Post(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, bind(api.CreateIssueCommentOption{}), repo.CreateIssueComment) | |||
m.Combo("/{id}", reqToken(auth_model.AccessTokenScopeRepo)).Patch(bind(api.EditIssueCommentOption{}), repo.EditIssueCommentDeprecated). | |||
Delete(repo.DeleteIssueCommentDeprecated) | |||
}) | |||
m.Get("/timeline", repo.ListIssueCommentsAndTimeline) | |||
m.Group("/labels", func() { | |||
m.Combo("").Get(repo.ListIssueLabels). | |||
Post(reqToken(auth_model.AccessTokenScopeRepo), bind(api.IssueLabelsOption{}), repo.AddIssueLabels). | |||
Put(reqToken(auth_model.AccessTokenScopeRepo), bind(api.IssueLabelsOption{}), repo.ReplaceIssueLabels). | |||
Delete(reqToken(auth_model.AccessTokenScopeRepo), repo.ClearIssueLabels) | |||
m.Delete("/{id}", reqToken(auth_model.AccessTokenScopeRepo), repo.DeleteIssueLabel) | |||
}) | |||
m.Group("/times", func() { | |||
m.Combo(""). | |||
Get(repo.ListTrackedTimes). | |||
Post(bind(api.AddTimeOption{}), repo.AddTime). | |||
Delete(repo.ResetIssueTime) | |||
m.Delete("/{id}", repo.DeleteTime) | |||
}, reqToken(auth_model.AccessTokenScopeRepo)) | |||
m.Combo("/deadline").Post(reqToken(auth_model.AccessTokenScopeRepo), bind(api.EditDeadlineOption{}), repo.UpdateIssueDeadline) | |||
m.Group("/stopwatch", func() { | |||
m.Post("/start", reqToken(auth_model.AccessTokenScopeRepo), repo.StartIssueStopwatch) | |||
m.Post("/stop", reqToken(auth_model.AccessTokenScopeRepo), repo.StopIssueStopwatch) | |||
m.Delete("/delete", reqToken(auth_model.AccessTokenScopeRepo), repo.DeleteIssueStopwatch) | |||
}) | |||
m.Group("/subscriptions", func() { | |||
m.Get("", repo.GetIssueSubscribers) | |||
m.Get("/check", reqToken(auth_model.AccessTokenScopeRepo), repo.CheckIssueSubscription) | |||
m.Put("/{user}", reqToken(auth_model.AccessTokenScopeRepo), repo.AddIssueSubscription) | |||
m.Delete("/{user}", reqToken(auth_model.AccessTokenScopeRepo), repo.DelIssueSubscription) | |||
}) | |||
m.Combo("/reactions"). | |||
Get(repo.GetIssueReactions). | |||
Post(reqToken(auth_model.AccessTokenScopeRepo), bind(api.EditReactionOption{}), repo.PostIssueReaction). | |||
Delete(reqToken(auth_model.AccessTokenScopeRepo), bind(api.EditReactionOption{}), repo.DeleteIssueReaction) | |||
m.Group("/assets", func() { | |||
m.Combo(""). | |||
Get(repo.ListIssueAttachments). | |||
Post(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, repo.CreateIssueAttachment) | |||
m.Combo("/{asset}"). | |||
Get(repo.GetIssueAttachment). | |||
Patch(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, bind(api.EditAttachmentOptions{}), repo.EditIssueAttachment). | |||
Delete(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, repo.DeleteIssueAttachment) | |||
}, mustEnableAttachments) | |||
m.Combo("/dependencies"). | |||
Get(repo.GetIssueDependencies). | |||
Post(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, bind(api.IssueMeta{}), repo.CreateIssueDependency). | |||
Delete(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, bind(api.IssueMeta{}), repo.RemoveIssueDependency) | |||
m.Combo("/blocks"). | |||
Get(repo.GetIssueBlocks). | |||
Post(reqToken(auth_model.AccessTokenScopeRepo), bind(api.IssueMeta{}), repo.CreateIssueBlocking). | |||
Delete(reqToken(auth_model.AccessTokenScopeRepo), bind(api.IssueMeta{}), repo.RemoveIssueBlocking) | |||
m.Group("/pin", func() { | |||
m.Combo(""). | |||
Post(reqToken(auth_model.AccessTokenScopeRepo), reqAdmin(), repo.PinIssue). | |||
Delete(reqToken(auth_model.AccessTokenScopeRepo), reqAdmin(), repo.UnpinIssue) | |||
m.Patch("/{position}", reqToken(auth_model.AccessTokenScopeRepo), reqAdmin(), repo.MoveIssuePin) | |||
}) | |||
}) | |||
}, mustEnableIssuesOrPulls) | |||
m.Group("/labels", func() { | |||
m.Combo("").Get(repo.ListLabels). | |||
Post(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.CreateLabelOption{}), repo.CreateLabel) | |||
m.Combo("/{id}").Get(repo.GetLabel). | |||
Patch(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.EditLabelOption{}), repo.EditLabel). | |||
Delete(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), repo.DeleteLabel) | |||
}) | |||
m.Post("/markup", reqToken(auth_model.AccessTokenScopeRepo), bind(api.MarkupOption{}), misc.Markup) | |||
m.Post("/markdown", reqToken(auth_model.AccessTokenScopeRepo), bind(api.MarkdownOption{}), misc.Markdown) | |||
m.Post("/markdown/raw", reqToken(auth_model.AccessTokenScopeRepo), misc.MarkdownRaw) | |||
m.Group("/milestones", func() { | |||
m.Combo("").Get(repo.ListMilestones). | |||
Post(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.CreateMilestoneOption{}), repo.CreateMilestone) | |||
m.Combo("/{id}").Get(repo.GetMilestone). | |||
Patch(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.EditMilestoneOption{}), repo.EditMilestone). | |||
Delete(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), repo.DeleteMilestone) | |||
}) | |||
m.Post("/markup", reqToken(), bind(api.MarkupOption{}), misc.Markup) | |||
m.Post("/markdown", reqToken(), bind(api.MarkdownOption{}), misc.Markdown) | |||
m.Post("/markdown/raw", reqToken(), misc.MarkdownRaw) | |||
m.Get("/stargazers", repo.ListStargazers) | |||
m.Get("/subscribers", repo.ListSubscribers) | |||
m.Group("/subscription", func() { | |||
m.Get("", user.IsWatching) | |||
m.Put("", reqToken(auth_model.AccessTokenScopeRepo), user.Watch) | |||
m.Delete("", reqToken(auth_model.AccessTokenScopeRepo), user.Unwatch) | |||
m.Put("", reqToken(), user.Watch) | |||
m.Delete("", reqToken(), user.Unwatch) | |||
}) | |||
m.Group("/releases", func() { | |||
m.Combo("").Get(repo.ListReleases). | |||
Post(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeReleases), context.ReferencesGitRepo(), bind(api.CreateReleaseOption{}), repo.CreateRelease) | |||
Post(reqToken(), reqRepoWriter(unit.TypeReleases), context.ReferencesGitRepo(), bind(api.CreateReleaseOption{}), repo.CreateRelease) | |||
m.Combo("/latest").Get(repo.GetLatestRelease) | |||
m.Group("/{id}", func() { | |||
m.Combo("").Get(repo.GetRelease). | |||
Patch(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeReleases), context.ReferencesGitRepo(), bind(api.EditReleaseOption{}), repo.EditRelease). | |||
Delete(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeReleases), repo.DeleteRelease) | |||
Patch(reqToken(), reqRepoWriter(unit.TypeReleases), context.ReferencesGitRepo(), bind(api.EditReleaseOption{}), repo.EditRelease). | |||
Delete(reqToken(), reqRepoWriter(unit.TypeReleases), repo.DeleteRelease) | |||
m.Group("/assets", func() { | |||
m.Combo("").Get(repo.ListReleaseAttachments). | |||
Post(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeReleases), repo.CreateReleaseAttachment) | |||
Post(reqToken(), reqRepoWriter(unit.TypeReleases), repo.CreateReleaseAttachment) | |||
m.Combo("/{asset}").Get(repo.GetReleaseAttachment). | |||
Patch(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeReleases), bind(api.EditAttachmentOptions{}), repo.EditReleaseAttachment). | |||
Delete(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeReleases), repo.DeleteReleaseAttachment) | |||
Patch(reqToken(), reqRepoWriter(unit.TypeReleases), bind(api.EditAttachmentOptions{}), repo.EditReleaseAttachment). | |||
Delete(reqToken(), reqRepoWriter(unit.TypeReleases), repo.DeleteReleaseAttachment) | |||
}) | |||
}) | |||
m.Group("/tags", func() { | |||
m.Combo("/{tag}"). | |||
Get(repo.GetReleaseByTag). | |||
Delete(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeReleases), repo.DeleteReleaseByTag) | |||
Delete(reqToken(), reqRepoWriter(unit.TypeReleases), repo.DeleteReleaseByTag) | |||
}) | |||
}, reqRepoReader(unit.TypeReleases)) | |||
m.Post("/mirror-sync", reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeCode), repo.MirrorSync) | |||
m.Post("/push_mirrors-sync", reqAdmin(), reqToken(auth_model.AccessTokenScopeRepo), repo.PushMirrorSync) | |||
m.Post("/mirror-sync", reqToken(), reqRepoWriter(unit.TypeCode), repo.MirrorSync) | |||
m.Post("/push_mirrors-sync", reqAdmin(), reqToken(), repo.PushMirrorSync) | |||
m.Group("/push_mirrors", func() { | |||
m.Combo("").Get(repo.ListPushMirrors). | |||
Post(bind(api.CreatePushMirrorOption{}), repo.AddPushMirror) | |||
m.Combo("/{name}"). | |||
Delete(repo.DeletePushMirrorByRemoteName). | |||
Get(repo.GetPushMirrorByName) | |||
}, reqAdmin(), reqToken(auth_model.AccessTokenScopeRepo)) | |||
}, reqAdmin(), reqToken()) | |||
m.Get("/editorconfig/{filename}", context.ReferencesGitRepo(), context.RepoRefForAPI, reqRepoReader(unit.TypeCode), repo.GetEditorconfig) | |||
m.Group("/pulls", func() { | |||
m.Combo("").Get(repo.ListPullRequests). | |||
Post(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, bind(api.CreatePullRequestOption{}), repo.CreatePullRequest) | |||
Post(reqToken(), mustNotBeArchived, bind(api.CreatePullRequestOption{}), repo.CreatePullRequest) | |||
m.Get("/pinned", repo.ListPinnedPullRequests) | |||
m.Group("/{index}", func() { | |||
m.Combo("").Get(repo.GetPullRequest). | |||
Patch(reqToken(auth_model.AccessTokenScopeRepo), bind(api.EditPullRequestOption{}), repo.EditPullRequest) | |||
Patch(reqToken(), bind(api.EditPullRequestOption{}), repo.EditPullRequest) | |||
m.Get(".{diffType:diff|patch}", repo.DownloadPullDiffOrPatch) | |||
m.Post("/update", reqToken(auth_model.AccessTokenScopeRepo), repo.UpdatePullRequest) | |||
m.Post("/update", reqToken(), repo.UpdatePullRequest) | |||
m.Get("/commits", repo.GetPullRequestCommits) | |||
m.Get("/files", repo.GetPullRequestFiles) | |||
m.Combo("/merge").Get(repo.IsPullRequestMerged). | |||
Post(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, bind(forms.MergePullRequestForm{}), repo.MergePullRequest). | |||
Delete(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, repo.CancelScheduledAutoMerge) | |||
Post(reqToken(), mustNotBeArchived, bind(forms.MergePullRequestForm{}), repo.MergePullRequest). | |||
Delete(reqToken(), mustNotBeArchived, repo.CancelScheduledAutoMerge) | |||
m.Group("/reviews", func() { | |||
m.Combo(""). | |||
Get(repo.ListPullReviews). | |||
Post(reqToken(auth_model.AccessTokenScopeRepo), bind(api.CreatePullReviewOptions{}), repo.CreatePullReview) | |||
Post(reqToken(), bind(api.CreatePullReviewOptions{}), repo.CreatePullReview) | |||
m.Group("/{id}", func() { | |||
m.Combo(""). | |||
Get(repo.GetPullReview). | |||
Delete(reqToken(auth_model.AccessTokenScopeRepo), repo.DeletePullReview). | |||
Post(reqToken(auth_model.AccessTokenScopeRepo), bind(api.SubmitPullReviewOptions{}), repo.SubmitPullReview) | |||
Delete(reqToken(), repo.DeletePullReview). | |||
Post(reqToken(), bind(api.SubmitPullReviewOptions{}), repo.SubmitPullReview) | |||
m.Combo("/comments"). | |||
Get(repo.GetPullReviewComments) | |||
m.Post("/dismissals", reqToken(auth_model.AccessTokenScopeRepo), bind(api.DismissPullReviewOptions{}), repo.DismissPullReview) | |||
m.Post("/undismissals", reqToken(auth_model.AccessTokenScopeRepo), repo.UnDismissPullReview) | |||
m.Post("/dismissals", reqToken(), bind(api.DismissPullReviewOptions{}), repo.DismissPullReview) | |||
m.Post("/undismissals", reqToken(), repo.UnDismissPullReview) | |||
}) | |||
}) | |||
m.Combo("/requested_reviewers", reqToken(auth_model.AccessTokenScopeRepo)). | |||
m.Combo("/requested_reviewers", reqToken()). | |||
Delete(bind(api.PullReviewRequestOptions{}), repo.DeleteReviewRequests). | |||
Post(bind(api.PullReviewRequestOptions{}), repo.CreateReviewRequests) | |||
}) | |||
}, mustAllowPulls, reqRepoReader(unit.TypeCode), context.ReferencesGitRepo()) | |||
m.Group("/statuses", func() { | |||
m.Combo("/{sha}").Get(repo.GetCommitStatuses). | |||
Post(reqToken(auth_model.AccessTokenScopeRepoStatus), reqRepoWriter(unit.TypeCode), bind(api.CreateStatusOption{}), repo.NewCommitStatus) | |||
Post(reqToken(), reqRepoWriter(unit.TypeCode), bind(api.CreateStatusOption{}), repo.NewCommitStatus) | |||
}, reqRepoReader(unit.TypeCode)) | |||
m.Group("/commits", func() { | |||
m.Get("", context.ReferencesGitRepo(), repo.GetAllCommits) | |||
@@ -1170,24 +1114,24 @@ func Routes(ctx gocontext.Context) *web.Route { | |||
m.Get("/tags/{sha}", repo.GetAnnotatedTag) | |||
m.Get("/notes/{sha}", repo.GetNote) | |||
}, context.ReferencesGitRepo(true), reqRepoReader(unit.TypeCode)) | |||
m.Post("/diffpatch", reqRepoWriter(unit.TypeCode), reqToken(auth_model.AccessTokenScopeRepo), bind(api.ApplyDiffPatchFileOptions{}), repo.ApplyDiffPatch) | |||
m.Post("/diffpatch", reqRepoWriter(unit.TypeCode), reqToken(), bind(api.ApplyDiffPatchFileOptions{}), repo.ApplyDiffPatch) | |||
m.Group("/contents", func() { | |||
m.Get("", repo.GetContentsList) | |||
m.Post("", reqToken(auth_model.AccessTokenScopeRepo), bind(api.ChangeFilesOptions{}), reqRepoBranchWriter, repo.ChangeFiles) | |||
m.Post("", reqToken(), bind(api.ChangeFilesOptions{}), reqRepoBranchWriter, repo.ChangeFiles) | |||
m.Get("/*", repo.GetContents) | |||
m.Group("/*", func() { | |||
m.Post("", bind(api.CreateFileOptions{}), reqRepoBranchWriter, repo.CreateFile) | |||
m.Put("", bind(api.UpdateFileOptions{}), reqRepoBranchWriter, repo.UpdateFile) | |||
m.Delete("", bind(api.DeleteFileOptions{}), reqRepoBranchWriter, repo.DeleteFile) | |||
}, reqToken(auth_model.AccessTokenScopeRepo)) | |||
}, reqToken()) | |||
}, reqRepoReader(unit.TypeCode)) | |||
m.Get("/signing-key.gpg", misc.SigningKey) | |||
m.Group("/topics", func() { | |||
m.Combo("").Get(repo.ListTopics). | |||
Put(reqToken(auth_model.AccessTokenScopeRepo), reqAdmin(), bind(api.RepoTopicOptions{}), repo.UpdateTopics) | |||
Put(reqToken(), reqAdmin(), bind(api.RepoTopicOptions{}), repo.UpdateTopics) | |||
m.Group("/{topic}", func() { | |||
m.Combo("").Put(reqToken(auth_model.AccessTokenScopeRepo), repo.AddTopic). | |||
Delete(reqToken(auth_model.AccessTokenScopeRepo), repo.DeleteTopic) | |||
m.Combo("").Put(reqToken(), repo.AddTopic). | |||
Delete(reqToken(), repo.DeleteTopic) | |||
}, reqAdmin()) | |||
}, reqAnyRepoReader()) | |||
m.Get("/issue_templates", context.ReferencesGitRepo(), repo.GetIssueTemplates) | |||
@@ -1197,54 +1141,177 @@ func Routes(ctx gocontext.Context) *web.Route { | |||
m.Get("/activities/feeds", repo.ListRepoActivityFeeds) | |||
m.Get("/new_pin_allowed", repo.AreNewIssuePinsAllowed) | |||
}, repoAssignment()) | |||
}) | |||
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository)) | |||
// Notifications (requires notifications scope) | |||
m.Group("/repos", func() { | |||
m.Group("/{username}/{reponame}", func() { | |||
m.Combo("/notifications", reqToken()). | |||
Get(notify.ListRepoNotifications). | |||
Put(notify.ReadRepoNotifications) | |||
}, repoAssignment()) | |||
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryNotification)) | |||
// Issue (requires issue scope) | |||
m.Group("/repos", func() { | |||
m.Get("/issues/search", repo.SearchIssues) | |||
m.Group("/{username}/{reponame}", func() { | |||
m.Group("/issues", func() { | |||
m.Combo("").Get(repo.ListIssues). | |||
Post(reqToken(), mustNotBeArchived, bind(api.CreateIssueOption{}), repo.CreateIssue) | |||
m.Get("/pinned", repo.ListPinnedIssues) | |||
m.Group("/comments", func() { | |||
m.Get("", repo.ListRepoIssueComments) | |||
m.Group("/{id}", func() { | |||
m.Combo(""). | |||
Get(repo.GetIssueComment). | |||
Patch(mustNotBeArchived, reqToken(), bind(api.EditIssueCommentOption{}), repo.EditIssueComment). | |||
Delete(reqToken(), repo.DeleteIssueComment) | |||
m.Combo("/reactions"). | |||
Get(repo.GetIssueCommentReactions). | |||
Post(reqToken(), bind(api.EditReactionOption{}), repo.PostIssueCommentReaction). | |||
Delete(reqToken(), bind(api.EditReactionOption{}), repo.DeleteIssueCommentReaction) | |||
m.Group("/assets", func() { | |||
m.Combo(""). | |||
Get(repo.ListIssueCommentAttachments). | |||
Post(reqToken(), mustNotBeArchived, repo.CreateIssueCommentAttachment) | |||
m.Combo("/{asset}"). | |||
Get(repo.GetIssueCommentAttachment). | |||
Patch(reqToken(), mustNotBeArchived, bind(api.EditAttachmentOptions{}), repo.EditIssueCommentAttachment). | |||
Delete(reqToken(), mustNotBeArchived, repo.DeleteIssueCommentAttachment) | |||
}, mustEnableAttachments) | |||
}) | |||
}) | |||
m.Group("/{index}", func() { | |||
m.Combo("").Get(repo.GetIssue). | |||
Patch(reqToken(), bind(api.EditIssueOption{}), repo.EditIssue). | |||
Delete(reqToken(), reqAdmin(), context.ReferencesGitRepo(), repo.DeleteIssue) | |||
m.Group("/comments", func() { | |||
m.Combo("").Get(repo.ListIssueComments). | |||
Post(reqToken(), mustNotBeArchived, bind(api.CreateIssueCommentOption{}), repo.CreateIssueComment) | |||
m.Combo("/{id}", reqToken()).Patch(bind(api.EditIssueCommentOption{}), repo.EditIssueCommentDeprecated). | |||
Delete(repo.DeleteIssueCommentDeprecated) | |||
}) | |||
m.Get("/timeline", repo.ListIssueCommentsAndTimeline) | |||
m.Group("/labels", func() { | |||
m.Combo("").Get(repo.ListIssueLabels). | |||
Post(reqToken(), bind(api.IssueLabelsOption{}), repo.AddIssueLabels). | |||
Put(reqToken(), bind(api.IssueLabelsOption{}), repo.ReplaceIssueLabels). | |||
Delete(reqToken(), repo.ClearIssueLabels) | |||
m.Delete("/{id}", reqToken(), repo.DeleteIssueLabel) | |||
}) | |||
m.Group("/times", func() { | |||
m.Combo(""). | |||
Get(repo.ListTrackedTimes). | |||
Post(bind(api.AddTimeOption{}), repo.AddTime). | |||
Delete(repo.ResetIssueTime) | |||
m.Delete("/{id}", repo.DeleteTime) | |||
}, reqToken()) | |||
m.Combo("/deadline").Post(reqToken(), bind(api.EditDeadlineOption{}), repo.UpdateIssueDeadline) | |||
m.Group("/stopwatch", func() { | |||
m.Post("/start", repo.StartIssueStopwatch) | |||
m.Post("/stop", repo.StopIssueStopwatch) | |||
m.Delete("/delete", repo.DeleteIssueStopwatch) | |||
}, reqToken()) | |||
m.Group("/subscriptions", func() { | |||
m.Get("", repo.GetIssueSubscribers) | |||
m.Get("/check", reqToken(), repo.CheckIssueSubscription) | |||
m.Put("/{user}", reqToken(), repo.AddIssueSubscription) | |||
m.Delete("/{user}", reqToken(), repo.DelIssueSubscription) | |||
}) | |||
m.Combo("/reactions"). | |||
Get(repo.GetIssueReactions). | |||
Post(reqToken(), bind(api.EditReactionOption{}), repo.PostIssueReaction). | |||
Delete(reqToken(), bind(api.EditReactionOption{}), repo.DeleteIssueReaction) | |||
m.Group("/assets", func() { | |||
m.Combo(""). | |||
Get(repo.ListIssueAttachments). | |||
Post(reqToken(), mustNotBeArchived, repo.CreateIssueAttachment) | |||
m.Combo("/{asset}"). | |||
Get(repo.GetIssueAttachment). | |||
Patch(reqToken(), mustNotBeArchived, bind(api.EditAttachmentOptions{}), repo.EditIssueAttachment). | |||
Delete(reqToken(), mustNotBeArchived, repo.DeleteIssueAttachment) | |||
}, mustEnableAttachments) | |||
m.Combo("/dependencies"). | |||
Get(repo.GetIssueDependencies). | |||
Post(reqToken(), mustNotBeArchived, bind(api.IssueMeta{}), repo.CreateIssueDependency). | |||
Delete(reqToken(), mustNotBeArchived, bind(api.IssueMeta{}), repo.RemoveIssueDependency) | |||
m.Combo("/blocks"). | |||
Get(repo.GetIssueBlocks). | |||
Post(reqToken(), bind(api.IssueMeta{}), repo.CreateIssueBlocking). | |||
Delete(reqToken(), bind(api.IssueMeta{}), repo.RemoveIssueBlocking) | |||
m.Group("/pin", func() { | |||
m.Combo(""). | |||
Post(reqToken(), reqAdmin(), repo.PinIssue). | |||
Delete(reqToken(), reqAdmin(), repo.UnpinIssue) | |||
m.Patch("/{position}", reqToken(), reqAdmin(), repo.MoveIssuePin) | |||
}) | |||
}) | |||
}, mustEnableIssuesOrPulls) | |||
m.Group("/labels", func() { | |||
m.Combo("").Get(repo.ListLabels). | |||
Post(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.CreateLabelOption{}), repo.CreateLabel) | |||
m.Combo("/{id}").Get(repo.GetLabel). | |||
Patch(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.EditLabelOption{}), repo.EditLabel). | |||
Delete(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), repo.DeleteLabel) | |||
}) | |||
m.Group("/milestones", func() { | |||
m.Combo("").Get(repo.ListMilestones). | |||
Post(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.CreateMilestoneOption{}), repo.CreateMilestone) | |||
m.Combo("/{id}").Get(repo.GetMilestone). | |||
Patch(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.EditMilestoneOption{}), repo.EditMilestone). | |||
Delete(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), repo.DeleteMilestone) | |||
}) | |||
}, repoAssignment()) | |||
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryIssue)) | |||
// NOTE: these are Gitea package management API - see packages.CommonRoutes and packages.DockerContainerRoutes for endpoints that implement package manager APIs | |||
m.Group("/packages/{username}", func() { | |||
m.Group("/{type}/{name}/{version}", func() { | |||
m.Get("", reqToken(auth_model.AccessTokenScopeReadPackage), packages.GetPackage) | |||
m.Delete("", reqToken(auth_model.AccessTokenScopeDeletePackage), reqPackageAccess(perm.AccessModeWrite), packages.DeletePackage) | |||
m.Get("/files", reqToken(auth_model.AccessTokenScopeReadPackage), packages.ListPackageFiles) | |||
m.Get("", reqToken(), packages.GetPackage) | |||
m.Delete("", reqToken(), reqPackageAccess(perm.AccessModeWrite), packages.DeletePackage) | |||
m.Get("/files", reqToken(), packages.ListPackageFiles) | |||
}) | |||
m.Get("/", reqToken(auth_model.AccessTokenScopeReadPackage), packages.ListPackages) | |||
}, context_service.UserAssignmentAPI(), context.PackageAssignmentAPI(), reqPackageAccess(perm.AccessModeRead)) | |||
m.Get("/", reqToken(), packages.ListPackages) | |||
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryPackage), context_service.UserAssignmentAPI(), context.PackageAssignmentAPI(), reqPackageAccess(perm.AccessModeRead)) | |||
// Organizations | |||
m.Get("/user/orgs", reqToken(auth_model.AccessTokenScopeReadOrg), org.ListMyOrgs) | |||
m.Get("/user/orgs", reqToken(), tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser, auth_model.AccessTokenScopeCategoryOrganization), org.ListMyOrgs) | |||
m.Group("/users/{username}/orgs", func() { | |||
m.Get("", reqToken(auth_model.AccessTokenScopeReadOrg), org.ListUserOrgs) | |||
m.Get("/{org}/permissions", reqToken(auth_model.AccessTokenScopeReadOrg), org.GetUserOrgsPermissions) | |||
}, context_service.UserAssignmentAPI()) | |||
m.Post("/orgs", reqToken(auth_model.AccessTokenScopeWriteOrg), bind(api.CreateOrgOption{}), org.Create) | |||
m.Get("/orgs", org.GetAll) | |||
m.Get("", reqToken(), org.ListUserOrgs) | |||
m.Get("/{org}/permissions", reqToken(), org.GetUserOrgsPermissions) | |||
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser, auth_model.AccessTokenScopeCategoryOrganization), context_service.UserAssignmentAPI()) | |||
m.Post("/orgs", tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), reqToken(), bind(api.CreateOrgOption{}), org.Create) | |||
m.Get("/orgs", org.GetAll, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization)) | |||
m.Group("/orgs/{org}", func() { | |||
m.Combo("").Get(org.Get). | |||
Patch(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), bind(api.EditOrgOption{}), org.Edit). | |||
Delete(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), org.Delete) | |||
Patch(reqToken(), reqOrgOwnership(), bind(api.EditOrgOption{}), org.Edit). | |||
Delete(reqToken(), reqOrgOwnership(), org.Delete) | |||
m.Combo("/repos").Get(user.ListOrgRepos). | |||
Post(reqToken(auth_model.AccessTokenScopeWriteOrg), bind(api.CreateRepoOption{}), repo.CreateOrgRepo) | |||
Post(reqToken(), bind(api.CreateRepoOption{}), repo.CreateOrgRepo) | |||
m.Group("/members", func() { | |||
m.Get("", reqToken(auth_model.AccessTokenScopeReadOrg), org.ListMembers) | |||
m.Combo("/{username}").Get(reqToken(auth_model.AccessTokenScopeReadOrg), org.IsMember). | |||
Delete(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), org.DeleteMember) | |||
m.Get("", reqToken(), org.ListMembers) | |||
m.Combo("/{username}").Get(reqToken(), org.IsMember). | |||
Delete(reqToken(), reqOrgOwnership(), org.DeleteMember) | |||
}) | |||
m.Group("/public_members", func() { | |||
m.Get("", org.ListPublicMembers) | |||
m.Combo("/{username}").Get(org.IsPublicMember). | |||
Put(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgMembership(), org.PublicizeMember). | |||
Delete(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgMembership(), org.ConcealMember) | |||
Put(reqToken(), reqOrgMembership(), org.PublicizeMember). | |||
Delete(reqToken(), reqOrgMembership(), org.ConcealMember) | |||
}) | |||
m.Group("/teams", func() { | |||
m.Get("", reqToken(auth_model.AccessTokenScopeReadOrg), org.ListTeams) | |||
m.Post("", reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), bind(api.CreateTeamOption{}), org.CreateTeam) | |||
m.Get("/search", reqToken(auth_model.AccessTokenScopeReadOrg), org.SearchTeam) | |||
m.Get("", reqToken(), org.ListTeams) | |||
m.Post("", reqToken(), reqOrgOwnership(), bind(api.CreateTeamOption{}), org.CreateTeam) | |||
m.Get("/search", reqToken(), org.SearchTeam) | |||
}, reqOrgMembership()) | |||
m.Group("/labels", func() { | |||
m.Get("", org.ListLabels) | |||
m.Post("", reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), bind(api.CreateLabelOption{}), org.CreateLabel) | |||
m.Combo("/{id}").Get(reqToken(auth_model.AccessTokenScopeReadOrg), org.GetLabel). | |||
Patch(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), bind(api.EditLabelOption{}), org.EditLabel). | |||
Delete(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), org.DeleteLabel) | |||
m.Post("", reqToken(), reqOrgOwnership(), bind(api.CreateLabelOption{}), org.CreateLabel) | |||
m.Combo("/{id}").Get(reqToken(), org.GetLabel). | |||
Patch(reqToken(), reqOrgOwnership(), bind(api.EditLabelOption{}), org.EditLabel). | |||
Delete(reqToken(), reqOrgOwnership(), org.DeleteLabel) | |||
}) | |||
m.Group("/hooks", func() { | |||
m.Combo("").Get(org.ListHooks). | |||
@@ -1252,29 +1319,29 @@ func Routes(ctx gocontext.Context) *web.Route { | |||
m.Combo("/{id}").Get(org.GetHook). | |||
Patch(bind(api.EditHookOption{}), org.EditHook). | |||
Delete(org.DeleteHook) | |||
}, reqToken(auth_model.AccessTokenScopeAdminOrgHook), reqOrgOwnership(), reqWebhooksEnabled()) | |||
}, reqToken(), reqOrgOwnership(), reqWebhooksEnabled()) | |||
m.Get("/activities/feeds", org.ListOrgActivityFeeds) | |||
}, orgAssignment(true)) | |||
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), orgAssignment(true)) | |||
m.Group("/teams/{teamid}", func() { | |||
m.Combo("").Get(reqToken(auth_model.AccessTokenScopeReadOrg), org.GetTeam). | |||
Patch(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), bind(api.EditTeamOption{}), org.EditTeam). | |||
Delete(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), org.DeleteTeam) | |||
m.Combo("").Get(reqToken(), org.GetTeam). | |||
Patch(reqToken(), reqOrgOwnership(), bind(api.EditTeamOption{}), org.EditTeam). | |||
Delete(reqToken(), reqOrgOwnership(), org.DeleteTeam) | |||
m.Group("/members", func() { | |||
m.Get("", reqToken(auth_model.AccessTokenScopeReadOrg), org.GetTeamMembers) | |||
m.Get("", reqToken(), org.GetTeamMembers) | |||
m.Combo("/{username}"). | |||
Get(reqToken(auth_model.AccessTokenScopeReadOrg), org.GetTeamMember). | |||
Put(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), org.AddTeamMember). | |||
Delete(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), org.RemoveTeamMember) | |||
Get(reqToken(), org.GetTeamMember). | |||
Put(reqToken(), reqOrgOwnership(), org.AddTeamMember). | |||
Delete(reqToken(), reqOrgOwnership(), org.RemoveTeamMember) | |||
}) | |||
m.Group("/repos", func() { | |||
m.Get("", reqToken(auth_model.AccessTokenScopeReadOrg), org.GetTeamRepos) | |||
m.Get("", reqToken(), org.GetTeamRepos) | |||
m.Combo("/{org}/{reponame}"). | |||
Put(reqToken(auth_model.AccessTokenScopeWriteOrg), org.AddTeamRepository). | |||
Delete(reqToken(auth_model.AccessTokenScopeWriteOrg), org.RemoveTeamRepository). | |||
Get(reqToken(auth_model.AccessTokenScopeReadOrg), org.GetTeamRepo) | |||
Put(reqToken(), org.AddTeamRepository). | |||
Delete(reqToken(), org.RemoveTeamRepository). | |||
Get(reqToken(), org.GetTeamRepo) | |||
}) | |||
m.Get("/activities/feeds", org.ListTeamActivityFeeds) | |||
}, orgAssignment(false, true), reqToken(""), reqTeamMembership()) | |||
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), orgAssignment(false, true), reqToken(), reqTeamMembership()) | |||
m.Group("/admin", func() { | |||
m.Group("/cron", func() { | |||
@@ -1314,11 +1381,11 @@ func Routes(ctx gocontext.Context) *web.Route { | |||
Patch(bind(api.EditHookOption{}), admin.EditHook). | |||
Delete(admin.DeleteHook) | |||
}) | |||
}, reqToken(auth_model.AccessTokenScopeSudo), reqSiteAdmin()) | |||
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryAdmin), reqToken(), reqSiteAdmin()) | |||
m.Group("/topics", func() { | |||
m.Get("/search", repo.TopicSearch) | |||
}) | |||
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository)) | |||
}, sudo()) | |||
return m |
@@ -152,7 +152,7 @@ func httpBase(ctx *context.Context) (h *serviceHandler) { | |||
return | |||
} | |||
context.CheckRepoScopedToken(ctx, repo) | |||
context.CheckRepoScopedToken(ctx, repo, auth_model.GetScopeLevelFromAccessMode(accessMode)) | |||
if ctx.Written() { | |||
return | |||
} |
@@ -89,6 +89,7 @@ func DeleteApplication(ctx *context.Context) { | |||
} | |||
func loadApplicationsData(ctx *context.Context) { | |||
ctx.Data["AccessTokenScopePublicOnly"] = auth_model.AccessTokenScopePublicOnly | |||
tokens, err := auth_model.ListAccessTokens(auth_model.ListAccessTokensOptions{UserID: ctx.Doer.ID}) | |||
if err != nil { | |||
ctx.ServerError("ListAccessTokens", err) | |||
@@ -96,6 +97,7 @@ func loadApplicationsData(ctx *context.Context) { | |||
} | |||
ctx.Data["Tokens"] = tokens | |||
ctx.Data["EnableOAuth2"] = setting.OAuth2.Enable | |||
ctx.Data["IsAdmin"] = ctx.Doer.IsAdmin | |||
if setting.OAuth2.Enable { | |||
ctx.Data["Applications"], err = auth_model.GetOAuth2ApplicationsByUserID(ctx, ctx.Doer.ID) | |||
if err != nil { |
@@ -112,12 +112,12 @@ func TestNewAccessTokenForm_GetScope(t *testing.T) { | |||
expectedErr error | |||
}{ | |||
{ | |||
form: NewAccessTokenForm{Name: "test", Scope: []string{"repo"}}, | |||
scope: "repo", | |||
form: NewAccessTokenForm{Name: "test", Scope: []string{"read:repository"}}, | |||
scope: "read:repository", | |||
}, | |||
{ | |||
form: NewAccessTokenForm{Name: "test", Scope: []string{"repo", "user"}}, | |||
scope: "repo,user", | |||
form: NewAccessTokenForm{Name: "test", Scope: []string{"read:repository", "write:user"}}, | |||
scope: "read:repository,write:user", | |||
}, | |||
} | |||
@@ -8,6 +8,7 @@ import ( | |||
"strconv" | |||
"strings" | |||
auth_model "code.gitea.io/gitea/models/auth" | |||
git_model "code.gitea.io/gitea/models/git" | |||
repo_model "code.gitea.io/gitea/models/repo" | |||
"code.gitea.io/gitea/modules/context" | |||
@@ -58,7 +59,7 @@ func GetListLockHandler(ctx *context.Context) { | |||
} | |||
repository.MustOwner(ctx) | |||
context.CheckRepoScopedToken(ctx, repository) | |||
context.CheckRepoScopedToken(ctx, repository, auth_model.Read) | |||
if ctx.Written() { | |||
return | |||
} | |||
@@ -150,7 +151,7 @@ func PostLockHandler(ctx *context.Context) { | |||
} | |||
repository.MustOwner(ctx) | |||
context.CheckRepoScopedToken(ctx, repository) | |||
context.CheckRepoScopedToken(ctx, repository, auth_model.Write) | |||
if ctx.Written() { | |||
return | |||
} | |||
@@ -222,7 +223,7 @@ func VerifyLockHandler(ctx *context.Context) { | |||
} | |||
repository.MustOwner(ctx) | |||
context.CheckRepoScopedToken(ctx, repository) | |||
context.CheckRepoScopedToken(ctx, repository, auth_model.Read) | |||
if ctx.Written() { | |||
return | |||
} | |||
@@ -293,7 +294,7 @@ func UnLockHandler(ctx *context.Context) { | |||
} | |||
repository.MustOwner(ctx) | |||
context.CheckRepoScopedToken(ctx, repository) | |||
context.CheckRepoScopedToken(ctx, repository, auth_model.Write) | |||
if ctx.Written() { | |||
return | |||
} |
@@ -18,6 +18,7 @@ import ( | |||
"strings" | |||
actions_model "code.gitea.io/gitea/models/actions" | |||
auth_model "code.gitea.io/gitea/models/auth" | |||
git_model "code.gitea.io/gitea/models/git" | |||
"code.gitea.io/gitea/models/perm" | |||
access_model "code.gitea.io/gitea/models/perm/access" | |||
@@ -423,7 +424,12 @@ func getAuthenticatedRepository(ctx *context.Context, rc *requestContext, requir | |||
return nil | |||
} | |||
context.CheckRepoScopedToken(ctx, repository) | |||
if requireWrite { | |||
context.CheckRepoScopedToken(ctx, repository, auth_model.Write) | |||
} else { | |||
context.CheckRepoScopedToken(ctx, repository, auth_model.Read) | |||
} | |||
if ctx.Written() { | |||
return nil | |||
} |
@@ -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> |
@@ -21,7 +21,7 @@ import ( | |||
func TestAPIAdminOrgCreate(t *testing.T) { | |||
onGiteaRun(t, func(*testing.T, *url.URL) { | |||
session := loginUser(t, "user1") | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeSudo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteAdmin) | |||
org := api.CreateOrgOption{ | |||
UserName: "user2_org", | |||
@@ -55,7 +55,7 @@ func TestAPIAdminOrgCreate(t *testing.T) { | |||
func TestAPIAdminOrgCreateBadVisibility(t *testing.T) { | |||
onGiteaRun(t, func(*testing.T, *url.URL) { | |||
session := loginUser(t, "user1") | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeSudo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteAdmin) | |||
org := api.CreateOrgOption{ | |||
UserName: "user2_org", |
@@ -25,7 +25,7 @@ func TestAPIAdminCreateAndDeleteSSHKey(t *testing.T) { | |||
session := loginUser(t, "user1") | |||
keyOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user2"}) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeSudo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteAdmin) | |||
urlStr := fmt.Sprintf("/api/v1/admin/users/%s/keys?token=%s", keyOwner.Name, token) | |||
req := NewRequestWithValues(t, "POST", urlStr, map[string]string{ | |||
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC4cn+iXnA4KvcQYSV88vGn0Yi91vG47t1P7okprVmhNTkipNRIHWr6WdCO4VDr/cvsRkuVJAsLO2enwjGWWueOO6BodiBgyAOZ/5t5nJNMCNuLGT5UIo/RI1b0WRQwxEZTRjt6mFNw6lH14wRd8ulsr9toSWBPMOGWoYs1PDeDL0JuTjL+tr1SZi/EyxCngpYszKdXllJEHyI79KQgeD0Vt3pTrkbNVTOEcCNqZePSVmUH8X8Vhugz3bnE0/iE9Pb5fkWO9c4AnM1FgI/8Bvp27Fw2ShryIXuR6kKvUqhVMTuOSDHwu6A8jLE5Owt3GAYugDpDYuwTVNGrHLXKpPzrGGPE/jPmaLCMZcsdkec95dYeU3zKODEm8UQZFhmJmDeWVJ36nGrGZHL4J5aTTaeFUJmmXDaJYiJ+K2/ioKgXqnXvltu0A9R8/LGy4nrTJRr4JMLuJFoUXvGm1gXQ70w2LSpk6yl71RNC0hCtsBe8BP8IhYCM0EP5jh7eCMQZNvM= nocomment\n", | |||
@@ -52,7 +52,7 @@ func TestAPIAdminDeleteMissingSSHKey(t *testing.T) { | |||
defer tests.PrepareTestEnv(t)() | |||
// user1 is an admin user | |||
token := getUserToken(t, "user1", auth_model.AccessTokenScopeSudo) | |||
token := getUserToken(t, "user1", auth_model.AccessTokenScopeWriteAdmin) | |||
req := NewRequestf(t, "DELETE", "/api/v1/admin/users/user1/keys/%d?token=%s", unittest.NonexistentID, token) | |||
MakeRequest(t, req, http.StatusNotFound) | |||
} | |||
@@ -61,7 +61,7 @@ func TestAPIAdminDeleteUnauthorizedKey(t *testing.T) { | |||
defer tests.PrepareTestEnv(t)() | |||
adminUsername := "user1" | |||
normalUsername := "user2" | |||
token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeSudo) | |||
token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeWriteAdmin) | |||
urlStr := fmt.Sprintf("/api/v1/admin/users/%s/keys?token=%s", adminUsername, token) | |||
req := NewRequestWithValues(t, "POST", urlStr, map[string]string{ | |||
@@ -82,7 +82,7 @@ func TestAPISudoUser(t *testing.T) { | |||
defer tests.PrepareTestEnv(t)() | |||
adminUsername := "user1" | |||
normalUsername := "user2" | |||
token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeSudo) | |||
token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeReadUser) | |||
urlStr := fmt.Sprintf("/api/v1/user?sudo=%s&token=%s", normalUsername, token) | |||
req := NewRequest(t, "GET", urlStr) | |||
@@ -98,7 +98,7 @@ func TestAPISudoUserForbidden(t *testing.T) { | |||
adminUsername := "user1" | |||
normalUsername := "user2" | |||
token := getUserToken(t, normalUsername, auth_model.AccessTokenScopeSudo) | |||
token := getUserToken(t, normalUsername, auth_model.AccessTokenScopeReadAdmin) | |||
urlStr := fmt.Sprintf("/api/v1/user?sudo=%s&token=%s", adminUsername, token) | |||
req := NewRequest(t, "GET", urlStr) | |||
MakeRequest(t, req, http.StatusForbidden) | |||
@@ -107,7 +107,7 @@ func TestAPISudoUserForbidden(t *testing.T) { | |||
func TestAPIListUsers(t *testing.T) { | |||
defer tests.PrepareTestEnv(t)() | |||
adminUsername := "user1" | |||
token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeSudo) | |||
token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeReadAdmin) | |||
urlStr := fmt.Sprintf("/api/v1/admin/users?token=%s", token) | |||
req := NewRequest(t, "GET", urlStr) | |||
@@ -143,7 +143,7 @@ func TestAPIListUsersNonAdmin(t *testing.T) { | |||
func TestAPICreateUserInvalidEmail(t *testing.T) { | |||
defer tests.PrepareTestEnv(t)() | |||
adminUsername := "user1" | |||
token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeSudo) | |||
token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeWriteAdmin) | |||
urlStr := fmt.Sprintf("/api/v1/admin/users?token=%s", token) | |||
req := NewRequestWithValues(t, "POST", urlStr, map[string]string{ | |||
"email": "invalid_email@domain.com\r\n", | |||
@@ -161,7 +161,7 @@ func TestAPICreateUserInvalidEmail(t *testing.T) { | |||
func TestAPICreateAndDeleteUser(t *testing.T) { | |||
defer tests.PrepareTestEnv(t)() | |||
adminUsername := "user1" | |||
token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeSudo) | |||
token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeWriteAdmin) | |||
req := NewRequestWithValues( | |||
t, | |||
@@ -187,7 +187,7 @@ func TestAPICreateAndDeleteUser(t *testing.T) { | |||
func TestAPIEditUser(t *testing.T) { | |||
defer tests.PrepareTestEnv(t)() | |||
adminUsername := "user1" | |||
token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeSudo) | |||
token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeWriteAdmin) | |||
urlStr := fmt.Sprintf("/api/v1/admin/users/%s?token=%s", "user2", token) | |||
req := NewRequestWithValues(t, "PATCH", urlStr, map[string]string{ | |||
@@ -229,7 +229,7 @@ func TestAPIEditUser(t *testing.T) { | |||
func TestAPICreateRepoForUser(t *testing.T) { | |||
defer tests.PrepareTestEnv(t)() | |||
adminUsername := "user1" | |||
token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeSudo) | |||
token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeWriteAdmin) | |||
req := NewRequestWithJSON( | |||
t, | |||
@@ -245,7 +245,7 @@ func TestAPICreateRepoForUser(t *testing.T) { | |||
func TestAPIRenameUser(t *testing.T) { | |||
defer tests.PrepareTestEnv(t)() | |||
adminUsername := "user1" | |||
token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeSudo) | |||
token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeWriteAdmin) | |||
urlStr := fmt.Sprintf("/api/v1/admin/users/%s/rename?token=%s", "user2", token) | |||
req := NewRequestWithValues(t, "POST", urlStr, map[string]string{ | |||
// required |
@@ -16,7 +16,7 @@ import ( | |||
) | |||
func testAPIGetBranch(t *testing.T, branchName string, exists bool) { | |||
token := getUserToken(t, "user2", auth_model.AccessTokenScopeRepo) | |||
token := getUserToken(t, "user2", auth_model.AccessTokenScopeReadRepository) | |||
req := NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/branches/%s?token=%s", branchName, token) | |||
resp := MakeRequest(t, req, NoExpectedStatus) | |||
if !exists { | |||
@@ -32,7 +32,7 @@ func testAPIGetBranch(t *testing.T, branchName string, exists bool) { | |||
} | |||
func testAPIGetBranchProtection(t *testing.T, branchName string, expectedHTTPStatus int) { | |||
token := getUserToken(t, "user2", auth_model.AccessTokenScopeRepo) | |||
token := getUserToken(t, "user2", auth_model.AccessTokenScopeReadRepository) | |||
req := NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/branch_protections/%s?token=%s", branchName, token) | |||
resp := MakeRequest(t, req, expectedHTTPStatus) | |||
@@ -44,7 +44,7 @@ func testAPIGetBranchProtection(t *testing.T, branchName string, expectedHTTPSta | |||
} | |||
func testAPICreateBranchProtection(t *testing.T, branchName string, expectedHTTPStatus int) { | |||
token := getUserToken(t, "user2", auth_model.AccessTokenScopeRepo) | |||
token := getUserToken(t, "user2", auth_model.AccessTokenScopeWriteRepository) | |||
req := NewRequestWithJSON(t, "POST", "/api/v1/repos/user2/repo1/branch_protections?token="+token, &api.BranchProtection{ | |||
RuleName: branchName, | |||
}) | |||
@@ -58,7 +58,7 @@ func testAPICreateBranchProtection(t *testing.T, branchName string, expectedHTTP | |||
} | |||
func testAPIEditBranchProtection(t *testing.T, branchName string, body *api.BranchProtection, expectedHTTPStatus int) { | |||
token := getUserToken(t, "user2", auth_model.AccessTokenScopeRepo) | |||
token := getUserToken(t, "user2", auth_model.AccessTokenScopeWriteRepository) | |||
req := NewRequestWithJSON(t, "PATCH", "/api/v1/repos/user2/repo1/branch_protections/"+branchName+"?token="+token, body) | |||
resp := MakeRequest(t, req, expectedHTTPStatus) | |||
@@ -70,13 +70,13 @@ func testAPIEditBranchProtection(t *testing.T, branchName string, body *api.Bran | |||
} | |||
func testAPIDeleteBranchProtection(t *testing.T, branchName string, expectedHTTPStatus int) { | |||
token := getUserToken(t, "user2", auth_model.AccessTokenScopeRepo) | |||
token := getUserToken(t, "user2", auth_model.AccessTokenScopeWriteRepository) | |||
req := NewRequestf(t, "DELETE", "/api/v1/repos/user2/repo1/branch_protections/%s?token=%s", branchName, token) | |||
MakeRequest(t, req, expectedHTTPStatus) | |||
} | |||
func testAPIDeleteBranch(t *testing.T, branchName string, expectedHTTPStatus int) { | |||
token := getUserToken(t, "user2", auth_model.AccessTokenScopeRepo) | |||
token := getUserToken(t, "user2", auth_model.AccessTokenScopeWriteRepository) | |||
req := NewRequestf(t, "DELETE", "/api/v1/repos/user2/repo1/branches/%s?token=%s", branchName, token) | |||
MakeRequest(t, req, expectedHTTPStatus) | |||
} | |||
@@ -102,7 +102,7 @@ func TestAPICreateBranch(t *testing.T) { | |||
func testAPICreateBranches(t *testing.T, giteaURL *url.URL) { | |||
username := "user2" | |||
ctx := NewAPITestContext(t, username, "my-noo-repo", auth_model.AccessTokenScopeRepo) | |||
ctx := NewAPITestContext(t, username, "my-noo-repo", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) | |||
giteaURL.Path = ctx.GitPath() | |||
t.Run("CreateRepo", doAPICreateRepository(ctx, false)) | |||
@@ -149,7 +149,7 @@ func testAPICreateBranches(t *testing.T, giteaURL *url.URL) { | |||
} | |||
func testAPICreateBranch(t testing.TB, session *TestSession, user, repo, oldBranch, newBranch string, status int) bool { | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
req := NewRequestWithJSON(t, "POST", "/api/v1/repos/"+user+"/"+repo+"/branches?token="+token, &api.CreateBranchRepoOption{ | |||
BranchName: newBranch, | |||
OldBranchName: oldBranch, |
@@ -36,8 +36,8 @@ func TestAPIGetCommentAttachment(t *testing.T) { | |||
repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | |||
session := loginUser(t, repoOwner.Name) | |||
token := getTokenForLoggedInUser(t, session) | |||
req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/issues/comments/%d/assets/%d", repoOwner.Name, repo.Name, comment.ID, attachment.ID) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadIssue) | |||
req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/issues/comments/%d/assets/%d?token=%s", repoOwner.Name, repo.Name, comment.ID, attachment.ID, token) | |||
session.MakeRequest(t, req, http.StatusOK) | |||
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/issues/comments/%d/assets/%d?token=%s", repoOwner.Name, repo.Name, comment.ID, attachment.ID, token) | |||
resp := session.MakeRequest(t, req, http.StatusOK) | |||
@@ -61,8 +61,9 @@ func TestAPIListCommentAttachments(t *testing.T) { | |||
repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | |||
session := loginUser(t, repoOwner.Name) | |||
req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/issues/comments/%d/assets", | |||
repoOwner.Name, repo.Name, comment.ID) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadIssue) | |||
req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/issues/comments/%d/assets?token=%s", | |||
repoOwner.Name, repo.Name, comment.ID, token) | |||
resp := session.MakeRequest(t, req, http.StatusOK) | |||
var apiAttachments []*api.Attachment | |||
@@ -82,7 +83,7 @@ func TestAPICreateCommentAttachment(t *testing.T) { | |||
repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | |||
session := loginUser(t, repoOwner.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue) | |||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/comments/%d/assets?token=%s", | |||
repoOwner.Name, repo.Name, comment.ID, token) | |||
@@ -121,7 +122,7 @@ func TestAPIEditCommentAttachment(t *testing.T) { | |||
repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | |||
session := loginUser(t, repoOwner.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue) | |||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/comments/%d/assets/%d?token=%s", | |||
repoOwner.Name, repo.Name, comment.ID, attachment.ID, token) | |||
req := NewRequestWithValues(t, "PATCH", urlStr, map[string]string{ | |||
@@ -144,7 +145,7 @@ func TestAPIDeleteCommentAttachment(t *testing.T) { | |||
repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | |||
session := loginUser(t, repoOwner.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue) | |||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/comments/%d/assets/%d?token=%s", | |||
repoOwner.Name, repo.Name, comment.ID, attachment.ID, token) | |||
@@ -76,7 +76,7 @@ func TestAPIListIssueComments(t *testing.T) { | |||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}) | |||
repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | |||
token := getUserToken(t, repoOwner.Name, auth_model.AccessTokenScopeRepo) | |||
token := getUserToken(t, repoOwner.Name, auth_model.AccessTokenScopeReadIssue) | |||
req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/issues/%d/comments?token=%s", | |||
repoOwner.Name, repo.Name, issue.Index, token) | |||
resp := MakeRequest(t, req, http.StatusOK) | |||
@@ -96,7 +96,7 @@ func TestAPICreateComment(t *testing.T) { | |||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}) | |||
repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | |||
token := getUserToken(t, repoOwner.Name, auth_model.AccessTokenScopeRepo) | |||
token := getUserToken(t, repoOwner.Name, auth_model.AccessTokenScopeWriteIssue) | |||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/comments?token=%s", | |||
repoOwner.Name, repo.Name, issue.Index, token) | |||
req := NewRequestWithValues(t, "POST", urlStr, map[string]string{ | |||
@@ -118,7 +118,7 @@ func TestAPIGetComment(t *testing.T) { | |||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: comment.Issue.RepoID}) | |||
repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | |||
token := getUserToken(t, repoOwner.Name, auth_model.AccessTokenScopeRepo) | |||
token := getUserToken(t, repoOwner.Name, auth_model.AccessTokenScopeReadIssue) | |||
req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/issues/comments/%d", repoOwner.Name, repo.Name, comment.ID) | |||
MakeRequest(t, req, http.StatusOK) | |||
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/issues/comments/%d?token=%s", repoOwner.Name, repo.Name, comment.ID, token) | |||
@@ -146,7 +146,7 @@ func TestAPIEditComment(t *testing.T) { | |||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}) | |||
repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | |||
token := getUserToken(t, repoOwner.Name, auth_model.AccessTokenScopeRepo) | |||
token := getUserToken(t, repoOwner.Name, auth_model.AccessTokenScopeWriteIssue) | |||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/comments/%d?token=%s", | |||
repoOwner.Name, repo.Name, comment.ID, token) | |||
req := NewRequestWithValues(t, "PATCH", urlStr, map[string]string{ | |||
@@ -170,7 +170,7 @@ func TestAPIDeleteComment(t *testing.T) { | |||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}) | |||
repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | |||
token := getUserToken(t, repoOwner.Name, auth_model.AccessTokenScopeRepo) | |||
token := getUserToken(t, repoOwner.Name, auth_model.AccessTokenScopeWriteIssue) | |||
req := NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/issues/comments/%d?token=%s", | |||
repoOwner.Name, repo.Name, comment.ID, token) | |||
MakeRequest(t, req, http.StatusNoContent) |
@@ -21,8 +21,8 @@ type makeRequestFunc func(testing.TB, *http.Request, int) *httptest.ResponseReco | |||
func TestGPGKeys(t *testing.T) { | |||
defer tests.PrepareTestEnv(t)() | |||
session := loginUser(t, "user2") | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
tokenWithGPGKeyScope := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAdminGPGKey, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
tokenWithGPGKeyScope := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) | |||
tt := []struct { | |||
name string | |||
@@ -36,7 +36,7 @@ func TestGPGKeys(t *testing.T) { | |||
}, | |||
{ | |||
name: "LoggedAsUser2", makeRequest: session.MakeRequest, token: token, | |||
results: []int{http.StatusForbidden, http.StatusOK, http.StatusForbidden, http.StatusForbidden, http.StatusForbidden, http.StatusForbidden, http.StatusForbidden, http.StatusForbidden, http.StatusForbidden}, | |||
results: []int{http.StatusForbidden, http.StatusForbidden, http.StatusForbidden, http.StatusForbidden, http.StatusForbidden, http.StatusForbidden, http.StatusForbidden, http.StatusForbidden, http.StatusForbidden}, | |||
}, | |||
{ | |||
name: "LoggedAsUser2WithScope", makeRequest: session.MakeRequest, token: tokenWithGPGKeyScope, |
@@ -53,7 +53,7 @@ func TestHTTPSigPubKey(t *testing.T) { | |||
// Add our public key to user1 | |||
defer tests.PrepareTestEnv(t)() | |||
session := loginUser(t, "user1") | |||
token := url.QueryEscape(getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAdminPublicKey, auth_model.AccessTokenScopeSudo)) | |||
token := url.QueryEscape(getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteUser)) | |||
keysURL := fmt.Sprintf("/api/v1/user/keys?token=%s", token) | |||
keyType := "ssh-rsa" | |||
keyContent := "AAAAB3NzaC1yc2EAAAADAQABAAABAQCqOZB5vkRvXFXups1/0StDRdG8plbNSwsWEnNnP4Bvurxa0+z3W9B8GLKnDiLw5MbpbMNyBlpXw13GfuIeciy10DWTz0xUbiy3J3KabCaT36asIw2y7k6Z0jL0UBnrVENwq5/lUbZYqSZ4rRU744wkhh8TULpzM14npQCZwg6aEbG+MwjzddQ72fR+3BPBrKn5dTmmu8rH99O+U+Nuto81Tg7PA+NUupcHOmhdiEGq49plgVFXK98Vks5tiybL4GuzFyWgyX73Dg/QBMn2eMHt1EMv5Gs3i6GFhKKGo4rjDi9qI6PX5oDR4LTNe6cR8td8YhVD8WFZwLLl/vaYyIqd" | |||
@@ -69,7 +69,8 @@ func TestHTTPSigPubKey(t *testing.T) { | |||
keyID := ssh.FingerprintSHA256(sshSigner.PublicKey()) | |||
// create the request | |||
req = NewRequest(t, "GET", "/api/v1/admin/users") | |||
token = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadAdmin) | |||
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/admin/users?token=%s", token)) | |||
signer, _, err := httpsig.NewSSHSigner(sshSigner, httpsig.DigestSha512, []string{httpsig.RequestTarget, "(created)", "(expires)"}, httpsig.Signature, 10) | |||
if err != nil { |
@@ -32,7 +32,7 @@ func TestAPIGetIssueAttachment(t *testing.T) { | |||
repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | |||
session := loginUser(t, repoOwner.Name) | |||
token := getTokenForLoggedInUser(t, session) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadIssue) | |||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/assets/%d?token=%s", | |||
repoOwner.Name, repo.Name, issue.Index, attachment.ID, token) | |||
@@ -53,7 +53,7 @@ func TestAPIListIssueAttachments(t *testing.T) { | |||
repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | |||
session := loginUser(t, repoOwner.Name) | |||
token := getTokenForLoggedInUser(t, session) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadIssue) | |||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/assets?token=%s", | |||
repoOwner.Name, repo.Name, issue.Index, token) | |||
@@ -73,7 +73,7 @@ func TestAPICreateIssueAttachment(t *testing.T) { | |||
repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | |||
session := loginUser(t, repoOwner.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue) | |||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/assets?token=%s", | |||
repoOwner.Name, repo.Name, issue.Index, token) | |||
@@ -111,7 +111,7 @@ func TestAPIEditIssueAttachment(t *testing.T) { | |||
repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | |||
session := loginUser(t, repoOwner.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue) | |||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/assets/%d?token=%s", | |||
repoOwner.Name, repo.Name, issue.Index, attachment.ID, token) | |||
req := NewRequestWithValues(t, "PATCH", urlStr, map[string]string{ | |||
@@ -133,7 +133,7 @@ func TestAPIDeleteIssueAttachment(t *testing.T) { | |||
repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | |||
session := loginUser(t, repoOwner.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue) | |||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/assets/%d?token=%s", | |||
repoOwner.Name, repo.Name, issue.Index, attachment.ID, token) | |||
@@ -25,7 +25,7 @@ func TestAPIModifyLabels(t *testing.T) { | |||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) | |||
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | |||
session := loginUser(t, owner.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue) | |||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/labels?token=%s", owner.Name, repo.Name, token) | |||
// CreateLabel | |||
@@ -97,7 +97,7 @@ func TestAPIAddIssueLabels(t *testing.T) { | |||
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | |||
session := loginUser(t, owner.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue) | |||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/labels?token=%s", | |||
repo.OwnerName, repo.Name, issue.Index, token) | |||
req := NewRequestWithJSON(t, "POST", urlStr, &api.IssueLabelsOption{ | |||
@@ -120,7 +120,7 @@ func TestAPIReplaceIssueLabels(t *testing.T) { | |||
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | |||
session := loginUser(t, owner.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue) | |||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/labels?token=%s", | |||
owner.Name, repo.Name, issue.Index, token) | |||
req := NewRequestWithJSON(t, "PUT", urlStr, &api.IssueLabelsOption{ | |||
@@ -144,7 +144,7 @@ func TestAPIModifyOrgLabels(t *testing.T) { | |||
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | |||
user := "user1" | |||
session := loginUser(t, user) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo, auth_model.AccessTokenScopeAdminOrg) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteOrganization) | |||
urlStr := fmt.Sprintf("/api/v1/orgs/%s/labels?token=%s", owner.Name, token) | |||
// CreateLabel |
@@ -29,7 +29,7 @@ func TestAPIIssuesMilestone(t *testing.T) { | |||
assert.Equal(t, structs.StateOpen, milestone.State()) | |||
session := loginUser(t, owner.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue) | |||
// update values of issue | |||
milestoneState := "closed" |
@@ -29,7 +29,7 @@ func TestAPIPinIssue(t *testing.T) { | |||
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | |||
session := loginUser(t, owner.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue) | |||
// Pin the Issue | |||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/pin?token=%s", | |||
@@ -56,7 +56,7 @@ func TestAPIUnpinIssue(t *testing.T) { | |||
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | |||
session := loginUser(t, owner.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue) | |||
// Pin the Issue | |||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/pin?token=%s", | |||
@@ -97,7 +97,7 @@ func TestAPIMoveIssuePin(t *testing.T) { | |||
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | |||
session := loginUser(t, owner.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue) | |||
// Pin the first Issue | |||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/pin?token=%s", | |||
@@ -152,7 +152,7 @@ func TestAPIListPinnedIssues(t *testing.T) { | |||
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | |||
session := loginUser(t, owner.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue) | |||
// Pin the Issue | |||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/pin?token=%s", |
@@ -29,7 +29,7 @@ func TestAPIIssuesReactions(t *testing.T) { | |||
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue.Repo.OwnerID}) | |||
session := loginUser(t, owner.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue) | |||
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | |||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/reactions?token=%s", | |||
@@ -88,7 +88,7 @@ func TestAPICommentReactions(t *testing.T) { | |||
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue.Repo.OwnerID}) | |||
session := loginUser(t, owner.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue) | |||
user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) | |||
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) |
@@ -26,7 +26,7 @@ func TestAPIListStopWatches(t *testing.T) { | |||
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | |||
session := loginUser(t, owner.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository, auth_model.AccessTokenScopeReadUser) | |||
req := NewRequestf(t, "GET", "/api/v1/user/stopwatches?token=%s", token) | |||
resp := MakeRequest(t, req, http.StatusOK) | |||
var apiWatches []*api.StopWatch | |||
@@ -52,7 +52,7 @@ func TestAPIStopStopWatches(t *testing.T) { | |||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | |||
session := loginUser(t, user.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue) | |||
req := NewRequestf(t, "POST", "/api/v1/repos/%s/%s/issues/%d/stopwatch/stop?token=%s", owner.Name, issue.Repo.Name, issue.Index, token) | |||
MakeRequest(t, req, http.StatusCreated) | |||
@@ -68,7 +68,7 @@ func TestAPICancelStopWatches(t *testing.T) { | |||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) | |||
session := loginUser(t, user.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue) | |||
req := NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/issues/%d/stopwatch/delete?token=%s", owner.Name, issue.Repo.Name, issue.Index, token) | |||
MakeRequest(t, req, http.StatusNoContent) | |||
@@ -84,7 +84,7 @@ func TestAPIStartStopWatches(t *testing.T) { | |||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | |||
session := loginUser(t, user.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue) | |||
req := NewRequestf(t, "POST", "/api/v1/repos/%s/%s/issues/%d/stopwatch/start?token=%s", owner.Name, issue.Repo.Name, issue.Index, token) | |||
MakeRequest(t, req, http.StatusCreated) |
@@ -31,7 +31,7 @@ func TestAPIIssueSubscriptions(t *testing.T) { | |||
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue1.PosterID}) | |||
session := loginUser(t, owner.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue) | |||
testSubscription := func(issue *issues_model.Issue, isWatching bool) { | |||
issueRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}) |
@@ -30,7 +30,7 @@ func TestAPIListIssues(t *testing.T) { | |||
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | |||
session := loginUser(t, owner.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadIssue) | |||
link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/issues", owner.Name, repo.Name)) | |||
link.RawQuery = url.Values{"token": {token}, "state": {"all"}}.Encode() | |||
@@ -81,7 +81,7 @@ func TestAPICreateIssue(t *testing.T) { | |||
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repoBefore.OwnerID}) | |||
session := loginUser(t, owner.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue) | |||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues?state=all&token=%s", owner.Name, repoBefore.Name, token) | |||
req := NewRequestWithJSON(t, "POST", urlStr, &api.CreateIssueOption{ | |||
Body: body, | |||
@@ -117,7 +117,7 @@ func TestAPIEditIssue(t *testing.T) { | |||
assert.Equal(t, api.StateOpen, issueBefore.State()) | |||
session := loginUser(t, owner.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue) | |||
// update values of issue | |||
issueState := "closed" | |||
@@ -171,7 +171,7 @@ func TestAPIEditIssue(t *testing.T) { | |||
func TestAPISearchIssues(t *testing.T) { | |||
defer tests.PrepareTestEnv(t)() | |||
token := getUserToken(t, "user2") | |||
token := getUserToken(t, "user2", auth_model.AccessTokenScopeReadIssue) | |||
// as this API was used in the frontend, it uses UI page size | |||
expectedIssueCount := 16 // from the fixtures | |||
@@ -180,7 +180,7 @@ func TestAPISearchIssues(t *testing.T) { | |||
} | |||
link, _ := url.Parse("/api/v1/repos/issues/search") | |||
query := url.Values{"token": {getUserToken(t, "user1")}} | |||
query := url.Values{"token": {getUserToken(t, "user1", auth_model.AccessTokenScopeReadIssue)}} | |||
var apiIssues []*api.Issue | |||
link.RawQuery = query.Encode() | |||
@@ -278,7 +278,7 @@ func TestAPISearchIssuesWithLabels(t *testing.T) { | |||
} | |||
link, _ := url.Parse("/api/v1/repos/issues/search") | |||
query := url.Values{"token": {getUserToken(t, "user1")}} | |||
query := url.Values{"token": {getUserToken(t, "user1", auth_model.AccessTokenScopeReadIssue)}} | |||
var apiIssues []*api.Issue | |||
link.RawQuery = query.Encode() |
@@ -28,7 +28,7 @@ func TestAPIGetTrackedTimes(t *testing.T) { | |||
assert.NoError(t, issue2.LoadRepo(db.DefaultContext)) | |||
session := loginUser(t, user2.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadIssue) | |||
req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/issues/%d/times?token=%s", user2.Name, issue2.Repo.Name, issue2.Index, token) | |||
resp := MakeRequest(t, req, http.StatusOK) | |||
@@ -71,7 +71,7 @@ func TestAPIDeleteTrackedTime(t *testing.T) { | |||
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | |||
session := loginUser(t, user2.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue) | |||
// Deletion not allowed | |||
req := NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/issues/%d/times/%d?token=%s", user2.Name, issue2.Repo.Name, issue2.Index, time6.ID, token) | |||
@@ -106,7 +106,7 @@ func TestAPIAddTrackedTimes(t *testing.T) { | |||
admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) | |||
session := loginUser(t, admin.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue) | |||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/times?token=%s", user2.Name, issue2.Repo.Name, issue2.Index, token) | |||
@@ -54,7 +54,7 @@ func TestCreateReadOnlyDeployKey(t *testing.T) { | |||
repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | |||
session := loginUser(t, repoOwner.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
keysURL := fmt.Sprintf("/api/v1/repos/%s/%s/keys?token=%s", repoOwner.Name, repo.Name, token) | |||
rawKeyBody := api.CreateKeyOption{ | |||
Title: "read-only", | |||
@@ -80,7 +80,7 @@ func TestCreateReadWriteDeployKey(t *testing.T) { | |||
repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | |||
session := loginUser(t, repoOwner.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
keysURL := fmt.Sprintf("/api/v1/repos/%s/%s/keys?token=%s", repoOwner.Name, repo.Name, token) | |||
rawKeyBody := api.CreateKeyOption{ | |||
Title: "read-write", | |||
@@ -104,7 +104,7 @@ func TestCreateUserKey(t *testing.T) { | |||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user1"}) | |||
session := loginUser(t, "user1") | |||
token := url.QueryEscape(getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAdminPublicKey)) | |||
token := url.QueryEscape(getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteUser)) | |||
keysURL := fmt.Sprintf("/api/v1/user/keys?token=%s", token) | |||
keyType := "ssh-rsa" | |||
keyContent := "AAAAB3NzaC1yc2EAAAADAQABAAABgQC4cn+iXnA4KvcQYSV88vGn0Yi91vG47t1P7okprVmhNTkipNRIHWr6WdCO4VDr/cvsRkuVJAsLO2enwjGWWueOO6BodiBgyAOZ/5t5nJNMCNuLGT5UIo/RI1b0WRQwxEZTRjt6mFNw6lH14wRd8ulsr9toSWBPMOGWoYs1PDeDL0JuTjL+tr1SZi/EyxCngpYszKdXllJEHyI79KQgeD0Vt3pTrkbNVTOEcCNqZePSVmUH8X8Vhugz3bnE0/iE9Pb5fkWO9c4AnM1FgI/8Bvp27Fw2ShryIXuR6kKvUqhVMTuOSDHwu6A8jLE5Owt3GAYugDpDYuwTVNGrHLXKpPzrGGPE/jPmaLCMZcsdkec95dYeU3zKODEm8UQZFhmJmDeWVJ36nGrGZHL4J5aTTaeFUJmmXDaJYiJ+K2/ioKgXqnXvltu0A9R8/LGy4nrTJRr4JMLuJFoUXvGm1gXQ70w2LSpk6yl71RNC0hCtsBe8BP8IhYCM0EP5jh7eCMQZNvM=" | |||
@@ -168,7 +168,7 @@ func TestCreateUserKey(t *testing.T) { | |||
// Now login as user 2 | |||
session2 := loginUser(t, "user2") | |||
token2 := url.QueryEscape(getTokenForLoggedInUser(t, session2, auth_model.AccessTokenScopeAdminPublicKey)) | |||
token2 := url.QueryEscape(getTokenForLoggedInUser(t, session2, auth_model.AccessTokenScopeWriteUser)) | |||
// Should find key even though not ours, but we shouldn't know whose it is | |||
fingerprintURL = fmt.Sprintf("/api/v1/user/keys?token=%s&fingerprint=%s", token2, newPublicKey.Fingerprint) |
@@ -28,7 +28,7 @@ func TestAPINotification(t *testing.T) { | |||
thread5 := unittest.AssertExistsAndLoadBean(t, &activities_model.Notification{ID: 5}) | |||
assert.NoError(t, thread5.LoadAttributes(db.DefaultContext)) | |||
session := loginUser(t, user2.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeNotification) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteNotification, auth_model.AccessTokenScopeWriteRepository) | |||
// -- GET /notifications -- | |||
// test filter | |||
@@ -146,7 +146,7 @@ func TestAPINotificationPUT(t *testing.T) { | |||
thread5 := unittest.AssertExistsAndLoadBean(t, &activities_model.Notification{ID: 5}) | |||
assert.NoError(t, thread5.LoadAttributes(db.DefaultContext)) | |||
session := loginUser(t, user2.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeNotification) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteNotification) | |||
// Check notifications are as expected | |||
req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications?all=true&token=%s", token)) |
@@ -55,7 +55,7 @@ func testAPICreateOAuth2Application(t *testing.T) { | |||
func testAPIListOAuth2Applications(t *testing.T) { | |||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | |||
session := loginUser(t, user.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadApplication) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadUser) | |||
existApp := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ | |||
UID: user.ID, | |||
@@ -86,7 +86,7 @@ func testAPIListOAuth2Applications(t *testing.T) { | |||
func testAPIDeleteOAuth2Application(t *testing.T) { | |||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | |||
session := loginUser(t, user.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteApplication) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteUser) | |||
oldApp := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ | |||
UID: user.ID, | |||
@@ -107,7 +107,7 @@ func testAPIDeleteOAuth2Application(t *testing.T) { | |||
func testAPIGetOAuth2Application(t *testing.T) { | |||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | |||
session := loginUser(t, user.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadApplication) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadUser) | |||
existApp := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ | |||
UID: user.ID, |
@@ -26,7 +26,7 @@ import ( | |||
func TestAPIOrgCreate(t *testing.T) { | |||
onGiteaRun(t, func(*testing.T, *url.URL) { | |||
token := getUserToken(t, "user1", auth_model.AccessTokenScopeWriteOrg) | |||
token := getUserToken(t, "user1", auth_model.AccessTokenScopeWriteOrganization) | |||
org := api.CreateOrgOption{ | |||
UserName: "user1_org", | |||
@@ -100,7 +100,7 @@ func TestAPIOrgEdit(t *testing.T) { | |||
onGiteaRun(t, func(*testing.T, *url.URL) { | |||
session := loginUser(t, "user1") | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteOrg) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteOrganization) | |||
org := api.EditOrgOption{ | |||
FullName: "User3 organization new full name", | |||
Description: "A new description", | |||
@@ -127,7 +127,7 @@ func TestAPIOrgEditBadVisibility(t *testing.T) { | |||
onGiteaRun(t, func(*testing.T, *url.URL) { | |||
session := loginUser(t, "user1") | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteOrg) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteOrganization) | |||
org := api.EditOrgOption{ | |||
FullName: "User3 organization new full name", | |||
Description: "A new description", | |||
@@ -162,7 +162,7 @@ func TestAPIOrgDeny(t *testing.T) { | |||
func TestAPIGetAll(t *testing.T) { | |||
defer tests.PrepareTestEnv(t)() | |||
token := getUserToken(t, "user1", auth_model.AccessTokenScopeReadOrg) | |||
token := getUserToken(t, "user1", auth_model.AccessTokenScopeReadOrganization) | |||
// accessing with a token will return all orgs | |||
req := NewRequestf(t, "GET", "/api/v1/orgs?token=%s", token) | |||
@@ -186,7 +186,7 @@ func TestAPIGetAll(t *testing.T) { | |||
func TestAPIOrgSearchEmptyTeam(t *testing.T) { | |||
onGiteaRun(t, func(*testing.T, *url.URL) { | |||
token := getUserToken(t, "user1", auth_model.AccessTokenScopeAdminOrg) | |||
token := getUserToken(t, "user1", auth_model.AccessTokenScopeWriteOrganization) | |||
orgName := "org_with_empty_team" | |||
// create org |
@@ -28,7 +28,7 @@ func TestPackageNpm(t *testing.T) { | |||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | |||
token := fmt.Sprintf("Bearer %s", getTokenForLoggedInUser(t, loginUser(t, user.Name), auth_model.AccessTokenScopePackage)) | |||
token := fmt.Sprintf("Bearer %s", getTokenForLoggedInUser(t, loginUser(t, user.Name), auth_model.AccessTokenScopeWritePackage)) | |||
packageName := "@scope/test-package" | |||
packageVersion := "1.0.1-pre" |
@@ -75,7 +75,7 @@ func TestPackageNuGet(t *testing.T) { | |||
} | |||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | |||
token := getUserToken(t, user.Name, auth_model.AccessTokenScopePackage) | |||
token := getUserToken(t, user.Name, auth_model.AccessTokenScopeWritePackage) | |||
packageName := "test.package" | |||
packageVersion := "1.0.3" |
@@ -31,7 +31,7 @@ func TestPackagePub(t *testing.T) { | |||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | |||
token := "Bearer " + getUserToken(t, user.Name, auth_model.AccessTokenScopePackage) | |||
token := "Bearer " + getUserToken(t, user.Name, auth_model.AccessTokenScopeWritePackage) | |||
packageName := "test_package" | |||
packageVersion := "1.0.1" |
@@ -34,7 +34,7 @@ func TestPackageAPI(t *testing.T) { | |||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) | |||
session := loginUser(t, user.Name) | |||
tokenReadPackage := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadPackage) | |||
tokenDeletePackage := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeDeletePackage) | |||
tokenDeletePackage := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWritePackage) | |||
packageName := "test-package" | |||
packageVersion := "1.0.3" |
@@ -28,7 +28,7 @@ func TestPackageVagrant(t *testing.T) { | |||
defer tests.PrepareTestEnv(t)() | |||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | |||
token := "Bearer " + getUserToken(t, user.Name, auth_model.AccessTokenScopePackage) | |||
token := "Bearer " + getUserToken(t, user.Name, auth_model.AccessTokenScopeWritePackage) | |||
packageName := "test_package" | |||
packageVersion := "1.0.1" |
@@ -28,7 +28,7 @@ func TestAPIPullReview(t *testing.T) { | |||
// test ListPullReviews | |||
session := loginUser(t, "user2") | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
req := NewRequestf(t, http.MethodGet, "/api/v1/repos/%s/%s/pulls/%d/reviews?token=%s", repo.OwnerName, repo.Name, pullIssue.Index, token) | |||
resp := MakeRequest(t, req, http.StatusOK) | |||
@@ -231,7 +231,7 @@ func TestAPIPullReviewRequest(t *testing.T) { | |||
// Test add Review Request | |||
session := loginUser(t, "user2") | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers?token=%s", repo.OwnerName, repo.Name, pullIssue.Index, token), &api.PullReviewRequestOptions{ | |||
Reviewers: []string{"user4@example.com", "user8"}, | |||
}) | |||
@@ -251,7 +251,7 @@ func TestAPIPullReviewRequest(t *testing.T) { | |||
// Test Remove Review Request | |||
session2 := loginUser(t, "user4") | |||
token2 := getTokenForLoggedInUser(t, session2, auth_model.AccessTokenScopeRepo) | |||
token2 := getTokenForLoggedInUser(t, session2, auth_model.AccessTokenScopeWriteRepository) | |||
req = NewRequestWithJSON(t, http.MethodDelete, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers?token=%s", repo.OwnerName, repo.Name, pullIssue.Index, token2), &api.PullReviewRequestOptions{ | |||
Reviewers: []string{"user4"}, |
@@ -29,7 +29,7 @@ func TestAPIViewPulls(t *testing.T) { | |||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) | |||
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | |||
ctx := NewAPITestContext(t, "user2", repo.Name, auth_model.AccessTokenScopeRepo) | |||
ctx := NewAPITestContext(t, "user2", repo.Name, auth_model.AccessTokenScopeReadRepository) | |||
req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/pulls?state=all&token="+ctx.Token, owner.Name, repo.Name) | |||
resp := ctx.Session.MakeRequest(t, req, http.StatusOK) | |||
@@ -75,7 +75,7 @@ func TestAPIMergePullWIP(t *testing.T) { | |||
assert.Contains(t, pr.Issue.Title, setting.Repository.PullRequest.WorkInProgressPrefixes[0]) | |||
session := loginUser(t, owner.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/merge?token=%s", owner.Name, repo.Name, pr.Index, token), &forms.MergePullRequestForm{ | |||
MergeMessageField: pr.Issue.Title, | |||
Do: string(repo_model.MergeStyleMerge), | |||
@@ -94,7 +94,7 @@ func TestAPICreatePullSuccess(t *testing.T) { | |||
owner11 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo11.OwnerID}) | |||
session := loginUser(t, owner11.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls?token=%s", owner10.Name, repo10.Name, token), &api.CreatePullRequestOption{ | |||
Head: fmt.Sprintf("%s:master", owner11.Name), | |||
Base: "master", | |||
@@ -114,7 +114,7 @@ func TestAPICreatePullWithFieldsSuccess(t *testing.T) { | |||
owner11 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo11.OwnerID}) | |||
session := loginUser(t, owner11.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
opts := &api.CreatePullRequestOption{ | |||
Head: fmt.Sprintf("%s:master", owner11.Name), | |||
@@ -151,7 +151,7 @@ func TestAPICreatePullWithFieldsFailure(t *testing.T) { | |||
owner11 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo11.OwnerID}) | |||
session := loginUser(t, owner11.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
opts := &api.CreatePullRequestOption{ | |||
Head: fmt.Sprintf("%s:master", owner11.Name), | |||
@@ -181,7 +181,7 @@ func TestAPIEditPull(t *testing.T) { | |||
owner10 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo10.OwnerID}) | |||
session := loginUser(t, owner10.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls?token=%s", owner10.Name, repo10.Name, token), &api.CreatePullRequestOption{ | |||
Head: "develop", | |||
Base: "master", |
@@ -25,7 +25,7 @@ func TestAPIListReleases(t *testing.T) { | |||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) | |||
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | |||
token := getUserToken(t, user2.LowerName, auth_model.AccessTokenScopeRepo) | |||
token := getUserToken(t, user2.LowerName, auth_model.AccessTokenScopeReadRepository) | |||
link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/releases", user2.Name, repo.Name)) | |||
link.RawQuery = url.Values{"token": {token}}.Encode() | |||
@@ -101,7 +101,7 @@ func TestAPICreateAndUpdateRelease(t *testing.T) { | |||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) | |||
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | |||
session := loginUser(t, owner.LowerName) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
gitRepo, err := git.OpenRepository(git.DefaultContext, repo.RepoPath()) | |||
assert.NoError(t, err) | |||
@@ -153,7 +153,7 @@ func TestAPICreateReleaseToDefaultBranch(t *testing.T) { | |||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) | |||
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | |||
session := loginUser(t, owner.LowerName) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
createNewReleaseUsingAPI(t, session, token, owner, repo, "v0.0.1", "", "v0.0.1", "test") | |||
} | |||
@@ -164,7 +164,7 @@ func TestAPICreateReleaseToDefaultBranchOnExistingTag(t *testing.T) { | |||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) | |||
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | |||
session := loginUser(t, owner.LowerName) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
gitRepo, err := git.OpenRepository(git.DefaultContext, repo.RepoPath()) | |||
assert.NoError(t, err) | |||
@@ -232,7 +232,7 @@ func TestAPIDeleteReleaseByTagName(t *testing.T) { | |||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) | |||
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | |||
session := loginUser(t, owner.LowerName) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
createNewReleaseUsingAPI(t, session, token, owner, repo, "release-tag", "", "Release Tag", "test") | |||
@@ -25,7 +25,7 @@ func TestAPIDownloadArchive(t *testing.T) { | |||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) | |||
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | |||
session := loginUser(t, user2.LowerName) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) | |||
link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/archive/master.zip", user2.Name, repo.Name)) | |||
link.RawQuery = url.Values{"token": {token}}.Encode() |
@@ -28,7 +28,7 @@ func TestAPIRepoCollaboratorPermission(t *testing.T) { | |||
user10 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 10}) | |||
user11 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 11}) | |||
testCtx := NewAPITestContext(t, repo2Owner.Name, repo2.Name, auth_model.AccessTokenScopeRepo) | |||
testCtx := NewAPITestContext(t, repo2Owner.Name, repo2.Name, auth_model.AccessTokenScopeWriteRepository) | |||
t.Run("RepoOwnerShouldBeOwner", func(t *testing.T) { | |||
req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission?token=%s", repo2Owner.Name, repo2.Name, repo2Owner.Name, testCtx.Token) | |||
@@ -85,7 +85,7 @@ func TestAPIRepoCollaboratorPermission(t *testing.T) { | |||
t.Run("AddUserAsCollaboratorWithReadAccess", doAPIAddCollaborator(testCtx, user5.Name, perm.AccessModeRead)) | |||
_session := loginUser(t, user5.Name) | |||
_testCtx := NewAPITestContext(t, user5.Name, repo2.Name, auth_model.AccessTokenScopeRepo) | |||
_testCtx := NewAPITestContext(t, user5.Name, repo2.Name, auth_model.AccessTokenScopeReadRepository) | |||
req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission?token=%s", repo2Owner.Name, repo2.Name, user5.Name, _testCtx.Token) | |||
resp := _session.MakeRequest(t, req, http.StatusOK) | |||
@@ -100,7 +100,7 @@ func TestAPIRepoCollaboratorPermission(t *testing.T) { | |||
t.Run("AddUserAsCollaboratorWithReadAccess", doAPIAddCollaborator(testCtx, user5.Name, perm.AccessModeRead)) | |||
_session := loginUser(t, user5.Name) | |||
_testCtx := NewAPITestContext(t, user5.Name, repo2.Name, auth_model.AccessTokenScopeRepo) | |||
_testCtx := NewAPITestContext(t, user5.Name, repo2.Name, auth_model.AccessTokenScopeReadRepository) | |||
req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission?token=%s", repo2Owner.Name, repo2.Name, user5.Name, _testCtx.Token) | |||
resp := _session.MakeRequest(t, req, http.StatusOK) | |||
@@ -116,7 +116,7 @@ func TestAPIRepoCollaboratorPermission(t *testing.T) { | |||
t.Run("AddUserAsCollaboratorWithReadAccess", doAPIAddCollaborator(testCtx, user11.Name, perm.AccessModeRead)) | |||
_session := loginUser(t, user10.Name) | |||
_testCtx := NewAPITestContext(t, user10.Name, repo2.Name, auth_model.AccessTokenScopeRepo) | |||
_testCtx := NewAPITestContext(t, user10.Name, repo2.Name, auth_model.AccessTokenScopeReadRepository) | |||
req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission?token=%s", repo2Owner.Name, repo2.Name, user11.Name, _testCtx.Token) | |||
resp := _session.MakeRequest(t, req, http.StatusOK) |
@@ -147,10 +147,10 @@ func TestAPIRepoEdit(t *testing.T) { | |||
// Get user2's token | |||
session := loginUser(t, user2.Name) | |||
token2 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token2 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
// Get user4's token | |||
session = loginUser(t, user4.Name) | |||
token4 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token4 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
// Test editing a repo1 which user2 owns, changing name and many properties | |||
origRepoEditOption := getRepoEditOptionFromRepo(repo1) |
@@ -151,10 +151,10 @@ func TestAPICreateFile(t *testing.T) { | |||
// Get user2's token | |||
session := loginUser(t, user2.Name) | |||
token2 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token2 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) | |||
// Get user4's token | |||
session = loginUser(t, user4.Name) | |||
token4 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token4 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) | |||
// Test creating a file in repo1 which user2 owns, try both with branch and empty branch | |||
for _, branch := range [...]string{ | |||
@@ -280,7 +280,7 @@ func TestAPICreateFile(t *testing.T) { | |||
MakeRequest(t, req, http.StatusForbidden) | |||
// Test creating a file in an empty repository | |||
doAPICreateRepository(NewAPITestContext(t, "user2", "empty-repo", auth_model.AccessTokenScopeRepo), true)(t) | |||
doAPICreateRepository(NewAPITestContext(t, "user2", "empty-repo", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser), true)(t) | |||
createFileOptions = getCreateFileOptions() | |||
fileID++ | |||
treePath = fmt.Sprintf("new/file%d.txt", fileID) |
@@ -49,10 +49,10 @@ func TestAPIDeleteFile(t *testing.T) { | |||
// Get user2's token | |||
session := loginUser(t, user2.Name) | |||
token2 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token2 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
// Get user4's token | |||
session = loginUser(t, user4.Name) | |||
token4 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token4 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
// Test deleting a file in repo1 which user2 owns, try both with branch and empty branch | |||
for _, branch := range [...]string{ |
@@ -25,7 +25,7 @@ func TestAPIGetRawFileOrLFS(t *testing.T) { | |||
// Test with LFS | |||
onGiteaRun(t, func(t *testing.T, u *url.URL) { | |||
httpContext := NewAPITestContext(t, "user2", "repo-lfs-test", auth_model.AccessTokenScopeRepo, auth_model.AccessTokenScopeDeleteRepo) | |||
httpContext := NewAPITestContext(t, "user2", "repo-lfs-test", auth_model.AccessTokenScopeWriteRepository) | |||
doAPICreateRepository(httpContext, false, func(t *testing.T, repository api.Repository) { | |||
u.Path = httpContext.GitPath() | |||
dstPath := t.TempDir() |
@@ -117,10 +117,10 @@ func TestAPIUpdateFile(t *testing.T) { | |||
// Get user2's token | |||
session := loginUser(t, user2.Name) | |||
token2 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token2 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
// Get user4's token | |||
session = loginUser(t, user4.Name) | |||
token4 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token4 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
// Test updating a file in repo1 which user2 owns, try both with branch and empty branch | |||
for _, branch := range [...]string{ |
@@ -72,10 +72,10 @@ func TestAPIChangeFiles(t *testing.T) { | |||
// Get user2's token | |||
session := loginUser(t, user2.Name) | |||
token2 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token2 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
// Get user4's token | |||
session = loginUser(t, user4.Name) | |||
token4 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token4 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
// Test changing files in repo1 which user2 owns, try both with branch and empty branch | |||
for _, branch := range [...]string{ |
@@ -9,6 +9,7 @@ import ( | |||
"path/filepath" | |||
"testing" | |||
auth_model "code.gitea.io/gitea/models/auth" | |||
repo_model "code.gitea.io/gitea/models/repo" | |||
"code.gitea.io/gitea/models/unittest" | |||
user_model "code.gitea.io/gitea/models/user" | |||
@@ -64,10 +65,10 @@ func testAPIGetContentsList(t *testing.T, u *url.URL) { | |||
// Get user2's token | |||
session := loginUser(t, user2.Name) | |||
token2 := getTokenForLoggedInUser(t, session) | |||
token2 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) | |||
// Get user4's token | |||
session = loginUser(t, user4.Name) | |||
token4 := getTokenForLoggedInUser(t, session) | |||
token4 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) | |||
// Make a new branch in repo1 | |||
newBranch := "test_branch" |
@@ -9,6 +9,7 @@ import ( | |||
"net/url" | |||
"testing" | |||
auth_model "code.gitea.io/gitea/models/auth" | |||
repo_model "code.gitea.io/gitea/models/repo" | |||
"code.gitea.io/gitea/models/unittest" | |||
user_model "code.gitea.io/gitea/models/user" | |||
@@ -66,10 +67,10 @@ func testAPIGetContents(t *testing.T, u *url.URL) { | |||
// Get user2's token | |||
session := loginUser(t, user2.Name) | |||
token2 := getTokenForLoggedInUser(t, session) | |||
token2 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) | |||
// Get user4's token | |||
session = loginUser(t, user4.Name) | |||
token4 := getTokenForLoggedInUser(t, session) | |||
token4 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) | |||
// Make a new branch in repo1 | |||
newBranch := "test_branch" |
@@ -7,6 +7,7 @@ import ( | |||
"net/http" | |||
"testing" | |||
auth_model "code.gitea.io/gitea/models/auth" | |||
repo_model "code.gitea.io/gitea/models/repo" | |||
"code.gitea.io/gitea/models/unittest" | |||
user_model "code.gitea.io/gitea/models/user" | |||
@@ -31,7 +32,7 @@ func TestAPIReposGitBlobs(t *testing.T) { | |||
// Login as User2. | |||
session := loginUser(t, user2.Name) | |||
token := getTokenForLoggedInUser(t, session) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) | |||
// Test a public repo that anyone can GET the blob of | |||
req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/git/blobs/%s", user2.Name, repo1.Name, repo1ReadmeSHA) |
@@ -7,6 +7,7 @@ import ( | |||
"net/http" | |||
"testing" | |||
auth_model "code.gitea.io/gitea/models/auth" | |||
"code.gitea.io/gitea/models/unittest" | |||
user_model "code.gitea.io/gitea/models/user" | |||
api "code.gitea.io/gitea/modules/structs" | |||
@@ -28,7 +29,7 @@ func TestAPIReposGitCommits(t *testing.T) { | |||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | |||
// Login as User2. | |||
session := loginUser(t, user.Name) | |||
token := getTokenForLoggedInUser(t, session) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) | |||
// check invalid requests | |||
req := NewRequestf(t, "GET", "/api/v1/repos/%s/repo1/git/commits/12345?token="+token, user.Name) | |||
@@ -56,7 +57,7 @@ func TestAPIReposGitCommitList(t *testing.T) { | |||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | |||
// Login as User2. | |||
session := loginUser(t, user.Name) | |||
token := getTokenForLoggedInUser(t, session) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) | |||
// Test getting commits (Page 1) | |||
req := NewRequestf(t, "GET", "/api/v1/repos/%s/repo20/commits?token="+token+"¬=master&sha=remove-files-a", user.Name) | |||
@@ -79,7 +80,7 @@ func TestAPIReposGitCommitListNotMaster(t *testing.T) { | |||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | |||
// Login as User2. | |||
session := loginUser(t, user.Name) | |||
token := getTokenForLoggedInUser(t, session) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) | |||
// Test getting commits (Page 1) | |||
req := NewRequestf(t, "GET", "/api/v1/repos/%s/repo16/commits?token="+token, user.Name) | |||
@@ -104,7 +105,7 @@ func TestAPIReposGitCommitListPage2Empty(t *testing.T) { | |||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | |||
// Login as User2. | |||
session := loginUser(t, user.Name) | |||
token := getTokenForLoggedInUser(t, session) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) | |||
// Test getting commits (Page=2) | |||
req := NewRequestf(t, "GET", "/api/v1/repos/%s/repo16/commits?token="+token+"&page=2", user.Name) | |||
@@ -121,7 +122,7 @@ func TestAPIReposGitCommitListDifferentBranch(t *testing.T) { | |||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | |||
// Login as User2. | |||
session := loginUser(t, user.Name) | |||
token := getTokenForLoggedInUser(t, session) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) | |||
// Test getting commits (Page=1, Branch=good-sign) | |||
req := NewRequestf(t, "GET", "/api/v1/repos/%s/repo16/commits?token="+token+"&sha=good-sign", user.Name) | |||
@@ -140,7 +141,7 @@ func TestAPIReposGitCommitListWithoutSelectFields(t *testing.T) { | |||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | |||
// Login as User2. | |||
session := loginUser(t, user.Name) | |||
token := getTokenForLoggedInUser(t, session) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) | |||
// Test getting commits without files, verification, and stats | |||
req := NewRequestf(t, "GET", "/api/v1/repos/%s/repo16/commits?token="+token+"&sha=good-sign&stat=false&files=false&verification=false", user.Name) | |||
@@ -161,7 +162,7 @@ func TestDownloadCommitDiffOrPatch(t *testing.T) { | |||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | |||
// Login as User2. | |||
session := loginUser(t, user.Name) | |||
token := getTokenForLoggedInUser(t, session) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) | |||
// Test getting diff | |||
reqDiff := NewRequestf(t, "GET", "/api/v1/repos/%s/repo16/git/commits/f27c2b2b03dcab38beaf89b0ab4ff61f6de63441.diff?token="+token, user.Name) | |||
@@ -183,7 +184,7 @@ func TestGetFileHistory(t *testing.T) { | |||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | |||
// Login as User2. | |||
session := loginUser(t, user.Name) | |||
token := getTokenForLoggedInUser(t, session) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) | |||
req := NewRequestf(t, "GET", "/api/v1/repos/%s/repo16/commits?path=readme.md&token="+token+"&sha=good-sign", user.Name) | |||
resp := MakeRequest(t, req, http.StatusOK) | |||
@@ -203,7 +204,7 @@ func TestGetFileHistoryNotOnMaster(t *testing.T) { | |||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | |||
// Login as User2. | |||
session := loginUser(t, user.Name) | |||
token := getTokenForLoggedInUser(t, session) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) | |||
req := NewRequestf(t, "GET", "/api/v1/repos/%s/repo20/commits?path=test.csv&token="+token+"&sha=add-csv¬=master", user.Name) | |||
resp := MakeRequest(t, req, http.StatusOK) |
@@ -31,7 +31,7 @@ func TestAPIListGitHooks(t *testing.T) { | |||
// user1 is an admin user | |||
session := loginUser(t, "user1") | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepoHook) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) | |||
req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/hooks/git?token=%s", | |||
owner.Name, repo.Name, token) | |||
resp := MakeRequest(t, req, http.StatusOK) | |||
@@ -57,7 +57,7 @@ func TestAPIListGitHooksNoHooks(t *testing.T) { | |||
// user1 is an admin user | |||
session := loginUser(t, "user1") | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepoHook) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) | |||
req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/hooks/git?token=%s", | |||
owner.Name, repo.Name, token) | |||
resp := MakeRequest(t, req, http.StatusOK) | |||
@@ -77,7 +77,7 @@ func TestAPIListGitHooksNoAccess(t *testing.T) { | |||
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | |||
session := loginUser(t, owner.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepoHook) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) | |||
req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/hooks/git?token=%s", | |||
owner.Name, repo.Name, token) | |||
MakeRequest(t, req, http.StatusForbidden) | |||
@@ -91,7 +91,7 @@ func TestAPIGetGitHook(t *testing.T) { | |||
// user1 is an admin user | |||
session := loginUser(t, "user1") | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepoHook) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) | |||
req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/hooks/git/pre-receive?token=%s", | |||
owner.Name, repo.Name, token) | |||
resp := MakeRequest(t, req, http.StatusOK) | |||
@@ -108,7 +108,7 @@ func TestAPIGetGitHookNoAccess(t *testing.T) { | |||
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | |||
session := loginUser(t, owner.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepoHook) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) | |||
req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/hooks/git/pre-receive?token=%s", | |||
owner.Name, repo.Name, token) | |||
MakeRequest(t, req, http.StatusForbidden) | |||
@@ -122,7 +122,7 @@ func TestAPIEditGitHook(t *testing.T) { | |||
// user1 is an admin user | |||
session := loginUser(t, "user1") | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAdminRepoHook) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/hooks/git/pre-receive?token=%s", | |||
owner.Name, repo.Name, token) | |||
@@ -151,7 +151,7 @@ func TestAPIEditGitHookNoAccess(t *testing.T) { | |||
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | |||
session := loginUser(t, owner.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepoHook) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/hooks/git/pre-receive?token=%s", | |||
owner.Name, repo.Name, token) | |||
req := NewRequestWithJSON(t, "PATCH", urlStr, &api.EditGitHookOption{ | |||
@@ -168,7 +168,7 @@ func TestAPIDeleteGitHook(t *testing.T) { | |||
// user1 is an admin user | |||
session := loginUser(t, "user1") | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAdminRepoHook) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
req := NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/hooks/git/pre-receive?token=%s", | |||
owner.Name, repo.Name, token) | |||
@@ -190,7 +190,7 @@ func TestAPIDeleteGitHookNoAccess(t *testing.T) { | |||
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | |||
session := loginUser(t, owner.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepoHook) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
req := NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/hooks/git/pre-receive?token=%s", | |||
owner.Name, repo.Name, token) | |||
MakeRequest(t, req, http.StatusForbidden) |
@@ -8,6 +8,7 @@ import ( | |||
"net/url" | |||
"testing" | |||
auth_model "code.gitea.io/gitea/models/auth" | |||
"code.gitea.io/gitea/models/unittest" | |||
user_model "code.gitea.io/gitea/models/user" | |||
api "code.gitea.io/gitea/modules/structs" | |||
@@ -20,7 +21,7 @@ func TestAPIReposGitNotes(t *testing.T) { | |||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | |||
// Login as User2. | |||
session := loginUser(t, user.Name) | |||
token := getTokenForLoggedInUser(t, session) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) | |||
// check invalid requests | |||
req := NewRequestf(t, "GET", "/api/v1/repos/%s/repo1/git/notes/12345?token=%s", user.Name, token) |
@@ -7,6 +7,7 @@ import ( | |||
"net/http" | |||
"testing" | |||
auth_model "code.gitea.io/gitea/models/auth" | |||
"code.gitea.io/gitea/models/unittest" | |||
user_model "code.gitea.io/gitea/models/user" | |||
"code.gitea.io/gitea/tests" | |||
@@ -17,7 +18,7 @@ func TestAPIReposGitRefs(t *testing.T) { | |||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | |||
// Login as User2. | |||
session := loginUser(t, user.Name) | |||
token := getTokenForLoggedInUser(t, session) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) | |||
for _, ref := range [...]string{ | |||
"refs/heads/master", // Branch |
@@ -26,7 +26,7 @@ func TestAPIGitTags(t *testing.T) { | |||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) | |||
// Login as User2. | |||
session := loginUser(t, user.Name) | |||
token := getTokenForLoggedInUser(t, session) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) | |||
// Set up git config for the tagger | |||
_ = git.NewCommand(git.DefaultContext, "config", "user.name").AddDynamicArguments(user.Name).Run(&git.RunOpts{Dir: repo.RepoPath()}) | |||
@@ -70,7 +70,7 @@ func TestAPIDeleteTagByName(t *testing.T) { | |||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) | |||
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | |||
session := loginUser(t, owner.LowerName) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/tags/delete-tag?token=%s", | |||
owner.Name, repo.Name, token) |
@@ -7,6 +7,7 @@ import ( | |||
"net/http" | |||
"testing" | |||
auth_model "code.gitea.io/gitea/models/auth" | |||
repo_model "code.gitea.io/gitea/models/repo" | |||
"code.gitea.io/gitea/models/unittest" | |||
user_model "code.gitea.io/gitea/models/user" | |||
@@ -28,7 +29,7 @@ func TestAPIReposGitTrees(t *testing.T) { | |||
// Login as User2. | |||
session := loginUser(t, user2.Name) | |||
token := getTokenForLoggedInUser(t, session) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) | |||
// Test a public repo that anyone can GET the tree of | |||
for _, ref := range [...]string{ |
@@ -26,7 +26,7 @@ func TestAPICreateHook(t *testing.T) { | |||
// user1 is an admin user | |||
session := loginUser(t, "user1") | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepoHook) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
completeURL := func(lastSegment string) string { | |||
return fmt.Sprintf("/api/v1/repos/%s/%s/%s?token=%s", owner.Name, repo.Name, lastSegment, token) | |||
} |
@@ -31,7 +31,7 @@ func TestAPIRepoLFSMigrateLocal(t *testing.T) { | |||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) | |||
session := loginUser(t, user.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
req := NewRequestWithJSON(t, "POST", "/api/v1/repos/migrate?token="+token, &api.MigrateRepoOptions{ | |||
CloneAddr: path.Join(setting.RepoRootPath, "migration/lfs-test.git"), |
@@ -60,7 +60,7 @@ func TestAPILFSMediaType(t *testing.T) { | |||
} | |||
func createLFSTestRepository(t *testing.T, name string) *repo_model.Repository { | |||
ctx := NewAPITestContext(t, "user2", "lfs-"+name+"-repo", auth_model.AccessTokenScopeRepo) | |||
ctx := NewAPITestContext(t, "user2", "lfs-"+name+"-repo", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) | |||
t.Run("CreateRepo", doAPICreateRepository(ctx, false)) | |||
repo, err := repo_model.GetRepositoryByOwnerAndName(db.DefaultContext, "user2", "lfs-"+name+"-repo") |
@@ -20,7 +20,7 @@ func TestAPIReposRaw(t *testing.T) { | |||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | |||
// Login as User2. | |||
session := loginUser(t, user.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) | |||
for _, ref := range [...]string{ | |||
"master", // Branch |
@@ -23,7 +23,7 @@ func TestAPIRepoTags(t *testing.T) { | |||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | |||
// Login as User2. | |||
session := loginUser(t, user.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
repoName := "repo1" | |||
@@ -28,7 +28,7 @@ func TestAPIRepoTeams(t *testing.T) { | |||
// user4 | |||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) | |||
session := loginUser(t, user.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
// ListTeams | |||
url := fmt.Sprintf("/api/v1/repos/%s/teams?token=%s", publicOrgRepo.FullName(), token) | |||
@@ -68,7 +68,7 @@ func TestAPIRepoTeams(t *testing.T) { | |||
// AddTeam with user2 | |||
user = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | |||
session = loginUser(t, user.Name) | |||
token = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
url = fmt.Sprintf("/api/v1/repos/%s/teams/%s?token=%s", publicOrgRepo.FullName(), "team1", token) | |||
req = NewRequest(t, "PUT", url) | |||
MakeRequest(t, req, http.StatusNoContent) |
@@ -189,7 +189,7 @@ func TestAPISearchRepo(t *testing.T) { | |||
if userToLogin != nil && userToLogin.ID > 0 { | |||
testName = fmt.Sprintf("LoggedUser%d", userToLogin.ID) | |||
session := loginUser(t, userToLogin.Name) | |||
token = getTokenForLoggedInUser(t, session) | |||
token = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) | |||
userID = userToLogin.ID | |||
} else { | |||
testName = "AnonymousUser" | |||
@@ -295,7 +295,7 @@ func TestAPIOrgRepos(t *testing.T) { | |||
for userToLogin, expected := range expectedResults { | |||
testName := fmt.Sprintf("LoggedUser%d", userToLogin.ID) | |||
session := loginUser(t, userToLogin.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadOrg) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadOrganization) | |||
t.Run(testName, func(t *testing.T) { | |||
req := NewRequestf(t, "GET", "/api/v1/orgs/%s/repos?token="+token, sourceOrg.Name) | |||
@@ -317,7 +317,7 @@ func TestAPIGetRepoByIDUnauthorized(t *testing.T) { | |||
defer tests.PrepareTestEnv(t)() | |||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) | |||
session := loginUser(t, user.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) | |||
req := NewRequestf(t, "GET", "/api/v1/repositories/2?token="+token) | |||
MakeRequest(t, req, http.StatusNotFound) | |||
} | |||
@@ -341,7 +341,7 @@ func TestAPIRepoMigrate(t *testing.T) { | |||
for _, testCase := range testCases { | |||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: testCase.ctxUserID}) | |||
session := loginUser(t, user.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
req := NewRequestWithJSON(t, "POST", "/api/v1/repos/migrate?token="+token, &api.MigrateRepoOptions{ | |||
CloneAddr: testCase.cloneURL, | |||
RepoOwnerID: testCase.userID, | |||
@@ -371,7 +371,7 @@ func TestAPIRepoMigrateConflict(t *testing.T) { | |||
func testAPIRepoMigrateConflict(t *testing.T, u *url.URL) { | |||
username := "user2" | |||
baseAPITestContext := NewAPITestContext(t, username, "repo1", auth_model.AccessTokenScopeRepo) | |||
baseAPITestContext := NewAPITestContext(t, username, "repo1", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) | |||
u.Path = baseAPITestContext.GitPath() | |||
@@ -406,7 +406,7 @@ func TestAPIMirrorSyncNonMirrorRepo(t *testing.T) { | |||
defer tests.PrepareTestEnv(t)() | |||
session := loginUser(t, "user2") | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
var repo api.Repository | |||
req := NewRequest(t, "GET", "/api/v1/repos/user2/repo1") | |||
@@ -438,7 +438,7 @@ func TestAPIOrgRepoCreate(t *testing.T) { | |||
for _, testCase := range testCases { | |||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: testCase.ctxUserID}) | |||
session := loginUser(t, user.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAdminOrg) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteOrganization, auth_model.AccessTokenScopeWriteRepository) | |||
req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/org/%s/repos?token="+token, testCase.orgName), &api.CreateRepoOption{ | |||
Name: testCase.repoName, | |||
}) | |||
@@ -452,7 +452,7 @@ func TestAPIRepoCreateConflict(t *testing.T) { | |||
func testAPIRepoCreateConflict(t *testing.T, u *url.URL) { | |||
username := "user2" | |||
baseAPITestContext := NewAPITestContext(t, username, "repo1", auth_model.AccessTokenScopeRepo) | |||
baseAPITestContext := NewAPITestContext(t, username, "repo1", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) | |||
u.Path = baseAPITestContext.GitPath() | |||
@@ -502,7 +502,7 @@ func TestAPIRepoTransfer(t *testing.T) { | |||
// create repo to move | |||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) | |||
session := loginUser(t, user.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) | |||
repoName := "moveME" | |||
apiRepo := new(api.Repository) | |||
req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/user/repos?token=%s", token), &api.CreateRepoOption{ | |||
@@ -520,7 +520,7 @@ func TestAPIRepoTransfer(t *testing.T) { | |||
user = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: testCase.ctxUserID}) | |||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: apiRepo.ID}) | |||
session = loginUser(t, user.Name) | |||
token = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer?token=%s", repo.OwnerName, repo.Name, token), &api.TransferRepoOption{ | |||
NewOwner: testCase.newOwner, | |||
TeamIDs: testCase.teams, | |||
@@ -537,7 +537,7 @@ func transfer(t *testing.T) *repo_model.Repository { | |||
// create repo to move | |||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | |||
session := loginUser(t, user.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) | |||
repoName := "moveME" | |||
apiRepo := new(api.Repository) | |||
req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/user/repos?token=%s", token), &api.CreateRepoOption{ | |||
@@ -567,7 +567,7 @@ func TestAPIAcceptTransfer(t *testing.T) { | |||
// try to accept with not authorized user | |||
session := loginUser(t, "user2") | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) | |||
req := NewRequest(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer/reject?token=%s", repo.OwnerName, repo.Name, token)) | |||
MakeRequest(t, req, http.StatusForbidden) | |||
@@ -577,7 +577,7 @@ func TestAPIAcceptTransfer(t *testing.T) { | |||
// accept transfer | |||
session = loginUser(t, "user4") | |||
token = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) | |||
req = NewRequest(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer/accept?token=%s", repo.OwnerName, repo.Name, token)) | |||
resp := MakeRequest(t, req, http.StatusAccepted) | |||
@@ -593,7 +593,7 @@ func TestAPIRejectTransfer(t *testing.T) { | |||
// try to reject with not authorized user | |||
session := loginUser(t, "user2") | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
req := NewRequest(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer/reject?token=%s", repo.OwnerName, repo.Name, token)) | |||
MakeRequest(t, req, http.StatusForbidden) | |||
@@ -603,7 +603,7 @@ func TestAPIRejectTransfer(t *testing.T) { | |||
// reject transfer | |||
session = loginUser(t, "user4") | |||
token = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
req = NewRequest(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer/reject?token=%s", repo.OwnerName, repo.Name, token)) | |||
resp := MakeRequest(t, req, http.StatusOK) | |||
@@ -617,7 +617,7 @@ func TestAPIGenerateRepo(t *testing.T) { | |||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) | |||
session := loginUser(t, user.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
templateRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 44}) | |||
@@ -653,7 +653,7 @@ func TestAPIRepoGetReviewers(t *testing.T) { | |||
defer tests.PrepareTestEnv(t)() | |||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | |||
session := loginUser(t, user.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) | |||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) | |||
req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/reviewers?token=%s", user.Name, repo.Name, token) | |||
@@ -667,7 +667,7 @@ func TestAPIRepoGetAssignees(t *testing.T) { | |||
defer tests.PrepareTestEnv(t)() | |||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | |||
session := loginUser(t, user.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) | |||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) | |||
req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/assignees?token=%s", user.Name, repo.Name, token) |
@@ -60,7 +60,7 @@ func TestAPIRepoTopic(t *testing.T) { | |||
repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}) | |||
// Get user2's token | |||
token2 := getUserToken(t, user2.Name, auth_model.AccessTokenScopeRepo) | |||
token2 := getUserToken(t, user2.Name, auth_model.AccessTokenScopeWriteRepository) | |||
// Test read topics using login | |||
url := fmt.Sprintf("/api/v1/repos/%s/%s/topics", user2.Name, repo2.Name) | |||
@@ -140,7 +140,7 @@ func TestAPIRepoTopic(t *testing.T) { | |||
MakeRequest(t, req, http.StatusNotFound) | |||
// Get user4's token | |||
token4 := getUserToken(t, user4.Name, auth_model.AccessTokenScopeRepo) | |||
token4 := getUserToken(t, user4.Name, auth_model.AccessTokenScopeWriteRepository) | |||
// Test read topics with write access | |||
url = fmt.Sprintf("/api/v1/repos/%s/%s/topics?token=%s", user3.Name, repo3.Name, token4) |
@@ -33,7 +33,7 @@ func TestAPITeam(t *testing.T) { | |||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: teamUser.UID}) | |||
session := loginUser(t, user.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAdminOrg) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadOrganization) | |||
req := NewRequestf(t, "GET", "/api/v1/teams/%d?token="+token, teamUser.TeamID) | |||
resp := MakeRequest(t, req, http.StatusOK) | |||
@@ -48,7 +48,7 @@ func TestAPITeam(t *testing.T) { | |||
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: teamUser2.UID}) | |||
session = loginUser(t, user2.Name) | |||
token = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadOrg) | |||
token = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadOrganization) | |||
req = NewRequestf(t, "GET", "/api/v1/teams/%d?token="+token, teamUser.TeamID) | |||
_ = MakeRequest(t, req, http.StatusForbidden) | |||
@@ -58,7 +58,7 @@ func TestAPITeam(t *testing.T) { | |||
// Get an admin user able to create, update and delete teams. | |||
user = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) | |||
session = loginUser(t, user.Name) | |||
token = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAdminOrg) | |||
token = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteOrganization) | |||
org = unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 6}) | |||
@@ -262,7 +262,7 @@ func TestAPITeamSearch(t *testing.T) { | |||
var results TeamSearchResults | |||
token := getUserToken(t, user.Name, auth_model.AccessTokenScopeReadOrg) | |||
token := getUserToken(t, user.Name, auth_model.AccessTokenScopeReadOrganization) | |||
req := NewRequestf(t, "GET", "/api/v1/orgs/%s/teams/search?q=%s&token=%s", org.Name, "_team", token) | |||
resp := MakeRequest(t, req, http.StatusOK) | |||
DecodeJSON(t, resp, &results) | |||
@@ -272,7 +272,7 @@ func TestAPITeamSearch(t *testing.T) { | |||
// no access if not organization member | |||
user5 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}) | |||
token5 := getUserToken(t, user5.Name, auth_model.AccessTokenScopeReadOrg) | |||
token5 := getUserToken(t, user5.Name, auth_model.AccessTokenScopeReadOrganization) | |||
req = NewRequestf(t, "GET", "/api/v1/orgs/%s/teams/search?q=%s&token=%s", org.Name, "team", token5) | |||
MakeRequest(t, req, http.StatusForbidden) | |||
@@ -287,7 +287,7 @@ func TestAPIGetTeamRepo(t *testing.T) { | |||
var results api.Repository | |||
token := getUserToken(t, user.Name, auth_model.AccessTokenScopeReadOrg) | |||
token := getUserToken(t, user.Name, auth_model.AccessTokenScopeReadOrganization) | |||
req := NewRequestf(t, "GET", "/api/v1/teams/%d/repos/%s/?token=%s", team.ID, teamRepo.FullName(), token) | |||
resp := MakeRequest(t, req, http.StatusOK) | |||
DecodeJSON(t, resp, &results) | |||
@@ -295,7 +295,7 @@ func TestAPIGetTeamRepo(t *testing.T) { | |||
// no access if not organization member | |||
user5 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}) | |||
token5 := getUserToken(t, user5.Name, auth_model.AccessTokenScopeReadOrg) | |||
token5 := getUserToken(t, user5.Name, auth_model.AccessTokenScopeReadOrganization) | |||
req = NewRequestf(t, "GET", "/api/v1/teams/%d/repos/%s/?token=%s", team.ID, teamRepo.FullName(), token5) | |||
MakeRequest(t, req, http.StatusNotFound) |
@@ -24,7 +24,7 @@ func TestAPITeamUser(t *testing.T) { | |||
normalUsername := "user2" | |||
session := loginUser(t, normalUsername) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadOrg) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadOrganization) | |||
req := NewRequest(t, "GET", "/api/v1/teams/1/members/user1?token="+token) | |||
MakeRequest(t, req, http.StatusNotFound) | |||
@@ -4,14 +4,18 @@ | |||
package integration | |||
import ( | |||
"fmt" | |||
"net/http" | |||
"testing" | |||
auth_model "code.gitea.io/gitea/models/auth" | |||
"code.gitea.io/gitea/models/unittest" | |||
user_model "code.gitea.io/gitea/models/user" | |||
"code.gitea.io/gitea/modules/log" | |||
api "code.gitea.io/gitea/modules/structs" | |||
"code.gitea.io/gitea/tests" | |||
"github.com/stretchr/testify/assert" | |||
) | |||
// TestAPICreateAndDeleteToken tests that token that was just created can be deleted | |||
@@ -19,9 +23,518 @@ func TestAPICreateAndDeleteToken(t *testing.T) { | |||
defer tests.PrepareTestEnv(t)() | |||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) | |||
req := NewRequestWithJSON(t, "POST", "/api/v1/users/user1/tokens", map[string]string{ | |||
"name": "test-key-1", | |||
newAccessToken := createAPIAccessTokenWithoutCleanUp(t, "test-key-1", user, nil) | |||
deleteAPIAccessToken(t, newAccessToken, user) | |||
newAccessToken = createAPIAccessTokenWithoutCleanUp(t, "test-key-2", user, nil) | |||
deleteAPIAccessToken(t, newAccessToken, user) | |||
} | |||
// TestAPIDeleteMissingToken ensures that error is thrown when token not found | |||
func TestAPIDeleteMissingToken(t *testing.T) { | |||
defer tests.PrepareTestEnv(t)() | |||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) | |||
req := NewRequestf(t, "DELETE", "/api/v1/users/user1/tokens/%d", unittest.NonexistentID) | |||
req = AddBasicAuthHeader(req, user.Name) | |||
MakeRequest(t, req, http.StatusNotFound) | |||
} | |||
type permission struct { | |||
category auth_model.AccessTokenScopeCategory | |||
level auth_model.AccessTokenScopeLevel | |||
} | |||
type requiredScopeTestCase struct { | |||
url string | |||
method string | |||
requiredPermissions []permission | |||
} | |||
func (c *requiredScopeTestCase) Name() string { | |||
return fmt.Sprintf("%v %v", c.method, c.url) | |||
} | |||
// TestAPIDeniesPermissionBasedOnTokenScope tests that API routes forbid access | |||
// when the correct token scope is not included. | |||
func TestAPIDeniesPermissionBasedOnTokenScope(t *testing.T) { | |||
defer tests.PrepareTestEnv(t)() | |||
// We'll assert that each endpoint, when fetched with a token with all | |||
// scopes *except* the ones specified, a forbidden status code is returned. | |||
// | |||
// This is to protect against endpoints having their access check copied | |||
// from other endpoints and not updated. | |||
// | |||
// Test cases are in alphabetical order by URL. | |||
// | |||
// Note: query parameters are not currently supported since the token is | |||
// appended with `?=token=<token>`. | |||
testCases := []requiredScopeTestCase{ | |||
{ | |||
"/api/v1/admin/emails", | |||
"GET", | |||
[]permission{ | |||
{ | |||
auth_model.AccessTokenScopeCategoryAdmin, | |||
auth_model.Read, | |||
}, | |||
}, | |||
}, | |||
{ | |||
"/api/v1/admin/users", | |||
"GET", | |||
[]permission{ | |||
{ | |||
auth_model.AccessTokenScopeCategoryAdmin, | |||
auth_model.Read, | |||
}, | |||
}, | |||
}, | |||
{ | |||
"/api/v1/admin/users", | |||
"POST", | |||
[]permission{ | |||
{ | |||
auth_model.AccessTokenScopeCategoryAdmin, | |||
auth_model.Write, | |||
}, | |||
}, | |||
}, | |||
{ | |||
"/api/v1/admin/users/user2", | |||
"PATCH", | |||
[]permission{ | |||
{ | |||
auth_model.AccessTokenScopeCategoryAdmin, | |||
auth_model.Write, | |||
}, | |||
}, | |||
}, | |||
{ | |||
"/api/v1/admin/users/user2/orgs", | |||
"GET", | |||
[]permission{ | |||
{ | |||
auth_model.AccessTokenScopeCategoryAdmin, | |||
auth_model.Read, | |||
}, | |||
}, | |||
}, | |||
{ | |||
"/api/v1/admin/users/user2/orgs", | |||
"POST", | |||
[]permission{ | |||
{ | |||
auth_model.AccessTokenScopeCategoryAdmin, | |||
auth_model.Write, | |||
}, | |||
}, | |||
}, | |||
{ | |||
"/api/v1/admin/orgs", | |||
"GET", | |||
[]permission{ | |||
{ | |||
auth_model.AccessTokenScopeCategoryAdmin, | |||
auth_model.Read, | |||
}, | |||
}, | |||
}, | |||
{ | |||
"/api/v1/markdown", | |||
"POST", | |||
[]permission{ | |||
{ | |||
auth_model.AccessTokenScopeCategoryMisc, | |||
auth_model.Write, | |||
}, | |||
}, | |||
}, | |||
{ | |||
"/api/v1/markdown/raw", | |||
"POST", | |||
[]permission{ | |||
{ | |||
auth_model.AccessTokenScopeCategoryMisc, | |||
auth_model.Write, | |||
}, | |||
}, | |||
}, | |||
{ | |||
"/api/v1/notifications", | |||
"GET", | |||
[]permission{ | |||
{ | |||
auth_model.AccessTokenScopeCategoryNotification, | |||
auth_model.Read, | |||
}, | |||
}, | |||
}, | |||
{ | |||
"/api/v1/notifications", | |||
"PUT", | |||
[]permission{ | |||
{ | |||
auth_model.AccessTokenScopeCategoryNotification, | |||
auth_model.Write, | |||
}, | |||
}, | |||
}, | |||
{ | |||
"/api/v1/org/org1/repos", | |||
"POST", | |||
[]permission{ | |||
{ | |||
auth_model.AccessTokenScopeCategoryOrganization, | |||
auth_model.Write, | |||
}, | |||
{ | |||
auth_model.AccessTokenScopeCategoryRepository, | |||
auth_model.Write, | |||
}, | |||
}, | |||
}, | |||
{ | |||
"/api/v1/packages/user1/type/name/1", | |||
"GET", | |||
[]permission{ | |||
{ | |||
auth_model.AccessTokenScopeCategoryPackage, | |||
auth_model.Read, | |||
}, | |||
}, | |||
}, | |||
{ | |||
"/api/v1/packages/user1/type/name/1", | |||
"DELETE", | |||
[]permission{ | |||
{ | |||
auth_model.AccessTokenScopeCategoryPackage, | |||
auth_model.Write, | |||
}, | |||
}, | |||
}, | |||
{ | |||
"/api/v1/repos/user1/repo1", | |||
"GET", | |||
[]permission{ | |||
{ | |||
auth_model.AccessTokenScopeCategoryRepository, | |||
auth_model.Read, | |||
}, | |||
}, | |||
}, | |||
{ | |||
"/api/v1/repos/user1/repo1", | |||
"PATCH", | |||
[]permission{ | |||
{ | |||
auth_model.AccessTokenScopeCategoryRepository, | |||
auth_model.Write, | |||
}, | |||
}, | |||
}, | |||
{ | |||
"/api/v1/repos/user1/repo1", | |||
"DELETE", | |||
[]permission{ | |||
{ | |||
auth_model.AccessTokenScopeCategoryRepository, | |||
auth_model.Write, | |||
}, | |||
}, | |||
}, | |||
{ | |||
"/api/v1/repos/user1/repo1/branches", | |||
"GET", | |||
[]permission{ | |||
{ | |||
auth_model.AccessTokenScopeCategoryRepository, | |||
auth_model.Read, | |||
}, | |||
}, | |||
}, | |||
{ | |||
"/api/v1/repos/user1/repo1/archive/foo", | |||
"GET", | |||
[]permission{ | |||
{ | |||
auth_model.AccessTokenScopeCategoryRepository, | |||
auth_model.Read, | |||
}, | |||
}, | |||
}, | |||
{ | |||
"/api/v1/repos/user1/repo1/issues", | |||
"GET", | |||
[]permission{ | |||
{ | |||
auth_model.AccessTokenScopeCategoryIssue, | |||
auth_model.Read, | |||
}, | |||
}, | |||
}, | |||
{ | |||
"/api/v1/repos/user1/repo1/media/foo", | |||
"GET", | |||
[]permission{ | |||
{ | |||
auth_model.AccessTokenScopeCategoryRepository, | |||
auth_model.Read, | |||
}, | |||
}, | |||
}, | |||
{ | |||
"/api/v1/repos/user1/repo1/raw/foo", | |||
"GET", | |||
[]permission{ | |||
{ | |||
auth_model.AccessTokenScopeCategoryRepository, | |||
auth_model.Read, | |||
}, | |||
}, | |||
}, | |||
{ | |||
"/api/v1/repos/user1/repo1/teams", | |||
"GET", | |||
[]permission{ | |||
{ | |||
auth_model.AccessTokenScopeCategoryRepository, | |||
auth_model.Read, | |||
}, | |||
}, | |||
}, | |||
{ | |||
"/api/v1/repos/user1/repo1/teams/team1", | |||
"PUT", | |||
[]permission{ | |||
{ | |||
auth_model.AccessTokenScopeCategoryRepository, | |||
auth_model.Write, | |||
}, | |||
}, | |||
}, | |||
{ | |||
"/api/v1/repos/user1/repo1/transfer", | |||
"POST", | |||
[]permission{ | |||
{ | |||
auth_model.AccessTokenScopeCategoryRepository, | |||
auth_model.Write, | |||
}, | |||
}, | |||
}, | |||
// Private repo | |||
{ | |||
"/api/v1/repos/user2/repo2", | |||
"GET", | |||
[]permission{ | |||
{ | |||
auth_model.AccessTokenScopeCategoryRepository, | |||
auth_model.Read, | |||
}, | |||
}, | |||
}, | |||
// Private repo | |||
{ | |||
"/api/v1/repos/user2/repo2", | |||
"GET", | |||
[]permission{ | |||
{ | |||
auth_model.AccessTokenScopeCategoryRepository, | |||
auth_model.Read, | |||
}, | |||
}, | |||
}, | |||
{ | |||
"/api/v1/settings/api", | |||
"GET", | |||
[]permission{ | |||
{ | |||
auth_model.AccessTokenScopeCategoryMisc, | |||
auth_model.Read, | |||
}, | |||
}, | |||
}, | |||
{ | |||
"/api/v1/user", | |||
"GET", | |||
[]permission{ | |||
{ | |||
auth_model.AccessTokenScopeCategoryUser, | |||
auth_model.Read, | |||
}, | |||
}, | |||
}, | |||
{ | |||
"/api/v1/user/emails", | |||
"GET", | |||
[]permission{ | |||
{ | |||
auth_model.AccessTokenScopeCategoryUser, | |||
auth_model.Read, | |||
}, | |||
}, | |||
}, | |||
{ | |||
"/api/v1/user/emails", | |||
"POST", | |||
[]permission{ | |||
{ | |||
auth_model.AccessTokenScopeCategoryUser, | |||
auth_model.Write, | |||
}, | |||
}, | |||
}, | |||
{ | |||
"/api/v1/user/emails", | |||
"DELETE", | |||
[]permission{ | |||
{ | |||
auth_model.AccessTokenScopeCategoryUser, | |||
auth_model.Write, | |||
}, | |||
}, | |||
}, | |||
{ | |||
"/api/v1/user/applications/oauth2", | |||
"GET", | |||
[]permission{ | |||
{ | |||
auth_model.AccessTokenScopeCategoryUser, | |||
auth_model.Read, | |||
}, | |||
}, | |||
}, | |||
{ | |||
"/api/v1/user/applications/oauth2", | |||
"POST", | |||
[]permission{ | |||
{ | |||
auth_model.AccessTokenScopeCategoryUser, | |||
auth_model.Write, | |||
}, | |||
}, | |||
}, | |||
{ | |||
"/api/v1/users/search", | |||
"GET", | |||
[]permission{ | |||
{ | |||
auth_model.AccessTokenScopeCategoryUser, | |||
auth_model.Read, | |||
}, | |||
}, | |||
}, | |||
// Private user | |||
{ | |||
"/api/v1/users/user31", | |||
"GET", | |||
[]permission{ | |||
{ | |||
auth_model.AccessTokenScopeCategoryUser, | |||
auth_model.Read, | |||
}, | |||
}, | |||
}, | |||
// Private user | |||
{ | |||
"/api/v1/users/user31/gpg_keys", | |||
"GET", | |||
[]permission{ | |||
{ | |||
auth_model.AccessTokenScopeCategoryUser, | |||
auth_model.Read, | |||
}, | |||
}, | |||
}, | |||
} | |||
// User needs to be admin so that we can verify that tokens without admin | |||
// scopes correctly deny access. | |||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) | |||
assert.True(t, user.IsAdmin, "User needs to be admin") | |||
for _, testCase := range testCases { | |||
runTestCase(t, &testCase, user) | |||
} | |||
} | |||
// runTestCase Helper function to run a single test case. | |||
func runTestCase(t *testing.T, testCase *requiredScopeTestCase, user *user_model.User) { | |||
t.Run(testCase.Name(), func(t *testing.T) { | |||
defer tests.PrintCurrentTest(t)() | |||
// Create a token with all scopes NOT required by the endpoint. | |||
var unauthorizedScopes []auth_model.AccessTokenScope | |||
for _, category := range auth_model.AllAccessTokenScopeCategories { | |||
// For permissions, Write > Read > NoAccess. So we need to | |||
// find the minimum required, and only grant permission up to but | |||
// not including the minimum required. | |||
minRequiredLevel := auth_model.Write | |||
categoryIsRequired := false | |||
for _, requiredPermission := range testCase.requiredPermissions { | |||
if requiredPermission.category != category { | |||
continue | |||
} | |||
categoryIsRequired = true | |||
if requiredPermission.level < minRequiredLevel { | |||
minRequiredLevel = requiredPermission.level | |||
} | |||
} | |||
unauthorizedLevel := auth_model.Write | |||
if categoryIsRequired { | |||
if minRequiredLevel == auth_model.Read { | |||
unauthorizedLevel = auth_model.NoAccess | |||
} else if minRequiredLevel == auth_model.Write { | |||
unauthorizedLevel = auth_model.Read | |||
} else { | |||
assert.Failf(t, "Invalid test case", "Unknown access token scope level: %v", minRequiredLevel) | |||
return | |||
} | |||
} | |||
if unauthorizedLevel == auth_model.NoAccess { | |||
continue | |||
} | |||
cateogoryUnauthorizedScopes := auth_model.GetRequiredScopes( | |||
unauthorizedLevel, | |||
category) | |||
unauthorizedScopes = append(unauthorizedScopes, cateogoryUnauthorizedScopes...) | |||
} | |||
accessToken := createAPIAccessTokenWithoutCleanUp(t, "test-token", user, &unauthorizedScopes) | |||
defer deleteAPIAccessToken(t, accessToken, user) | |||
// Add API access token to the URL. | |||
url := fmt.Sprintf("%s?token=%s", testCase.url, accessToken.Token) | |||
// Request the endpoint. Verify that permission is denied. | |||
req := NewRequestf(t, testCase.method, url) | |||
MakeRequest(t, req, http.StatusForbidden) | |||
}) | |||
} | |||
// createAPIAccessTokenWithoutCleanUp Create an API access token and assert that | |||
// creation succeeded. The caller is responsible for deleting the token. | |||
func createAPIAccessTokenWithoutCleanUp(t *testing.T, tokenName string, user *user_model.User, scopes *[]auth_model.AccessTokenScope) api.AccessToken { | |||
payload := map[string]interface{}{ | |||
"name": tokenName, | |||
} | |||
if scopes != nil { | |||
for _, scope := range *scopes { | |||
scopes, scopesExists := payload["scopes"].([]string) | |||
if !scopesExists { | |||
scopes = make([]string, 0) | |||
} | |||
scopes = append(scopes, string(scope)) | |||
payload["scopes"] = scopes | |||
} | |||
} | |||
log.Debug("Requesting creation of token with scopes: %v", scopes) | |||
req := NewRequestWithJSON(t, "POST", "/api/v1/users/user1/tokens", payload) | |||
req = AddBasicAuthHeader(req, user.Name) | |||
resp := MakeRequest(t, req, http.StatusCreated) | |||
@@ -34,32 +547,15 @@ func TestAPICreateAndDeleteToken(t *testing.T) { | |||
UID: user.ID, | |||
}) | |||
req = NewRequestf(t, "DELETE", "/api/v1/users/user1/tokens/%d", newAccessToken.ID) | |||
req = AddBasicAuthHeader(req, user.Name) | |||
MakeRequest(t, req, http.StatusNoContent) | |||
unittest.AssertNotExistsBean(t, &auth_model.AccessToken{ID: newAccessToken.ID}) | |||
req = NewRequestWithJSON(t, "POST", "/api/v1/users/user1/tokens", map[string]string{ | |||
"name": "test-key-2", | |||
}) | |||
req = AddBasicAuthHeader(req, user.Name) | |||
resp = MakeRequest(t, req, http.StatusCreated) | |||
DecodeJSON(t, resp, &newAccessToken) | |||
return newAccessToken | |||
} | |||
req = NewRequestf(t, "DELETE", "/api/v1/users/user1/tokens/%s", newAccessToken.Name) | |||
// createAPIAccessTokenWithoutCleanUp Delete an API access token and assert that | |||
// deletion succeeded. | |||
func deleteAPIAccessToken(t *testing.T, accessToken api.AccessToken, user *user_model.User) { | |||
req := NewRequestf(t, "DELETE", "/api/v1/users/user1/tokens/%d", accessToken.ID) | |||
req = AddBasicAuthHeader(req, user.Name) | |||
MakeRequest(t, req, http.StatusNoContent) | |||
unittest.AssertNotExistsBean(t, &auth_model.AccessToken{ID: newAccessToken.ID}) | |||
} | |||
// TestAPIDeleteMissingToken ensures that error is thrown when token not found | |||
func TestAPIDeleteMissingToken(t *testing.T) { | |||
defer tests.PrepareTestEnv(t)() | |||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) | |||
req := NewRequestf(t, "DELETE", "/api/v1/users/user1/tokens/%d", unittest.NonexistentID) | |||
req = AddBasicAuthHeader(req, user.Name) | |||
MakeRequest(t, req, http.StatusNotFound) | |||
unittest.AssertNotExistsBean(t, &auth_model.AccessToken{ID: accessToken.ID}) | |||
} |
@@ -46,7 +46,7 @@ func TestAPIAddEmail(t *testing.T) { | |||
normalUsername := "user2" | |||
session := loginUser(t, normalUsername) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeUser) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteUser) | |||
opts := api.CreateEmailOption{ | |||
Emails: []string{"user101@example.com"}, | |||
@@ -83,7 +83,7 @@ func TestAPIDeleteEmail(t *testing.T) { | |||
normalUsername := "user2" | |||
session := loginUser(t, normalUsername) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeUser) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteUser) | |||
opts := api.DeleteEmailOption{ | |||
Emails: []string{"user2-3@example.com"}, |
@@ -22,10 +22,10 @@ func TestAPIFollow(t *testing.T) { | |||
user2 := "user1" | |||
session1 := loginUser(t, user1) | |||
token1 := getTokenForLoggedInUser(t, session1) | |||
token1 := getTokenForLoggedInUser(t, session1, auth_model.AccessTokenScopeReadUser) | |||
session2 := loginUser(t, user2) | |||
token2 := getTokenForLoggedInUser(t, session2, auth_model.AccessTokenScopeUserFollow) | |||
token2 := getTokenForLoggedInUser(t, session2, auth_model.AccessTokenScopeWriteUser) | |||
t.Run("Follow", func(t *testing.T) { | |||
defer tests.PrintCurrentTest(t)() |
@@ -10,6 +10,7 @@ import ( | |||
"time" | |||
activities_model "code.gitea.io/gitea/models/activities" | |||
auth_model "code.gitea.io/gitea/models/auth" | |||
"code.gitea.io/gitea/modules/timeutil" | |||
"code.gitea.io/gitea/tests" | |||
@@ -20,7 +21,7 @@ func TestUserHeatmap(t *testing.T) { | |||
defer tests.PrepareTestEnv(t)() | |||
adminUsername := "user1" | |||
normalUsername := "user2" | |||
token := getUserToken(t, adminUsername) | |||
token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeReadUser) | |||
fakeNow := time.Date(2011, 10, 20, 0, 0, 0, 0, time.Local) | |||
timeutil.Set(fakeNow) |
@@ -8,6 +8,7 @@ import ( | |||
"net/http" | |||
"testing" | |||
auth_model "code.gitea.io/gitea/models/auth" | |||
api "code.gitea.io/gitea/modules/structs" | |||
"code.gitea.io/gitea/tests" | |||
@@ -21,7 +22,7 @@ func TestAPIUserInfo(t *testing.T) { | |||
user2 := "user31" | |||
session := loginUser(t, user) | |||
token := getTokenForLoggedInUser(t, session) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadUser) | |||
t.Run("GetInfo", func(t *testing.T) { | |||
defer tests.PrintCurrentTest(t)() |
@@ -33,7 +33,7 @@ func sampleTest(t *testing.T, auoptc apiUserOrgPermTestCase) { | |||
defer tests.PrepareTestEnv(t)() | |||
session := loginUser(t, auoptc.LoginUser) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadOrg) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadOrganization, auth_model.AccessTokenScopeReadUser) | |||
req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/users/%s/orgs/%s/permissions?token=%s", auoptc.User, auoptc.Organization, token)) | |||
resp := MakeRequest(t, req, http.StatusOK) | |||
@@ -126,7 +126,7 @@ func TestUnknowUser(t *testing.T) { | |||
defer tests.PrepareTestEnv(t)() | |||
session := loginUser(t, "user1") | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadOrg) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadUser, auth_model.AccessTokenScopeReadOrganization) | |||
req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/users/unknow/orgs/org25/permissions?token=%s", token)) | |||
resp := MakeRequest(t, req, http.StatusNotFound) | |||
@@ -140,7 +140,7 @@ func TestUnknowOrganization(t *testing.T) { | |||
defer tests.PrepareTestEnv(t)() | |||
session := loginUser(t, "user1") | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadOrg) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadUser, auth_model.AccessTokenScopeReadOrganization) | |||
req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/users/user1/orgs/unknow/permissions?token=%s", token)) | |||
resp := MakeRequest(t, req, http.StatusNotFound) |
@@ -70,7 +70,7 @@ func TestUserOrgs(t *testing.T) { | |||
func getUserOrgs(t *testing.T, userDoer, userCheck string) (orgs []*api.Organization) { | |||
token := "" | |||
if len(userDoer) != 0 { | |||
token = getUserToken(t, userDoer, auth_model.AccessTokenScopeReadOrg) | |||
token = getUserToken(t, userDoer, auth_model.AccessTokenScopeReadOrganization, auth_model.AccessTokenScopeReadUser) | |||
} | |||
urlStr := fmt.Sprintf("/api/v1/users/%s/orgs?token=%s", userCheck, token) | |||
req := NewRequest(t, "GET", urlStr) | |||
@@ -92,7 +92,7 @@ func TestMyOrgs(t *testing.T) { | |||
MakeRequest(t, req, http.StatusUnauthorized) | |||
normalUsername := "user2" | |||
token := getUserToken(t, normalUsername, auth_model.AccessTokenScopeReadOrg) | |||
token := getUserToken(t, normalUsername, auth_model.AccessTokenScopeReadOrganization, auth_model.AccessTokenScopeReadUser) | |||
req = NewRequest(t, "GET", "/api/v1/user/orgs?token="+token) | |||
resp := MakeRequest(t, req, http.StatusOK) | |||
var orgs []*api.Organization |
@@ -8,6 +8,7 @@ import ( | |||
"net/http" | |||
"testing" | |||
auth_model "code.gitea.io/gitea/models/auth" | |||
"code.gitea.io/gitea/models/unittest" | |||
user_model "code.gitea.io/gitea/models/user" | |||
"code.gitea.io/gitea/modules/setting" | |||
@@ -26,7 +27,7 @@ func TestAPIUserSearchLoggedIn(t *testing.T) { | |||
defer tests.PrepareTestEnv(t)() | |||
adminUsername := "user1" | |||
session := loginUser(t, adminUsername) | |||
token := getTokenForLoggedInUser(t, session) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadUser) | |||
query := "user2" | |||
req := NewRequestf(t, "GET", "/api/v1/users/search?token=%s&q=%s", token, query) | |||
resp := MakeRequest(t, req, http.StatusOK) | |||
@@ -65,7 +66,7 @@ func TestAPIUserSearchAdminLoggedInUserHidden(t *testing.T) { | |||
defer tests.PrepareTestEnv(t)() | |||
adminUsername := "user1" | |||
session := loginUser(t, adminUsername) | |||
token := getTokenForLoggedInUser(t, session) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadUser) | |||
query := "user31" | |||
req := NewRequestf(t, "GET", "/api/v1/users/search?token=%s&q=%s", token, query) | |||
req.SetBasicAuth(token, "x-oauth-basic") |
@@ -22,13 +22,13 @@ func TestAPIStar(t *testing.T) { | |||
repo := "user2/repo1" | |||
session := loginUser(t, user) | |||
token := getTokenForLoggedInUser(t, session) | |||
tokenWithRepoScope := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadUser) | |||
tokenWithUserScope := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteUser, auth_model.AccessTokenScopeWriteRepository) | |||
t.Run("Star", func(t *testing.T) { | |||
defer tests.PrintCurrentTest(t)() | |||
req := NewRequest(t, "PUT", fmt.Sprintf("/api/v1/user/starred/%s?token=%s", repo, tokenWithRepoScope)) | |||
req := NewRequest(t, "PUT", fmt.Sprintf("/api/v1/user/starred/%s?token=%s", repo, tokenWithUserScope)) | |||
MakeRequest(t, req, http.StatusNoContent) | |||
}) | |||
@@ -49,7 +49,7 @@ func TestAPIStar(t *testing.T) { | |||
t.Run("GetMyStarredRepos", func(t *testing.T) { | |||
defer tests.PrintCurrentTest(t)() | |||
req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/starred?token=%s", tokenWithRepoScope)) | |||
req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/starred?token=%s", tokenWithUserScope)) | |||
resp := MakeRequest(t, req, http.StatusOK) | |||
assert.Equal(t, "1", resp.Header().Get("X-Total-Count")) | |||
@@ -63,17 +63,17 @@ func TestAPIStar(t *testing.T) { | |||
t.Run("IsStarring", func(t *testing.T) { | |||
defer tests.PrintCurrentTest(t)() | |||
req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/starred/%s?token=%s", repo, tokenWithRepoScope)) | |||
req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/starred/%s?token=%s", repo, tokenWithUserScope)) | |||
MakeRequest(t, req, http.StatusNoContent) | |||
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/starred/%s?token=%s", repo+"notexisting", tokenWithRepoScope)) | |||
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/starred/%s?token=%s", repo+"notexisting", tokenWithUserScope)) | |||
MakeRequest(t, req, http.StatusNotFound) | |||
}) | |||
t.Run("Unstar", func(t *testing.T) { | |||
defer tests.PrintCurrentTest(t)() | |||
req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/user/starred/%s?token=%s", repo, tokenWithRepoScope)) | |||
req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/user/starred/%s?token=%s", repo, tokenWithUserScope)) | |||
MakeRequest(t, req, http.StatusNoContent) | |||
}) | |||
} |
@@ -22,8 +22,8 @@ func TestAPIWatch(t *testing.T) { | |||
repo := "user2/repo1" | |||
session := loginUser(t, user) | |||
token := getTokenForLoggedInUser(t, session) | |||
tokenWithRepoScope := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadUser) | |||
tokenWithRepoScope := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeReadUser) | |||
t.Run("Watch", func(t *testing.T) { | |||
defer tests.PrintCurrentTest(t)() |
@@ -180,7 +180,7 @@ func TestAPINewWikiPage(t *testing.T) { | |||
defer tests.PrepareTestEnv(t)() | |||
username := "user2" | |||
session := loginUser(t, username) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/wiki/new?token=%s", username, "repo1", token) | |||
@@ -197,7 +197,7 @@ func TestAPIEditWikiPage(t *testing.T) { | |||
defer tests.PrepareTestEnv(t)() | |||
username := "user2" | |||
session := loginUser(t, username) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/wiki/page/Page-With-Spaced-Name?token=%s", username, "repo1", token) | |||
@@ -51,7 +51,7 @@ func TestDumpRestore(t *testing.T) { | |||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: reponame}) | |||
repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | |||
session := loginUser(t, repoOwner.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeReadMisc) | |||
// | |||
// Phase 1: dump repo1 from the Gitea instance to the filesystem |
@@ -117,7 +117,7 @@ func TestEmptyRepoAddFileByAPI(t *testing.T) { | |||
assert.NoError(t, err) | |||
session := loginUser(t, "user30") | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
url := fmt.Sprintf("/api/v1/repos/user30/empty/contents/new-file.txt?token=%s", token) | |||
req := NewRequestWithJSON(t, "POST", url, &api.CreateFileOptions{ |
@@ -60,7 +60,7 @@ func TestEventSourceManagerRun(t *testing.T) { | |||
thread5 := unittest.AssertExistsAndLoadBean(t, &activities_model.Notification{ID: 5}) | |||
assert.NoError(t, thread5.LoadAttributes(db.DefaultContext)) | |||
session := loginUser(t, user2.Name) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeNotification) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteNotification, auth_model.AccessTokenScopeWriteRepository) | |||
var apiNL []api.NotificationThread | |||
@@ -44,11 +44,11 @@ func TestGit(t *testing.T) { | |||
func testGit(t *testing.T, u *url.URL) { | |||
username := "user2" | |||
baseAPITestContext := NewAPITestContext(t, username, "repo1", auth_model.AccessTokenScopeRepo, auth_model.AccessTokenScopeWritePublicKey, auth_model.AccessTokenScopeDeleteRepo) | |||
baseAPITestContext := NewAPITestContext(t, username, "repo1", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) | |||
u.Path = baseAPITestContext.GitPath() | |||
forkedUserCtx := NewAPITestContext(t, "user4", "repo1", auth_model.AccessTokenScopeRepo) | |||
forkedUserCtx := NewAPITestContext(t, "user4", "repo1", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) | |||
t.Run("HTTP", func(t *testing.T) { | |||
defer tests.PrintCurrentTest(t)() | |||
@@ -359,7 +359,7 @@ func doBranchProtectPRMerge(baseCtx *APITestContext, dstPath string) func(t *tes | |||
t.Run("CreateBranchProtected", doGitCreateBranch(dstPath, "protected")) | |||
t.Run("PushProtectedBranch", doGitPushTestRepository(dstPath, "origin", "protected")) | |||
ctx := NewAPITestContext(t, baseCtx.Username, baseCtx.Reponame, auth_model.AccessTokenScopeRepo) | |||
ctx := NewAPITestContext(t, baseCtx.Username, baseCtx.Reponame, auth_model.AccessTokenScopeWriteRepository) | |||
t.Run("ProtectProtectedBranchNoWhitelist", doProtectBranch(ctx, "protected", "", "")) | |||
t.Run("GenerateCommit", func(t *testing.T) { | |||
_, err := generateCommitWithNewData(littleSize, dstPath, "user2@example.com", "User Two", "branch-data-file-") | |||
@@ -603,7 +603,7 @@ func doAutoPRMerge(baseCtx *APITestContext, dstPath string) func(t *testing.T) { | |||
return func(t *testing.T) { | |||
defer tests.PrintCurrentTest(t)() | |||
ctx := NewAPITestContext(t, baseCtx.Username, baseCtx.Reponame, auth_model.AccessTokenScopeRepo) | |||
ctx := NewAPITestContext(t, baseCtx.Username, baseCtx.Reponame, auth_model.AccessTokenScopeWriteRepository) | |||
t.Run("CheckoutProtected", doGitCheckoutBranch(dstPath, "protected")) | |||
t.Run("PullProtected", doGitPull(dstPath, "origin", "protected")) |
@@ -70,7 +70,7 @@ func TestGPGGit(t *testing.T) { | |||
t.Run("Unsigned-Initial", func(t *testing.T) { | |||
defer tests.PrintCurrentTest(t)() | |||
testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeRepo) | |||
testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) | |||
t.Run("CreateRepository", doAPICreateRepository(testCtx, false)) | |||
t.Run("CheckMasterBranchUnsigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) { | |||
assert.NotNil(t, branch.Commit) | |||
@@ -94,7 +94,7 @@ func TestGPGGit(t *testing.T) { | |||
t.Run("Unsigned-Initial-CRUD-ParentSigned", func(t *testing.T) { | |||
defer tests.PrintCurrentTest(t)() | |||
testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeRepo) | |||
testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) | |||
t.Run("CreateCRUDFile-ParentSigned", crudActionCreateFile( | |||
t, testCtx, user, "master", "parentsigned", "signed-parent.txt", func(t *testing.T, response api.FileResponse) { | |||
assert.False(t, response.Verification.Verified) | |||
@@ -111,7 +111,7 @@ func TestGPGGit(t *testing.T) { | |||
t.Run("Unsigned-Initial-CRUD-Never", func(t *testing.T) { | |||
defer tests.PrintCurrentTest(t)() | |||
testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeRepo) | |||
testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) | |||
t.Run("CreateCRUDFile-Never", crudActionCreateFile( | |||
t, testCtx, user, "parentsigned", "parentsigned-never", "unsigned-never2.txt", func(t *testing.T, response api.FileResponse) { | |||
assert.False(t, response.Verification.Verified) | |||
@@ -124,7 +124,7 @@ func TestGPGGit(t *testing.T) { | |||
t.Run("Unsigned-Initial-CRUD-Always", func(t *testing.T) { | |||
defer tests.PrintCurrentTest(t)() | |||
testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeRepo) | |||
testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) | |||
t.Run("CreateCRUDFile-Always", crudActionCreateFile( | |||
t, testCtx, user, "master", "always", "signed-always.txt", func(t *testing.T, response api.FileResponse) { | |||
assert.NotNil(t, response.Verification) | |||
@@ -161,7 +161,7 @@ func TestGPGGit(t *testing.T) { | |||
t.Run("Unsigned-Initial-CRUD-ParentSigned", func(t *testing.T) { | |||
defer tests.PrintCurrentTest(t)() | |||
testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeRepo) | |||
testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) | |||
t.Run("CreateCRUDFile-Always-ParentSigned", crudActionCreateFile( | |||
t, testCtx, user, "always", "always-parentsigned", "signed-always-parentsigned.txt", func(t *testing.T, response api.FileResponse) { | |||
assert.NotNil(t, response.Verification) | |||
@@ -184,7 +184,7 @@ func TestGPGGit(t *testing.T) { | |||
t.Run("AlwaysSign-Initial", func(t *testing.T) { | |||
defer tests.PrintCurrentTest(t)() | |||
testCtx := NewAPITestContext(t, username, "initial-always", auth_model.AccessTokenScopeRepo) | |||
testCtx := NewAPITestContext(t, username, "initial-always", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) | |||
t.Run("CreateRepository", doAPICreateRepository(testCtx, false)) | |||
t.Run("CheckMasterBranchSigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) { | |||
assert.NotNil(t, branch.Commit) | |||
@@ -212,7 +212,7 @@ func TestGPGGit(t *testing.T) { | |||
t.Run("AlwaysSign-Initial-CRUD-Never", func(t *testing.T) { | |||
defer tests.PrintCurrentTest(t)() | |||
testCtx := NewAPITestContext(t, username, "initial-always-never", auth_model.AccessTokenScopeRepo) | |||
testCtx := NewAPITestContext(t, username, "initial-always-never", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) | |||
t.Run("CreateRepository", doAPICreateRepository(testCtx, false)) | |||
t.Run("CreateCRUDFile-Never", crudActionCreateFile( | |||
t, testCtx, user, "master", "never", "unsigned-never.txt", func(t *testing.T, response api.FileResponse) { | |||
@@ -225,7 +225,7 @@ func TestGPGGit(t *testing.T) { | |||
u.Path = baseAPITestContext.GitPath() | |||
t.Run("AlwaysSign-Initial-CRUD-ParentSigned-On-Always", func(t *testing.T) { | |||
defer tests.PrintCurrentTest(t)() | |||
testCtx := NewAPITestContext(t, username, "initial-always-parent", auth_model.AccessTokenScopeRepo) | |||
testCtx := NewAPITestContext(t, username, "initial-always-parent", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) | |||
t.Run("CreateRepository", doAPICreateRepository(testCtx, false)) | |||
t.Run("CreateCRUDFile-ParentSigned", crudActionCreateFile( | |||
t, testCtx, user, "master", "parentsigned", "signed-parent.txt", func(t *testing.T, response api.FileResponse) { | |||
@@ -244,7 +244,7 @@ func TestGPGGit(t *testing.T) { | |||
t.Run("AlwaysSign-Initial-CRUD-Always", func(t *testing.T) { | |||
defer tests.PrintCurrentTest(t)() | |||
testCtx := NewAPITestContext(t, username, "initial-always-always", auth_model.AccessTokenScopeRepo) | |||
testCtx := NewAPITestContext(t, username, "initial-always-always", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) | |||
t.Run("CreateRepository", doAPICreateRepository(testCtx, false)) | |||
t.Run("CreateCRUDFile-Always", crudActionCreateFile( | |||
t, testCtx, user, "master", "always", "signed-always.txt", func(t *testing.T, response api.FileResponse) { | |||
@@ -264,7 +264,7 @@ func TestGPGGit(t *testing.T) { | |||
t.Run("UnsignedMerging", func(t *testing.T) { | |||
defer tests.PrintCurrentTest(t)() | |||
testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeRepo) | |||
testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) | |||
var err error | |||
t.Run("CreatePullRequest", func(t *testing.T) { | |||
pr, err = doAPICreatePullRequest(testCtx, testCtx.Username, testCtx.Reponame, "master", "never2")(t) | |||
@@ -285,7 +285,7 @@ func TestGPGGit(t *testing.T) { | |||
t.Run("BaseSignedMerging", func(t *testing.T) { | |||
defer tests.PrintCurrentTest(t)() | |||
testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeRepo) | |||
testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) | |||
var err error | |||
t.Run("CreatePullRequest", func(t *testing.T) { | |||
pr, err = doAPICreatePullRequest(testCtx, testCtx.Username, testCtx.Reponame, "master", "parentsigned2")(t) | |||
@@ -306,7 +306,7 @@ func TestGPGGit(t *testing.T) { | |||
t.Run("CommitsSignedMerging", func(t *testing.T) { | |||
defer tests.PrintCurrentTest(t)() | |||
testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeRepo) | |||
testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) | |||
var err error | |||
t.Run("CreatePullRequest", func(t *testing.T) { | |||
pr, err = doAPICreatePullRequest(testCtx, testCtx.Username, testCtx.Reponame, "master", "always-parentsigned")(t) |
@@ -41,13 +41,13 @@ func storeObjectInRepo(t *testing.T, repositoryID int64, content *[]byte) string | |||
return pointer.Oid | |||
} | |||
func storeAndGetLfsToken(t *testing.T, ts auth.AccessTokenScope, content *[]byte, extraHeader *http.Header, expectedStatus int) *httptest.ResponseRecorder { | |||
func storeAndGetLfsToken(t *testing.T, content *[]byte, extraHeader *http.Header, expectedStatus int, ts ...auth.AccessTokenScope) *httptest.ResponseRecorder { | |||
repo, err := repo_model.GetRepositoryByOwnerAndName(db.DefaultContext, "user2", "repo1") | |||
assert.NoError(t, err) | |||
oid := storeObjectInRepo(t, repo.ID, content) | |||
defer git_model.RemoveLFSMetaObjectByOid(db.DefaultContext, repo.ID, oid) | |||
token := getUserToken(t, "user2", ts) | |||
token := getUserToken(t, "user2", ts...) | |||
// Request OID | |||
req := NewRequest(t, "GET", "/user2/repo1.git/info/lfs/objects/"+oid+"/test") | |||
@@ -119,7 +119,7 @@ func TestGetLFSSmallToken(t *testing.T) { | |||
defer tests.PrepareTestEnv(t)() | |||
content := []byte("A very small file\n") | |||
resp := storeAndGetLfsToken(t, auth.AccessTokenScopePublicRepo, &content, nil, http.StatusOK) | |||
resp := storeAndGetLfsToken(t, &content, nil, http.StatusOK, auth.AccessTokenScopePublicOnly, auth.AccessTokenScopeReadRepository) | |||
checkResponseTestContentEncoding(t, &content, resp, false) | |||
} | |||
@@ -127,7 +127,7 @@ func TestGetLFSSmallTokenFail(t *testing.T) { | |||
defer tests.PrepareTestEnv(t)() | |||
content := []byte("A very small file\n") | |||
storeAndGetLfsToken(t, auth.AccessTokenScopeNotification, &content, nil, http.StatusForbidden) | |||
storeAndGetLfsToken(t, &content, nil, http.StatusForbidden, auth.AccessTokenScopeReadNotification) | |||
} | |||
func TestGetLFSLarge(t *testing.T) { |
@@ -67,7 +67,7 @@ func TestMigrateGiteaForm(t *testing.T) { | |||
repoName := "repo1" | |||
repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: ownerName}) | |||
session := loginUser(t, ownerName) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeReadMisc) | |||
// Step 0: verify the repo is available | |||
req := NewRequestf(t, "GET", fmt.Sprintf("/%s/%s", ownerName, repoName)) |
@@ -25,7 +25,7 @@ func testOrgCounts(t *testing.T, u *url.URL) { | |||
orgOwner := "user2" | |||
orgName := "testOrg" | |||
orgCollaborator := "user4" | |||
ctx := NewAPITestContext(t, orgOwner, "repo1", auth_model.AccessTokenScopeAdminOrg) | |||
ctx := NewAPITestContext(t, orgOwner, "repo1", auth_model.AccessTokenScopeWriteOrganization) | |||
var ownerCountRepos map[string]int | |||
var collabCountRepos map[string]int |
@@ -159,7 +159,7 @@ func TestOrgRestrictedUser(t *testing.T) { | |||
// Therefore create a read-only team | |||
adminSession := loginUser(t, "user1") | |||
token := getTokenForLoggedInUser(t, adminSession, auth_model.AccessTokenScopeAdminOrg) | |||
token := getTokenForLoggedInUser(t, adminSession, auth_model.AccessTokenScopeWriteOrganization) | |||
teamToCreate := &api.CreateTeamOption{ | |||
Name: "codereader", |
@@ -34,7 +34,7 @@ func testPrivateActivityDoSomethingForActionEntries(t *testing.T) { | |||
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repoBefore.OwnerID}) | |||
session := loginUser(t, privateActivityTestUser) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue) | |||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues?state=all&token=%s", owner.Name, repoBefore.Name, token) | |||
req := NewRequestWithJSON(t, "POST", urlStr, &api.CreateIssueOption{ | |||
Body: "test", | |||
@@ -125,7 +125,7 @@ func testPrivateActivityHelperHasHeatmapContentFromPublic(t *testing.T) bool { | |||
} | |||
func testPrivateActivityHelperHasHeatmapContentFromSession(t *testing.T, session *TestSession) bool { | |||
token := getTokenForLoggedInUser(t, session) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadUser) | |||
req := NewRequestf(t, "GET", "/api/v1/users/%s/heatmap?token=%s", privateActivityTestUser, token) | |||
resp := session.MakeRequest(t, req, http.StatusOK) |
@@ -218,7 +218,7 @@ func TestCantMergeConflict(t *testing.T) { | |||
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "base", "README.md", "Hello, World (Edited Twice)\n") | |||
// Use API to create a conflicting pr | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls?token=%s", "user1", "repo1", token), &api.CreatePullRequestOption{ | |||
Head: "conflict", | |||
Base: "base", | |||
@@ -326,7 +326,7 @@ func TestCantMergeUnrelated(t *testing.T) { | |||
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "conflict", "README.md", "Hello, World (Edited Once)\n") | |||
// Use API to create a conflicting pr | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls?token=%s", "user1", "repo1", token), &api.CreatePullRequestOption{ | |||
Head: "unrelated", | |||
Base: "base", |
@@ -64,7 +64,7 @@ func TestPullCreate_CommitStatus(t *testing.T) { | |||
api.CommitStatusWarning: "gitea-exclamation", | |||
} | |||
testCtx := NewAPITestContext(t, "user1", "repo1", auth_model.AccessTokenScopeRepo) | |||
testCtx := NewAPITestContext(t, "user1", "repo1", auth_model.AccessTokenScopeWriteRepository) | |||
// Update commit status, and check if icon is updated as well | |||
for _, status := range statusList { |
@@ -39,7 +39,7 @@ func TestAPIPullUpdate(t *testing.T) { | |||
assert.NoError(t, pr.LoadIssue(db.DefaultContext)) | |||
session := loginUser(t, "user2") | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
req := NewRequestf(t, "POST", "/api/v1/repos/%s/%s/pulls/%d/update?token="+token, pr.BaseRepo.OwnerName, pr.BaseRepo.Name, pr.Issue.Index) | |||
session.MakeRequest(t, req, http.StatusOK) | |||
@@ -67,7 +67,7 @@ func TestAPIPullUpdateByRebase(t *testing.T) { | |||
assert.NoError(t, pr.LoadIssue(db.DefaultContext)) | |||
session := loginUser(t, "user2") | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) | |||
req := NewRequestf(t, "POST", "/api/v1/repos/%s/%s/pulls/%d/update?style=rebase&token="+token, pr.BaseRepo.OwnerName, pr.BaseRepo.Name, pr.Issue.Index) | |||
session.MakeRequest(t, req, http.StatusOK) | |||
@@ -52,7 +52,7 @@ func doTestRepoCommitWithStatus(t *testing.T, state string, classes ...string) { | |||
assert.NotEmpty(t, commitURL) | |||
// Call API to add status for commit | |||
ctx := NewAPITestContext(t, "user2", "repo1", auth_model.AccessTokenScopeRepo) | |||
ctx := NewAPITestContext(t, "user2", "repo1", auth_model.AccessTokenScopeWriteRepository) | |||
t.Run("CreateStatus", doAPICreateCommitStatus(ctx, path.Base(commitURL), api.CreateStatusOption{ | |||
State: api.CommitStatusState(state), | |||
TargetURL: "http://test.ci/", | |||
@@ -157,7 +157,7 @@ func TestRepoCommitsStatusParallel(t *testing.T) { | |||
wg.Add(1) | |||
go func(parentT *testing.T, i int) { | |||
parentT.Run(fmt.Sprintf("ParallelCreateStatus_%d", i), func(t *testing.T) { | |||
ctx := NewAPITestContext(t, "user2", "repo1", auth_model.AccessTokenScopeRepoStatus) | |||
ctx := NewAPITestContext(t, "user2", "repo1", auth_model.AccessTokenScopeWriteRepository) | |||
runBody := doAPICreateCommitStatus(ctx, path.Base(commitURL), api.CreateStatusOption{ | |||
State: api.CommitStatusPending, | |||
TargetURL: "http://test.ci/", | |||
@@ -188,7 +188,7 @@ func TestRepoCommitsStatusMultiple(t *testing.T) { | |||
assert.NotEmpty(t, commitURL) | |||
// Call API to add status for commit | |||
ctx := NewAPITestContext(t, "user2", "repo1", auth_model.AccessTokenScopeRepo) | |||
ctx := NewAPITestContext(t, "user2", "repo1", auth_model.AccessTokenScopeWriteRepository) | |||
t.Run("CreateStatus", doAPICreateCommitStatus(ctx, path.Base(commitURL), api.CreateStatusOption{ | |||
State: api.CommitStatusSuccess, | |||
TargetURL: "http://test.ci/", |
@@ -48,8 +48,8 @@ func TestPushDeployKeyOnEmptyRepo(t *testing.T) { | |||
func testPushDeployKeyOnEmptyRepo(t *testing.T, u *url.URL) { | |||
// OK login | |||
ctx := NewAPITestContext(t, "user2", "deploy-key-empty-repo-1", auth_model.AccessTokenScopeRepo) | |||
ctxWithDeleteRepo := NewAPITestContext(t, "user2", "deploy-key-empty-repo-1", auth_model.AccessTokenScopeRepo, auth_model.AccessTokenScopeDeleteRepo) | |||
ctx := NewAPITestContext(t, "user2", "deploy-key-empty-repo-1", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) | |||
ctxWithDeleteRepo := NewAPITestContext(t, "user2", "deploy-key-empty-repo-1", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) | |||
keyname := fmt.Sprintf("%s-push", ctx.Reponame) | |||
u.Path = ctx.GitPath() | |||
@@ -92,8 +92,8 @@ func testKeyOnlyOneType(t *testing.T, u *url.URL) { | |||
keyname := fmt.Sprintf("%s-push", reponame) | |||
// OK login | |||
ctx := NewAPITestContext(t, username, reponame, auth_model.AccessTokenScopeRepo, auth_model.AccessTokenScopeAdminPublicKey) | |||
ctxWithDeleteRepo := NewAPITestContext(t, username, reponame, auth_model.AccessTokenScopeRepo, auth_model.AccessTokenScopeAdminPublicKey, auth_model.AccessTokenScopeDeleteRepo) | |||
ctx := NewAPITestContext(t, username, reponame, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) | |||
ctxWithDeleteRepo := NewAPITestContext(t, username, reponame, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) | |||
otherCtx := ctx | |||
otherCtx.Reponame = "ssh-key-test-repo-2" |
@@ -166,7 +166,7 @@ Note: This user hasn't uploaded any GPG keys. | |||
// Import key | |||
// User1 <user1@example.com> | |||
session := loginUser(t, "user1") | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteGPGKey) | |||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteUser) | |||
testCreateGPGKey(t, session.MakeRequest, token, http.StatusCreated, `-----BEGIN PGP PUBLIC KEY BLOCK----- | |||
mQENBFyy/VUBCADJ7zbM20Z1RWmFoVgp5WkQfI2rU1Vj9cQHes9i42wVLLtcbPeo |