mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-29 10:57:44 +09:00 
			
		
		
		
	Support scoped access tokens (#20908)
This PR adds the support for scopes of access tokens, mimicking the design of GitHub OAuth scopes. The changes of the core logic are in `models/auth` that `AccessToken` struct will have a `Scope` field. The normalized (no duplication of scope), comma-separated scope string will be stored in `access_token` table in the database. In `services/auth`, the scope will be stored in context, which will be used by `reqToken` middleware in API calls. Only OAuth2 tokens will have granular token scopes, while others like BasicAuth will default to scope `all`. A large amount of work happens in `routers/api/v1/api.go` and the corresponding `tests/integration` tests, that is adding necessary scopes to each of the API calls as they fit. - [x] Add `Scope` field to `AccessToken` - [x] Add access control to all API endpoints - [x] Update frontend & backend for when creating tokens - [x] Add a database migration for `scope` column (enable 'all' access to past tokens) I'm aiming to complete it before Gitea 1.19 release. Fixes #4300
This commit is contained in:
		| @@ -42,7 +42,41 @@ To use the Authorization Code Grant as a third party application it is required | |||||||
|  |  | ||||||
| ## Scopes | ## Scopes | ||||||
|  |  | ||||||
| Currently Gitea does not support scopes (see [#4300](https://github.com/go-gitea/gitea/issues/4300)) and all third party applications will be granted access to all resources of the user and their organizations. | 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 | | ||||||
|  | | **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. | | ||||||
|  |  | ||||||
| ## Client types | ## Client types | ||||||
|  |  | ||||||
|   | |||||||
| @@ -65,6 +65,7 @@ type AccessToken struct { | |||||||
| 	TokenHash      string `xorm:"UNIQUE"` // sha256 of token | 	TokenHash      string `xorm:"UNIQUE"` // sha256 of token | ||||||
| 	TokenSalt      string | 	TokenSalt      string | ||||||
| 	TokenLastEight string `xorm:"INDEX token_last_eight"` | 	TokenLastEight string `xorm:"INDEX token_last_eight"` | ||||||
|  | 	Scope          AccessTokenScope | ||||||
|  |  | ||||||
| 	CreatedUnix       timeutil.TimeStamp `xorm:"INDEX created"` | 	CreatedUnix       timeutil.TimeStamp `xorm:"INDEX created"` | ||||||
| 	UpdatedUnix       timeutil.TimeStamp `xorm:"INDEX updated"` | 	UpdatedUnix       timeutil.TimeStamp `xorm:"INDEX updated"` | ||||||
|   | |||||||
							
								
								
									
										251
									
								
								models/auth/token_scope.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										251
									
								
								models/auth/token_scope.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,251 @@ | |||||||
|  | // Copyright 2022 The Gitea Authors. All rights reserved. | ||||||
|  | // SPDX-License-Identifier: MIT | ||||||
|  |  | ||||||
|  | package auth | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // AccessTokenScope represents the scope for an access token. | ||||||
|  | type AccessTokenScope string | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	AccessTokenScopeAll AccessTokenScope = "all" | ||||||
|  |  | ||||||
|  | 	AccessTokenScopeRepo       AccessTokenScope = "repo" | ||||||
|  | 	AccessTokenScopeRepoStatus AccessTokenScope = "repo:status" | ||||||
|  | 	AccessTokenScopePublicRepo AccessTokenScope = "public_repo" | ||||||
|  |  | ||||||
|  | 	AccessTokenScopeAdminOrg AccessTokenScope = "admin:org" | ||||||
|  | 	AccessTokenScopeWriteOrg AccessTokenScope = "write:org" | ||||||
|  | 	AccessTokenScopeReadOrg  AccessTokenScope = "read:org" | ||||||
|  |  | ||||||
|  | 	AccessTokenScopeAdminPublicKey AccessTokenScope = "admin:public_key" | ||||||
|  | 	AccessTokenScopeWritePublicKey AccessTokenScope = "write:public_key" | ||||||
|  | 	AccessTokenScopeReadPublicKey  AccessTokenScope = "read:public_key" | ||||||
|  |  | ||||||
|  | 	AccessTokenScopeAdminRepoHook AccessTokenScope = "admin:repo_hook" | ||||||
|  | 	AccessTokenScopeWriteRepoHook AccessTokenScope = "write:repo_hook" | ||||||
|  | 	AccessTokenScopeReadRepoHook  AccessTokenScope = "read:repo_hook" | ||||||
|  |  | ||||||
|  | 	AccessTokenScopeAdminOrgHook AccessTokenScope = "admin:org_hook" | ||||||
|  |  | ||||||
|  | 	AccessTokenScopeNotification AccessTokenScope = "notification" | ||||||
|  |  | ||||||
|  | 	AccessTokenScopeUser       AccessTokenScope = "user" | ||||||
|  | 	AccessTokenScopeReadUser   AccessTokenScope = "read:user" | ||||||
|  | 	AccessTokenScopeUserEmail  AccessTokenScope = "user:email" | ||||||
|  | 	AccessTokenScopeUserFollow AccessTokenScope = "user:follow" | ||||||
|  |  | ||||||
|  | 	AccessTokenScopeDeleteRepo AccessTokenScope = "delete_repo" | ||||||
|  |  | ||||||
|  | 	AccessTokenScopePackage       AccessTokenScope = "package" | ||||||
|  | 	AccessTokenScopeWritePackage  AccessTokenScope = "write:package" | ||||||
|  | 	AccessTokenScopeReadPackage   AccessTokenScope = "read:package" | ||||||
|  | 	AccessTokenScopeDeletePackage AccessTokenScope = "delete:package" | ||||||
|  |  | ||||||
|  | 	AccessTokenScopeAdminGPGKey AccessTokenScope = "admin:gpg_key" | ||||||
|  | 	AccessTokenScopeWriteGPGKey AccessTokenScope = "write:gpg_key" | ||||||
|  | 	AccessTokenScopeReadGPGKey  AccessTokenScope = "read:gpg_key" | ||||||
|  |  | ||||||
|  | 	AccessTokenScopeAdminApplication AccessTokenScope = "admin:application" | ||||||
|  | 	AccessTokenScopeWriteApplication AccessTokenScope = "write:application" | ||||||
|  | 	AccessTokenScopeReadApplication  AccessTokenScope = "read:application" | ||||||
|  |  | ||||||
|  | 	AccessTokenScopeSudo AccessTokenScope = "sudo" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // 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 | | ||||||
|  | 		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 | ||||||
|  |  | ||||||
|  | 	AccessTokenScopeAdminRepoHookBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeWriteRepoHookBits | ||||||
|  | 	AccessTokenScopeWriteRepoHookBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeReadRepoHookBits | ||||||
|  | 	AccessTokenScopeReadRepoHookBits  AccessTokenScopeBitmap = 1 << iota | ||||||
|  |  | ||||||
|  | 	AccessTokenScopeAdminOrgHookBits AccessTokenScopeBitmap = 1 << iota | ||||||
|  |  | ||||||
|  | 	AccessTokenScopeNotificationBits AccessTokenScopeBitmap = 1 << iota | ||||||
|  |  | ||||||
|  | 	AccessTokenScopeUserBits       AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeReadUserBits | AccessTokenScopeUserEmailBits | AccessTokenScopeUserFollowBits | ||||||
|  | 	AccessTokenScopeReadUserBits   AccessTokenScopeBitmap = 1 << iota | ||||||
|  | 	AccessTokenScopeUserEmailBits  AccessTokenScopeBitmap = 1 << iota | ||||||
|  | 	AccessTokenScopeUserFollowBits AccessTokenScopeBitmap = 1 << iota | ||||||
|  |  | ||||||
|  | 	AccessTokenScopeDeleteRepoBits AccessTokenScopeBitmap = 1 << iota | ||||||
|  |  | ||||||
|  | 	AccessTokenScopePackageBits       AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeWritePackageBits | AccessTokenScopeDeletePackageBits | ||||||
|  | 	AccessTokenScopeWritePackageBits  AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeReadPackageBits | ||||||
|  | 	AccessTokenScopeReadPackageBits   AccessTokenScopeBitmap = 1 << iota | ||||||
|  | 	AccessTokenScopeDeletePackageBits AccessTokenScopeBitmap = 1 << iota | ||||||
|  |  | ||||||
|  | 	AccessTokenScopeAdminGPGKeyBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeWriteGPGKeyBits | ||||||
|  | 	AccessTokenScopeWriteGPGKeyBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeReadGPGKeyBits | ||||||
|  | 	AccessTokenScopeReadGPGKeyBits  AccessTokenScopeBitmap = 1 << iota | ||||||
|  |  | ||||||
|  | 	AccessTokenScopeAdminApplicationBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeWriteApplicationBits | ||||||
|  | 	AccessTokenScopeWriteApplicationBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeReadApplicationBits | ||||||
|  | 	AccessTokenScopeReadApplicationBits  AccessTokenScopeBitmap = 1 << iota | ||||||
|  |  | ||||||
|  | 	AccessTokenScopeSudoBits AccessTokenScopeBitmap = 1 << iota | ||||||
|  |  | ||||||
|  | 	// 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 precedes child scopes. | ||||||
|  | var allAccessTokenScopes = []AccessTokenScope{ | ||||||
|  | 	AccessTokenScopeRepo, AccessTokenScopeRepoStatus, AccessTokenScopePublicRepo, | ||||||
|  | 	AccessTokenScopeAdminOrg, AccessTokenScopeWriteOrg, AccessTokenScopeReadOrg, | ||||||
|  | 	AccessTokenScopeAdminPublicKey, AccessTokenScopeWritePublicKey, AccessTokenScopeReadPublicKey, | ||||||
|  | 	AccessTokenScopeAdminRepoHook, AccessTokenScopeWriteRepoHook, AccessTokenScopeReadRepoHook, | ||||||
|  | 	AccessTokenScopeAdminOrgHook, | ||||||
|  | 	AccessTokenScopeNotification, | ||||||
|  | 	AccessTokenScopeUser, AccessTokenScopeReadUser, AccessTokenScopeUserEmail, AccessTokenScopeUserFollow, | ||||||
|  | 	AccessTokenScopeDeleteRepo, | ||||||
|  | 	AccessTokenScopePackage, AccessTokenScopeWritePackage, AccessTokenScopeReadPackage, AccessTokenScopeDeletePackage, | ||||||
|  | 	AccessTokenScopeAdminGPGKey, AccessTokenScopeWriteGPGKey, AccessTokenScopeReadGPGKey, | ||||||
|  | 	AccessTokenScopeAdminApplication, AccessTokenScopeWriteApplication, AccessTokenScopeReadApplication, | ||||||
|  | 	AccessTokenScopeSudo, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 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, | ||||||
|  | 	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, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Parse parses the scope string into a bitmap, thus removing possible duplicates. | ||||||
|  | func (s AccessTokenScope) Parse() (AccessTokenScopeBitmap, error) { | ||||||
|  | 	list := strings.Split(string(s), ",") | ||||||
|  |  | ||||||
|  | 	var bitmap AccessTokenScopeBitmap | ||||||
|  | 	for _, v := range list { | ||||||
|  | 		singleScope := AccessTokenScope(v) | ||||||
|  | 		if singleScope == "" { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		if singleScope == AccessTokenScopeAll { | ||||||
|  | 			bitmap |= AccessTokenScopeAllBits | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		bits, ok := allAccessTokenScopeBits[singleScope] | ||||||
|  | 		if !ok { | ||||||
|  | 			return 0, fmt.Errorf("invalid access token scope: %s", singleScope) | ||||||
|  | 		} | ||||||
|  | 		bitmap |= bits | ||||||
|  | 	} | ||||||
|  | 	return bitmap, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Normalize returns a normalized scope string without any duplicates. | ||||||
|  | func (s AccessTokenScope) Normalize() (AccessTokenScope, error) { | ||||||
|  | 	bitmap, err := s.Parse() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	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() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return false, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return bitmap.HasScope(scope) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 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() 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 | ||||||
|  |  | ||||||
|  | 	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), | ||||||
|  | 		"repo,admin:org,admin:public_key,admin:org_hook,notification,user,delete_repo,package,admin:gpg_key,admin:application", | ||||||
|  | 		"all", | ||||||
|  | 	)) | ||||||
|  | 	return scope | ||||||
|  | } | ||||||
							
								
								
									
										84
									
								
								models/auth/token_scope_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								models/auth/token_scope_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,84 @@ | |||||||
|  | // Copyright 2022 The Gitea Authors. All rights reserved. | ||||||
|  | // SPDX-License-Identifier: MIT | ||||||
|  |  | ||||||
|  | package auth | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"github.com/stretchr/testify/assert" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestAccessTokenScope_Normalize(t *testing.T) { | ||||||
|  | 	tests := []struct { | ||||||
|  | 		in  AccessTokenScope | ||||||
|  | 		out AccessTokenScope | ||||||
|  | 		err error | ||||||
|  | 	}{ | ||||||
|  | 		{"", "", 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}, | ||||||
|  | 		{"all", "all", nil}, | ||||||
|  | 		{"repo,admin:org,admin:public_key,admin:repo_hook,admin:org_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,notification,user,delete_repo,package,admin:gpg_key,admin:application,sudo", "all,sudo", nil}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, test := range tests { | ||||||
|  | 		t.Run(string(test.in), func(t *testing.T) { | ||||||
|  | 			scope, err := test.in.Normalize() | ||||||
|  | 			assert.Equal(t, test.out, scope) | ||||||
|  | 			assert.Equal(t, test.err, err) | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | 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}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	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) | ||||||
|  | 			assert.Equal(t, test.err, err) | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -451,6 +451,8 @@ var migrations = []Migration{ | |||||||
| 	NewMigration("Drop ForeignReference table", v1_19.DropForeignReferenceTable), | 	NewMigration("Drop ForeignReference table", v1_19.DropForeignReferenceTable), | ||||||
| 	// v238 -> v239 | 	// v238 -> v239 | ||||||
| 	NewMigration("Add updated unix to LFSMetaObject", v1_19.AddUpdatedUnixToLFSMetaObject), | 	NewMigration("Add updated unix to LFSMetaObject", v1_19.AddUpdatedUnixToLFSMetaObject), | ||||||
|  | 	// v239 -> v240 | ||||||
|  | 	NewMigration("Add scope for access_token", v1_19.AddScopeForAccessTokens), | ||||||
| } | } | ||||||
|  |  | ||||||
| // GetCurrentDBVersion returns the current db version | // GetCurrentDBVersion returns the current db version | ||||||
|   | |||||||
							
								
								
									
										22
									
								
								models/migrations/v1_19/v239.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								models/migrations/v1_19/v239.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | |||||||
|  | // Copyright 2022 The Gitea Authors. All rights reserved. | ||||||
|  | // SPDX-License-Identifier: MIT | ||||||
|  |  | ||||||
|  | package v1_19 //nolint | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"xorm.io/xorm" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func AddScopeForAccessTokens(x *xorm.Engine) error { | ||||||
|  | 	type AccessToken struct { | ||||||
|  | 		Scope string | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err := x.Sync(new(AccessToken)); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// all previous tokens have `all` and `sudo` scopes | ||||||
|  | 	_, err := x.Exec("UPDATE access_token SET scope = ? WHERE scope IS NULL OR scope = ''", "all,sudo") | ||||||
|  | 	return err | ||||||
|  | } | ||||||
| @@ -747,6 +747,7 @@ access_token_deletion_cancel_action = Cancel | |||||||
| access_token_deletion_confirm_action = Delete | 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? | 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. | delete_token_success = The token has been deleted. Applications using it no longer have access to your account. | ||||||
|  | select_scopes = Select scopes | ||||||
|  |  | ||||||
| manage_oauth2_applications = Manage OAuth2 Applications | manage_oauth2_applications = Manage OAuth2 Applications | ||||||
| edit_oauth2_application = Edit OAuth2 Application | edit_oauth2_application = Edit OAuth2 Application | ||||||
|   | |||||||
| @@ -69,6 +69,7 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/organization" | 	"code.gitea.io/gitea/models/organization" | ||||||
| 	"code.gitea.io/gitea/models/perm" | 	"code.gitea.io/gitea/models/perm" | ||||||
| 	access_model "code.gitea.io/gitea/models/perm/access" | 	access_model "code.gitea.io/gitea/models/perm/access" | ||||||
| @@ -206,9 +207,36 @@ func reqPackageAccess(accessMode perm.AccessMode) func(ctx *context.APIContext) | |||||||
| } | } | ||||||
|  |  | ||||||
| // Contexter middleware already checks token for user sign in process. | // Contexter middleware already checks token for user sign in process. | ||||||
| func reqToken() func(ctx *context.APIContext) { | func reqToken(requiredScope auth_model.AccessTokenScope) func(ctx *context.APIContext) { | ||||||
| 	return func(ctx *context.APIContext) { | 	return func(ctx *context.APIContext) { | ||||||
| 		if true == ctx.Data["IsApiToken"] { | 		// If OAuth2 token is present | ||||||
|  | 		if _, ok := ctx.Data["ApiTokenScope"]; ctx.Data["IsApiToken"] == true && ok { | ||||||
|  | 			// no scope required | ||||||
|  | 			if requiredScope == "" { | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			// 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 { | ||||||
|  | 				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 | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			ctx.Error(http.StatusForbidden, "reqToken", "token does not have required scope: "+requiredScope) | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 		if ctx.Context.IsBasicAuth { | 		if ctx.Context.IsBasicAuth { | ||||||
| @@ -631,7 +659,7 @@ func Routes(ctx gocontext.Context) *web.Route { | |||||||
| 	})) | 	})) | ||||||
|  |  | ||||||
| 	m.Group("", func() { | 	m.Group("", func() { | ||||||
| 		// Miscellaneous | 		// Miscellaneous (no scope required) | ||||||
| 		if setting.API.EnableSwagger { | 		if setting.API.EnableSwagger { | ||||||
| 			m.Get("/swagger", func(ctx *context.APIContext) { | 			m.Get("/swagger", func(ctx *context.APIContext) { | ||||||
| 				ctx.Redirect(setting.AppSubURL + "/api/swagger") | 				ctx.Redirect(setting.AppSubURL + "/api/swagger") | ||||||
| @@ -657,7 +685,7 @@ func Routes(ctx gocontext.Context) *web.Route { | |||||||
| 			m.Get("/repository", settings.GetGeneralRepoSettings) | 			m.Get("/repository", settings.GetGeneralRepoSettings) | ||||||
| 		}) | 		}) | ||||||
|  |  | ||||||
| 		// Notifications | 		// Notifications (requires 'notification' scope) | ||||||
| 		m.Group("/notifications", func() { | 		m.Group("/notifications", func() { | ||||||
| 			m.Combo(""). | 			m.Combo(""). | ||||||
| 				Get(notify.ListNotifications). | 				Get(notify.ListNotifications). | ||||||
| @@ -666,9 +694,9 @@ func Routes(ctx gocontext.Context) *web.Route { | |||||||
| 			m.Combo("/threads/{id}"). | 			m.Combo("/threads/{id}"). | ||||||
| 				Get(notify.GetThread). | 				Get(notify.GetThread). | ||||||
| 				Patch(notify.ReadThread) | 				Patch(notify.ReadThread) | ||||||
| 		}, reqToken()) | 		}, reqToken(auth_model.AccessTokenScopeNotification)) | ||||||
|  |  | ||||||
| 		// Users | 		// Users (no scope required) | ||||||
| 		m.Group("/users", func() { | 		m.Group("/users", func() { | ||||||
| 			m.Get("/search", reqExploreSignIn(), user.Search) | 			m.Get("/search", reqExploreSignIn(), user.Search) | ||||||
|  |  | ||||||
| @@ -688,6 +716,7 @@ func Routes(ctx gocontext.Context) *web.Route { | |||||||
| 			}, context_service.UserAssignmentAPI()) | 			}, context_service.UserAssignmentAPI()) | ||||||
| 		}) | 		}) | ||||||
|  |  | ||||||
|  | 		// (no scope required) | ||||||
| 		m.Group("/users", func() { | 		m.Group("/users", func() { | ||||||
| 			m.Group("/{username}", func() { | 			m.Group("/{username}", func() { | ||||||
| 				m.Get("/keys", user.ListPublicKeys) | 				m.Get("/keys", user.ListPublicKeys) | ||||||
| @@ -703,57 +732,62 @@ func Routes(ctx gocontext.Context) *web.Route { | |||||||
|  |  | ||||||
| 				m.Get("/subscriptions", user.GetWatchedRepos) | 				m.Get("/subscriptions", user.GetWatchedRepos) | ||||||
| 			}, context_service.UserAssignmentAPI()) | 			}, context_service.UserAssignmentAPI()) | ||||||
| 		}, reqToken()) | 		}, reqToken("")) | ||||||
|  |  | ||||||
| 		m.Group("/user", func() { | 		m.Group("/user", func() { | ||||||
| 			m.Get("", user.GetAuthenticatedUser) | 			m.Get("", user.GetAuthenticatedUser) | ||||||
| 			m.Group("/settings", func() { | 			m.Group("/settings", func() { | ||||||
| 				m.Get("", user.GetUserSettings) | 				m.Get("", reqToken(auth_model.AccessTokenScopeReadUser), user.GetUserSettings) | ||||||
| 				m.Patch("", bind(api.UserSettingsOptions{}), user.UpdateUserSettings) | 				m.Patch("", reqToken(auth_model.AccessTokenScopeUser), bind(api.UserSettingsOptions{}), user.UpdateUserSettings) | ||||||
| 			}, reqToken()) | 			}) | ||||||
| 			m.Combo("/emails").Get(user.ListEmails). | 			m.Combo("/emails").Get(reqToken(auth_model.AccessTokenScopeReadUser), user.ListEmails). | ||||||
| 				Post(bind(api.CreateEmailOption{}), user.AddEmail). | 				Post(reqToken(auth_model.AccessTokenScopeUser), bind(api.CreateEmailOption{}), user.AddEmail). | ||||||
| 				Delete(bind(api.DeleteEmailOption{}), user.DeleteEmail) | 				Delete(reqToken(auth_model.AccessTokenScopeUser), bind(api.DeleteEmailOption{}), user.DeleteEmail) | ||||||
|  |  | ||||||
| 			m.Get("/followers", user.ListMyFollowers) | 			m.Get("/followers", user.ListMyFollowers) | ||||||
| 			m.Group("/following", func() { | 			m.Group("/following", func() { | ||||||
| 				m.Get("", user.ListMyFollowing) | 				m.Get("", user.ListMyFollowing) | ||||||
| 				m.Group("/{username}", func() { | 				m.Group("/{username}", func() { | ||||||
| 					m.Get("", user.CheckMyFollowing) | 					m.Get("", user.CheckMyFollowing) | ||||||
| 					m.Put("", user.Follow) | 					m.Put("", reqToken(auth_model.AccessTokenScopeUserFollow), user.Follow)      // requires 'user:follow' scope | ||||||
| 					m.Delete("", user.Unfollow) | 					m.Delete("", reqToken(auth_model.AccessTokenScopeUserFollow), user.Unfollow) // requires 'user:follow' scope | ||||||
| 				}, context_service.UserAssignmentAPI()) | 				}, context_service.UserAssignmentAPI()) | ||||||
| 			}) | 			}) | ||||||
|  |  | ||||||
|  | 			// (admin:public_key scope) | ||||||
| 			m.Group("/keys", func() { | 			m.Group("/keys", func() { | ||||||
| 				m.Combo("").Get(user.ListMyPublicKeys). | 				m.Combo("").Get(reqToken(auth_model.AccessTokenScopeReadPublicKey), user.ListMyPublicKeys). | ||||||
| 					Post(bind(api.CreateKeyOption{}), user.CreatePublicKey) | 					Post(reqToken(auth_model.AccessTokenScopeWritePublicKey), bind(api.CreateKeyOption{}), user.CreatePublicKey) | ||||||
| 				m.Combo("/{id}").Get(user.GetPublicKey). | 				m.Combo("/{id}").Get(reqToken(auth_model.AccessTokenScopeReadPublicKey), user.GetPublicKey). | ||||||
| 					Delete(user.DeletePublicKey) | 					Delete(reqToken(auth_model.AccessTokenScopeWritePublicKey), user.DeletePublicKey) | ||||||
| 			}) | 			}) | ||||||
|  |  | ||||||
|  | 			// (admin:application scope) | ||||||
| 			m.Group("/applications", func() { | 			m.Group("/applications", func() { | ||||||
| 				m.Combo("/oauth2"). | 				m.Combo("/oauth2"). | ||||||
| 					Get(user.ListOauth2Applications). | 					Get(reqToken(auth_model.AccessTokenScopeReadApplication), user.ListOauth2Applications). | ||||||
| 					Post(bind(api.CreateOAuth2ApplicationOptions{}), user.CreateOauth2Application) | 					Post(reqToken(auth_model.AccessTokenScopeWriteApplication), bind(api.CreateOAuth2ApplicationOptions{}), user.CreateOauth2Application) | ||||||
| 				m.Combo("/oauth2/{id}"). | 				m.Combo("/oauth2/{id}"). | ||||||
| 					Delete(user.DeleteOauth2Application). | 					Delete(reqToken(auth_model.AccessTokenScopeWriteApplication), user.DeleteOauth2Application). | ||||||
| 					Patch(bind(api.CreateOAuth2ApplicationOptions{}), user.UpdateOauth2Application). | 					Patch(reqToken(auth_model.AccessTokenScopeWriteApplication), bind(api.CreateOAuth2ApplicationOptions{}), user.UpdateOauth2Application). | ||||||
| 					Get(user.GetOauth2Application) | 					Get(reqToken(auth_model.AccessTokenScopeReadApplication), user.GetOauth2Application) | ||||||
| 			}, reqToken()) |  | ||||||
|  |  | ||||||
| 			m.Group("/gpg_keys", func() { |  | ||||||
| 				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", user.GetVerificationToken) | 			// (admin:gpg_key scope) | ||||||
| 			m.Post("/gpg_key_verify", bind(api.VerifyGPGKeyOption{}), user.VerifyUserGPGKey) | 			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.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.Combo("/repos").Get(user.ListMyRepos). | 			// (repo scope) | ||||||
|  | 			m.Combo("/repos", reqToken(auth_model.AccessTokenScopeRepo)).Get(user.ListMyRepos). | ||||||
| 				Post(bind(api.CreateRepoOption{}), repo.Create) | 				Post(bind(api.CreateRepoOption{}), repo.Create) | ||||||
|  |  | ||||||
|  | 			// (repo scope) | ||||||
| 			m.Group("/starred", func() { | 			m.Group("/starred", func() { | ||||||
| 				m.Get("", user.GetMyStarredRepos) | 				m.Get("", user.GetMyStarredRepos) | ||||||
| 				m.Group("/{username}/{reponame}", func() { | 				m.Group("/{username}/{reponame}", func() { | ||||||
| @@ -761,57 +795,57 @@ func Routes(ctx gocontext.Context) *web.Route { | |||||||
| 					m.Put("", user.Star) | 					m.Put("", user.Star) | ||||||
| 					m.Delete("", user.Unstar) | 					m.Delete("", user.Unstar) | ||||||
| 				}, repoAssignment()) | 				}, repoAssignment()) | ||||||
| 			}) | 			}, reqToken(auth_model.AccessTokenScopeRepo)) | ||||||
| 			m.Get("/times", repo.ListMyTrackedTimes) | 			m.Get("/times", reqToken(auth_model.AccessTokenScopeRepo), repo.ListMyTrackedTimes) | ||||||
|  | 			m.Get("/stopwatches", reqToken(auth_model.AccessTokenScopeRepo), repo.GetStopwatches) | ||||||
| 			m.Get("/stopwatches", repo.GetStopwatches) | 			m.Get("/subscriptions", reqToken(auth_model.AccessTokenScopeRepo), user.GetMyWatchedRepos) | ||||||
|  | 			m.Get("/teams", reqToken(auth_model.AccessTokenScopeRepo), org.ListUserTeams) | ||||||
| 			m.Get("/subscriptions", user.GetMyWatchedRepos) | 		}, reqToken("")) | ||||||
|  |  | ||||||
| 			m.Get("/teams", org.ListUserTeams) |  | ||||||
| 		}, reqToken()) |  | ||||||
|  |  | ||||||
| 		// Repositories | 		// Repositories | ||||||
| 		m.Post("/org/{org}/repos", reqToken(), bind(api.CreateRepoOption{}), repo.CreateOrgRepoDeprecated) | 		m.Post("/org/{org}/repos", reqToken(auth_model.AccessTokenScopeAdminOrg), bind(api.CreateRepoOption{}), repo.CreateOrgRepoDeprecated) | ||||||
|  |  | ||||||
| 		m.Combo("/repositories/{id}", reqToken()).Get(repo.GetByID) | 		m.Combo("/repositories/{id}", reqToken(auth_model.AccessTokenScopeRepo)).Get(repo.GetByID) | ||||||
|  |  | ||||||
| 		m.Group("/repos", func() { | 		m.Group("/repos", func() { | ||||||
| 			m.Get("/search", repo.Search) | 			m.Get("/search", repo.Search) | ||||||
|  |  | ||||||
| 			m.Get("/issues/search", repo.SearchIssues) | 			m.Get("/issues/search", repo.SearchIssues) | ||||||
|  |  | ||||||
| 			m.Post("/migrate", reqToken(), bind(api.MigrateRepoOptions{}), repo.Migrate) | 			// (repo scope) | ||||||
|  | 			m.Post("/migrate", reqToken(auth_model.AccessTokenScopeRepo), bind(api.MigrateRepoOptions{}), repo.Migrate) | ||||||
|  |  | ||||||
| 			m.Group("/{username}/{reponame}", func() { | 			m.Group("/{username}/{reponame}", func() { | ||||||
| 				m.Combo("").Get(reqAnyRepoReader(), repo.Get). | 				m.Combo("").Get(reqAnyRepoReader(), repo.Get). | ||||||
| 					Delete(reqToken(), reqOwner(), repo.Delete). | 					Delete(reqToken(auth_model.AccessTokenScopeDeleteRepo), reqOwner(), repo.Delete). | ||||||
| 					Patch(reqToken(), reqAdmin(), bind(api.EditRepoOption{}), repo.Edit) | 					Patch(reqToken(auth_model.AccessTokenScopeRepo), reqAdmin(), bind(api.EditRepoOption{}), repo.Edit) | ||||||
| 				m.Post("/generate", reqToken(), reqRepoReader(unit.TypeCode), bind(api.GenerateRepoOption{}), repo.Generate) | 				m.Post("/generate", reqToken(auth_model.AccessTokenScopeRepo), reqRepoReader(unit.TypeCode), bind(api.GenerateRepoOption{}), repo.Generate) | ||||||
| 				m.Post("/transfer", reqOwner(), bind(api.TransferRepoOption{}), repo.Transfer) | 				m.Group("/transfer", func() { | ||||||
| 				m.Post("/transfer/accept", reqToken(), repo.AcceptTransfer) | 					m.Post("", reqOwner(), bind(api.TransferRepoOption{}), repo.Transfer) | ||||||
| 				m.Post("/transfer/reject", reqToken(), repo.RejectTransfer) | 					m.Post("/accept", repo.AcceptTransfer) | ||||||
| 				m.Combo("/notifications"). | 					m.Post("/reject", repo.RejectTransfer) | ||||||
| 					Get(reqToken(), notify.ListRepoNotifications). | 				}, reqToken(auth_model.AccessTokenScopeRepo)) | ||||||
| 					Put(reqToken(), notify.ReadRepoNotifications) | 				m.Combo("/notifications", reqToken(auth_model.AccessTokenScopeNotification)). | ||||||
|  | 					Get(notify.ListRepoNotifications). | ||||||
|  | 					Put(notify.ReadRepoNotifications) | ||||||
| 				m.Group("/hooks/git", func() { | 				m.Group("/hooks/git", func() { | ||||||
| 					m.Combo("").Get(repo.ListGitHooks) | 					m.Combo("").Get(reqToken(auth_model.AccessTokenScopeReadRepoHook), repo.ListGitHooks) | ||||||
| 					m.Group("/{id}", func() { | 					m.Group("/{id}", func() { | ||||||
| 						m.Combo("").Get(repo.GetGitHook). | 						m.Combo("").Get(reqToken(auth_model.AccessTokenScopeReadRepoHook), repo.GetGitHook). | ||||||
| 							Patch(bind(api.EditGitHookOption{}), repo.EditGitHook). | 							Patch(reqToken(auth_model.AccessTokenScopeWriteRepoHook), bind(api.EditGitHookOption{}), repo.EditGitHook). | ||||||
| 							Delete(repo.DeleteGitHook) | 							Delete(reqToken(auth_model.AccessTokenScopeWriteRepoHook), repo.DeleteGitHook) | ||||||
| 					}) | 					}) | ||||||
| 				}, reqToken(), reqAdmin(), reqGitHook(), context.ReferencesGitRepo(true)) | 				}, reqAdmin(), reqGitHook(), context.ReferencesGitRepo(true)) | ||||||
| 				m.Group("/hooks", func() { | 				m.Group("/hooks", func() { | ||||||
| 					m.Combo("").Get(repo.ListHooks). | 					m.Combo("").Get(reqToken(auth_model.AccessTokenScopeReadRepoHook), repo.ListHooks). | ||||||
| 						Post(bind(api.CreateHookOption{}), repo.CreateHook) | 						Post(reqToken(auth_model.AccessTokenScopeWriteRepoHook), bind(api.CreateHookOption{}), repo.CreateHook) | ||||||
| 					m.Group("/{id}", func() { | 					m.Group("/{id}", func() { | ||||||
| 						m.Combo("").Get(repo.GetHook). | 						m.Combo("").Get(reqToken(auth_model.AccessTokenScopeReadRepoHook), repo.GetHook). | ||||||
| 							Patch(bind(api.EditHookOption{}), repo.EditHook). | 							Patch(reqToken(auth_model.AccessTokenScopeWriteRepoHook), bind(api.EditHookOption{}), repo.EditHook). | ||||||
| 							Delete(repo.DeleteHook) | 							Delete(reqToken(auth_model.AccessTokenScopeWriteRepoHook), repo.DeleteHook) | ||||||
| 						m.Post("/tests", context.ReferencesGitRepo(), context.RepoRefForAPI, repo.TestHook) | 						m.Post("/tests", reqToken(auth_model.AccessTokenScopeReadRepoHook), context.ReferencesGitRepo(), context.RepoRefForAPI, repo.TestHook) | ||||||
| 					}) | 					}) | ||||||
| 				}, reqToken(), reqAdmin(), reqWebhooksEnabled()) | 				}, reqAdmin(), reqWebhooksEnabled()) | ||||||
| 				m.Group("/collaborators", func() { | 				m.Group("/collaborators", func() { | ||||||
| 					m.Get("", reqAnyRepoReader(), repo.ListCollaborators) | 					m.Get("", reqAnyRepoReader(), repo.ListCollaborators) | ||||||
| 					m.Group("/{collaborator}", func() { | 					m.Group("/{collaborator}", func() { | ||||||
| @@ -819,26 +853,26 @@ func Routes(ctx gocontext.Context) *web.Route { | |||||||
| 							Put(reqAdmin(), bind(api.AddCollaboratorOption{}), repo.AddCollaborator). | 							Put(reqAdmin(), bind(api.AddCollaboratorOption{}), repo.AddCollaborator). | ||||||
| 							Delete(reqAdmin(), repo.DeleteCollaborator) | 							Delete(reqAdmin(), repo.DeleteCollaborator) | ||||||
| 						m.Get("/permission", repo.GetRepoPermissions) | 						m.Get("/permission", repo.GetRepoPermissions) | ||||||
| 					}, reqToken()) | 					}) | ||||||
| 				}, reqToken()) | 				}, reqToken(auth_model.AccessTokenScopeRepo)) | ||||||
| 				m.Get("/assignees", reqToken(), reqAnyRepoReader(), repo.GetAssignees) | 				m.Get("/assignees", reqToken(auth_model.AccessTokenScopeRepo), reqAnyRepoReader(), repo.GetAssignees) | ||||||
| 				m.Get("/reviewers", reqToken(), reqAnyRepoReader(), repo.GetReviewers) | 				m.Get("/reviewers", reqToken(auth_model.AccessTokenScopeRepo), reqAnyRepoReader(), repo.GetReviewers) | ||||||
| 				m.Group("/teams", func() { | 				m.Group("/teams", func() { | ||||||
| 					m.Get("", reqAnyRepoReader(), repo.ListTeams) | 					m.Get("", reqAnyRepoReader(), repo.ListTeams) | ||||||
| 					m.Combo("/{team}").Get(reqAnyRepoReader(), repo.IsTeam). | 					m.Combo("/{team}").Get(reqAnyRepoReader(), repo.IsTeam). | ||||||
| 						Put(reqAdmin(), repo.AddTeam). | 						Put(reqAdmin(), repo.AddTeam). | ||||||
| 						Delete(reqAdmin(), repo.DeleteTeam) | 						Delete(reqAdmin(), repo.DeleteTeam) | ||||||
| 				}, reqToken()) | 				}, reqToken(auth_model.AccessTokenScopeRepo)) | ||||||
| 				m.Get("/raw/*", context.ReferencesGitRepo(), context.RepoRefForAPI, reqRepoReader(unit.TypeCode), repo.GetRawFile) | 				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("/media/*", context.ReferencesGitRepo(), context.RepoRefForAPI, reqRepoReader(unit.TypeCode), repo.GetRawFileOrLFS) | ||||||
| 				m.Get("/archive/*", reqRepoReader(unit.TypeCode), repo.GetArchive) | 				m.Get("/archive/*", reqRepoReader(unit.TypeCode), repo.GetArchive) | ||||||
| 				m.Combo("/forks").Get(repo.ListForks). | 				m.Combo("/forks").Get(repo.ListForks). | ||||||
| 					Post(reqToken(), reqRepoReader(unit.TypeCode), bind(api.CreateForkOption{}), repo.CreateFork) | 					Post(reqToken(auth_model.AccessTokenScopeRepo), reqRepoReader(unit.TypeCode), bind(api.CreateForkOption{}), repo.CreateFork) | ||||||
| 				m.Group("/branches", func() { | 				m.Group("/branches", func() { | ||||||
| 					m.Get("", repo.ListBranches) | 					m.Get("", repo.ListBranches) | ||||||
| 					m.Get("/*", repo.GetBranch) | 					m.Get("/*", repo.GetBranch) | ||||||
| 					m.Delete("/*", reqRepoWriter(unit.TypeCode), repo.DeleteBranch) | 					m.Delete("/*", reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeCode), repo.DeleteBranch) | ||||||
| 					m.Post("", reqRepoWriter(unit.TypeCode), bind(api.CreateBranchRepoOption{}), repo.CreateBranch) | 					m.Post("", reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeCode), bind(api.CreateBranchRepoOption{}), repo.CreateBranch) | ||||||
| 				}, context.ReferencesGitRepo(), reqRepoReader(unit.TypeCode)) | 				}, context.ReferencesGitRepo(), reqRepoReader(unit.TypeCode)) | ||||||
| 				m.Group("/branch_protections", func() { | 				m.Group("/branch_protections", func() { | ||||||
| 					m.Get("", repo.ListBranchProtections) | 					m.Get("", repo.ListBranchProtections) | ||||||
| @@ -848,74 +882,74 @@ func Routes(ctx gocontext.Context) *web.Route { | |||||||
| 						m.Patch("", bind(api.EditBranchProtectionOption{}), repo.EditBranchProtection) | 						m.Patch("", bind(api.EditBranchProtectionOption{}), repo.EditBranchProtection) | ||||||
| 						m.Delete("", repo.DeleteBranchProtection) | 						m.Delete("", repo.DeleteBranchProtection) | ||||||
| 					}) | 					}) | ||||||
| 				}, reqToken(), reqAdmin()) | 				}, reqToken(auth_model.AccessTokenScopeRepo), reqAdmin()) | ||||||
| 				m.Group("/tags", func() { | 				m.Group("/tags", func() { | ||||||
| 					m.Get("", repo.ListTags) | 					m.Get("", repo.ListTags) | ||||||
| 					m.Get("/*", repo.GetTag) | 					m.Get("/*", repo.GetTag) | ||||||
| 					m.Post("", reqRepoWriter(unit.TypeCode), bind(api.CreateTagOption{}), repo.CreateTag) | 					m.Post("", reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeCode), bind(api.CreateTagOption{}), repo.CreateTag) | ||||||
| 					m.Delete("/*", repo.DeleteTag) | 					m.Delete("/*", reqToken(auth_model.AccessTokenScopeRepo), repo.DeleteTag) | ||||||
| 				}, reqRepoReader(unit.TypeCode), context.ReferencesGitRepo(true)) | 				}, reqRepoReader(unit.TypeCode), context.ReferencesGitRepo(true)) | ||||||
| 				m.Group("/keys", func() { | 				m.Group("/keys", func() { | ||||||
| 					m.Combo("").Get(repo.ListDeployKeys). | 					m.Combo("").Get(repo.ListDeployKeys). | ||||||
| 						Post(bind(api.CreateKeyOption{}), repo.CreateDeployKey) | 						Post(bind(api.CreateKeyOption{}), repo.CreateDeployKey) | ||||||
| 					m.Combo("/{id}").Get(repo.GetDeployKey). | 					m.Combo("/{id}").Get(repo.GetDeployKey). | ||||||
| 						Delete(repo.DeleteDeploykey) | 						Delete(repo.DeleteDeploykey) | ||||||
| 				}, reqToken(), reqAdmin()) | 				}, reqToken(auth_model.AccessTokenScopeRepo), reqAdmin()) | ||||||
| 				m.Group("/times", func() { | 				m.Group("/times", func() { | ||||||
| 					m.Combo("").Get(repo.ListTrackedTimesByRepository) | 					m.Combo("").Get(repo.ListTrackedTimesByRepository) | ||||||
| 					m.Combo("/{timetrackingusername}").Get(repo.ListTrackedTimesByUser) | 					m.Combo("/{timetrackingusername}").Get(repo.ListTrackedTimesByUser) | ||||||
| 				}, mustEnableIssues, reqToken()) | 				}, mustEnableIssues, reqToken(auth_model.AccessTokenScopeRepo)) | ||||||
| 				m.Group("/wiki", func() { | 				m.Group("/wiki", func() { | ||||||
| 					m.Combo("/page/{pageName}"). | 					m.Combo("/page/{pageName}"). | ||||||
| 						Get(repo.GetWikiPage). | 						Get(repo.GetWikiPage). | ||||||
| 						Patch(mustNotBeArchived, reqRepoWriter(unit.TypeWiki), bind(api.CreateWikiPageOptions{}), repo.EditWikiPage). | 						Patch(mustNotBeArchived, reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeWiki), bind(api.CreateWikiPageOptions{}), repo.EditWikiPage). | ||||||
| 						Delete(mustNotBeArchived, reqRepoWriter(unit.TypeWiki), repo.DeleteWikiPage) | 						Delete(mustNotBeArchived, reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeWiki), repo.DeleteWikiPage) | ||||||
| 					m.Get("/revisions/{pageName}", repo.ListPageRevisions) | 					m.Get("/revisions/{pageName}", repo.ListPageRevisions) | ||||||
| 					m.Post("/new", mustNotBeArchived, reqRepoWriter(unit.TypeWiki), bind(api.CreateWikiPageOptions{}), repo.NewWikiPage) | 					m.Post("/new", mustNotBeArchived, reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeWiki), bind(api.CreateWikiPageOptions{}), repo.NewWikiPage) | ||||||
| 					m.Get("/pages", repo.ListWikiPages) | 					m.Get("/pages", repo.ListWikiPages) | ||||||
| 				}, mustEnableWiki) | 				}, mustEnableWiki) | ||||||
| 				m.Group("/issues", func() { | 				m.Group("/issues", func() { | ||||||
| 					m.Combo("").Get(repo.ListIssues). | 					m.Combo("").Get(repo.ListIssues). | ||||||
| 						Post(reqToken(), mustNotBeArchived, bind(api.CreateIssueOption{}), repo.CreateIssue) | 						Post(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, bind(api.CreateIssueOption{}), repo.CreateIssue) | ||||||
| 					m.Group("/comments", func() { | 					m.Group("/comments", func() { | ||||||
| 						m.Get("", repo.ListRepoIssueComments) | 						m.Get("", repo.ListRepoIssueComments) | ||||||
| 						m.Group("/{id}", func() { | 						m.Group("/{id}", func() { | ||||||
| 							m.Combo(""). | 							m.Combo(""). | ||||||
| 								Get(repo.GetIssueComment). | 								Get(repo.GetIssueComment). | ||||||
| 								Patch(mustNotBeArchived, reqToken(), bind(api.EditIssueCommentOption{}), repo.EditIssueComment). | 								Patch(mustNotBeArchived, reqToken(auth_model.AccessTokenScopeRepo), bind(api.EditIssueCommentOption{}), repo.EditIssueComment). | ||||||
| 								Delete(reqToken(), repo.DeleteIssueComment) | 								Delete(reqToken(auth_model.AccessTokenScopeRepo), repo.DeleteIssueComment) | ||||||
| 							m.Combo("/reactions"). | 							m.Combo("/reactions"). | ||||||
| 								Get(repo.GetIssueCommentReactions). | 								Get(repo.GetIssueCommentReactions). | ||||||
| 								Post(reqToken(), bind(api.EditReactionOption{}), repo.PostIssueCommentReaction). | 								Post(reqToken(auth_model.AccessTokenScopeRepo), bind(api.EditReactionOption{}), repo.PostIssueCommentReaction). | ||||||
| 								Delete(reqToken(), bind(api.EditReactionOption{}), repo.DeleteIssueCommentReaction) | 								Delete(reqToken(auth_model.AccessTokenScopeRepo), bind(api.EditReactionOption{}), repo.DeleteIssueCommentReaction) | ||||||
| 							m.Group("/assets", func() { | 							m.Group("/assets", func() { | ||||||
| 								m.Combo(""). | 								m.Combo(""). | ||||||
| 									Get(repo.ListIssueCommentAttachments). | 									Get(repo.ListIssueCommentAttachments). | ||||||
| 									Post(reqToken(), mustNotBeArchived, repo.CreateIssueCommentAttachment) | 									Post(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, repo.CreateIssueCommentAttachment) | ||||||
| 								m.Combo("/{asset}"). | 								m.Combo("/{asset}"). | ||||||
| 									Get(repo.GetIssueCommentAttachment). | 									Get(repo.GetIssueCommentAttachment). | ||||||
| 									Patch(reqToken(), mustNotBeArchived, bind(api.EditAttachmentOptions{}), repo.EditIssueCommentAttachment). | 									Patch(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, bind(api.EditAttachmentOptions{}), repo.EditIssueCommentAttachment). | ||||||
| 									Delete(reqToken(), mustNotBeArchived, repo.DeleteIssueCommentAttachment) | 									Delete(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, repo.DeleteIssueCommentAttachment) | ||||||
| 							}, mustEnableAttachments) | 							}, mustEnableAttachments) | ||||||
| 						}) | 						}) | ||||||
| 					}) | 					}) | ||||||
| 					m.Group("/{index}", func() { | 					m.Group("/{index}", func() { | ||||||
| 						m.Combo("").Get(repo.GetIssue). | 						m.Combo("").Get(repo.GetIssue). | ||||||
| 							Patch(reqToken(), bind(api.EditIssueOption{}), repo.EditIssue). | 							Patch(reqToken(auth_model.AccessTokenScopeRepo), bind(api.EditIssueOption{}), repo.EditIssue). | ||||||
| 							Delete(reqToken(), reqAdmin(), context.ReferencesGitRepo(), repo.DeleteIssue) | 							Delete(reqToken(auth_model.AccessTokenScopeRepo), reqAdmin(), context.ReferencesGitRepo(), repo.DeleteIssue) | ||||||
| 						m.Group("/comments", func() { | 						m.Group("/comments", func() { | ||||||
| 							m.Combo("").Get(repo.ListIssueComments). | 							m.Combo("").Get(repo.ListIssueComments). | ||||||
| 								Post(reqToken(), mustNotBeArchived, bind(api.CreateIssueCommentOption{}), repo.CreateIssueComment) | 								Post(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, bind(api.CreateIssueCommentOption{}), repo.CreateIssueComment) | ||||||
| 							m.Combo("/{id}", reqToken()).Patch(bind(api.EditIssueCommentOption{}), repo.EditIssueCommentDeprecated). | 							m.Combo("/{id}", reqToken(auth_model.AccessTokenScopeRepo)).Patch(bind(api.EditIssueCommentOption{}), repo.EditIssueCommentDeprecated). | ||||||
| 								Delete(repo.DeleteIssueCommentDeprecated) | 								Delete(repo.DeleteIssueCommentDeprecated) | ||||||
| 						}) | 						}) | ||||||
| 						m.Get("/timeline", repo.ListIssueCommentsAndTimeline) | 						m.Get("/timeline", repo.ListIssueCommentsAndTimeline) | ||||||
| 						m.Group("/labels", func() { | 						m.Group("/labels", func() { | ||||||
| 							m.Combo("").Get(repo.ListIssueLabels). | 							m.Combo("").Get(repo.ListIssueLabels). | ||||||
| 								Post(reqToken(), bind(api.IssueLabelsOption{}), repo.AddIssueLabels). | 								Post(reqToken(auth_model.AccessTokenScopeRepo), bind(api.IssueLabelsOption{}), repo.AddIssueLabels). | ||||||
| 								Put(reqToken(), bind(api.IssueLabelsOption{}), repo.ReplaceIssueLabels). | 								Put(reqToken(auth_model.AccessTokenScopeRepo), bind(api.IssueLabelsOption{}), repo.ReplaceIssueLabels). | ||||||
| 								Delete(reqToken(), repo.ClearIssueLabels) | 								Delete(reqToken(auth_model.AccessTokenScopeRepo), repo.ClearIssueLabels) | ||||||
| 							m.Delete("/{id}", reqToken(), repo.DeleteIssueLabel) | 							m.Delete("/{id}", reqToken(auth_model.AccessTokenScopeRepo), repo.DeleteIssueLabel) | ||||||
| 						}) | 						}) | ||||||
| 						m.Group("/times", func() { | 						m.Group("/times", func() { | ||||||
| 							m.Combo(""). | 							m.Combo(""). | ||||||
| @@ -923,125 +957,125 @@ func Routes(ctx gocontext.Context) *web.Route { | |||||||
| 								Post(bind(api.AddTimeOption{}), repo.AddTime). | 								Post(bind(api.AddTimeOption{}), repo.AddTime). | ||||||
| 								Delete(repo.ResetIssueTime) | 								Delete(repo.ResetIssueTime) | ||||||
| 							m.Delete("/{id}", repo.DeleteTime) | 							m.Delete("/{id}", repo.DeleteTime) | ||||||
| 						}, reqToken()) | 						}, reqToken(auth_model.AccessTokenScopeRepo)) | ||||||
| 						m.Combo("/deadline").Post(reqToken(), bind(api.EditDeadlineOption{}), repo.UpdateIssueDeadline) | 						m.Combo("/deadline").Post(reqToken(auth_model.AccessTokenScopeRepo), bind(api.EditDeadlineOption{}), repo.UpdateIssueDeadline) | ||||||
| 						m.Group("/stopwatch", func() { | 						m.Group("/stopwatch", func() { | ||||||
| 							m.Post("/start", reqToken(), repo.StartIssueStopwatch) | 							m.Post("/start", reqToken(auth_model.AccessTokenScopeRepo), repo.StartIssueStopwatch) | ||||||
| 							m.Post("/stop", reqToken(), repo.StopIssueStopwatch) | 							m.Post("/stop", reqToken(auth_model.AccessTokenScopeRepo), repo.StopIssueStopwatch) | ||||||
| 							m.Delete("/delete", reqToken(), repo.DeleteIssueStopwatch) | 							m.Delete("/delete", reqToken(auth_model.AccessTokenScopeRepo), repo.DeleteIssueStopwatch) | ||||||
| 						}) | 						}) | ||||||
| 						m.Group("/subscriptions", func() { | 						m.Group("/subscriptions", func() { | ||||||
| 							m.Get("", repo.GetIssueSubscribers) | 							m.Get("", repo.GetIssueSubscribers) | ||||||
| 							m.Get("/check", reqToken(), repo.CheckIssueSubscription) | 							m.Get("/check", reqToken(auth_model.AccessTokenScopeRepo), repo.CheckIssueSubscription) | ||||||
| 							m.Put("/{user}", reqToken(), repo.AddIssueSubscription) | 							m.Put("/{user}", reqToken(auth_model.AccessTokenScopeRepo), repo.AddIssueSubscription) | ||||||
| 							m.Delete("/{user}", reqToken(), repo.DelIssueSubscription) | 							m.Delete("/{user}", reqToken(auth_model.AccessTokenScopeRepo), repo.DelIssueSubscription) | ||||||
| 						}) | 						}) | ||||||
| 						m.Combo("/reactions"). | 						m.Combo("/reactions"). | ||||||
| 							Get(repo.GetIssueReactions). | 							Get(repo.GetIssueReactions). | ||||||
| 							Post(reqToken(), bind(api.EditReactionOption{}), repo.PostIssueReaction). | 							Post(reqToken(auth_model.AccessTokenScopeRepo), bind(api.EditReactionOption{}), repo.PostIssueReaction). | ||||||
| 							Delete(reqToken(), bind(api.EditReactionOption{}), repo.DeleteIssueReaction) | 							Delete(reqToken(auth_model.AccessTokenScopeRepo), bind(api.EditReactionOption{}), repo.DeleteIssueReaction) | ||||||
| 						m.Group("/assets", func() { | 						m.Group("/assets", func() { | ||||||
| 							m.Combo(""). | 							m.Combo(""). | ||||||
| 								Get(repo.ListIssueAttachments). | 								Get(repo.ListIssueAttachments). | ||||||
| 								Post(reqToken(), mustNotBeArchived, repo.CreateIssueAttachment) | 								Post(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, repo.CreateIssueAttachment) | ||||||
| 							m.Combo("/{asset}"). | 							m.Combo("/{asset}"). | ||||||
| 								Get(repo.GetIssueAttachment). | 								Get(repo.GetIssueAttachment). | ||||||
| 								Patch(reqToken(), mustNotBeArchived, bind(api.EditAttachmentOptions{}), repo.EditIssueAttachment). | 								Patch(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, bind(api.EditAttachmentOptions{}), repo.EditIssueAttachment). | ||||||
| 								Delete(reqToken(), mustNotBeArchived, repo.DeleteIssueAttachment) | 								Delete(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, repo.DeleteIssueAttachment) | ||||||
| 						}, mustEnableAttachments) | 						}, mustEnableAttachments) | ||||||
| 					}) | 					}) | ||||||
| 				}, mustEnableIssuesOrPulls) | 				}, mustEnableIssuesOrPulls) | ||||||
| 				m.Group("/labels", func() { | 				m.Group("/labels", func() { | ||||||
| 					m.Combo("").Get(repo.ListLabels). | 					m.Combo("").Get(repo.ListLabels). | ||||||
| 						Post(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.CreateLabelOption{}), repo.CreateLabel) | 						Post(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.CreateLabelOption{}), repo.CreateLabel) | ||||||
| 					m.Combo("/{id}").Get(repo.GetLabel). | 					m.Combo("/{id}").Get(repo.GetLabel). | ||||||
| 						Patch(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.EditLabelOption{}), repo.EditLabel). | 						Patch(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.EditLabelOption{}), repo.EditLabel). | ||||||
| 						Delete(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), repo.DeleteLabel) | 						Delete(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), repo.DeleteLabel) | ||||||
| 				}) | 				}) | ||||||
| 				m.Post("/markdown", bind(api.MarkdownOption{}), misc.Markdown) | 				m.Post("/markdown", reqToken(auth_model.AccessTokenScopeRepo), bind(api.MarkdownOption{}), misc.Markdown) | ||||||
| 				m.Post("/markdown/raw", misc.MarkdownRaw) | 				m.Post("/markdown/raw", reqToken(auth_model.AccessTokenScopeRepo), misc.MarkdownRaw) | ||||||
| 				m.Group("/milestones", func() { | 				m.Group("/milestones", func() { | ||||||
| 					m.Combo("").Get(repo.ListMilestones). | 					m.Combo("").Get(repo.ListMilestones). | ||||||
| 						Post(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.CreateMilestoneOption{}), repo.CreateMilestone) | 						Post(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.CreateMilestoneOption{}), repo.CreateMilestone) | ||||||
| 					m.Combo("/{id}").Get(repo.GetMilestone). | 					m.Combo("/{id}").Get(repo.GetMilestone). | ||||||
| 						Patch(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.EditMilestoneOption{}), repo.EditMilestone). | 						Patch(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.EditMilestoneOption{}), repo.EditMilestone). | ||||||
| 						Delete(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), repo.DeleteMilestone) | 						Delete(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), repo.DeleteMilestone) | ||||||
| 				}) | 				}) | ||||||
| 				m.Get("/stargazers", repo.ListStargazers) | 				m.Get("/stargazers", repo.ListStargazers) | ||||||
| 				m.Get("/subscribers", repo.ListSubscribers) | 				m.Get("/subscribers", repo.ListSubscribers) | ||||||
| 				m.Group("/subscription", func() { | 				m.Group("/subscription", func() { | ||||||
| 					m.Get("", user.IsWatching) | 					m.Get("", user.IsWatching) | ||||||
| 					m.Put("", reqToken(), user.Watch) | 					m.Put("", reqToken(auth_model.AccessTokenScopeRepo), user.Watch) | ||||||
| 					m.Delete("", reqToken(), user.Unwatch) | 					m.Delete("", reqToken(auth_model.AccessTokenScopeRepo), user.Unwatch) | ||||||
| 				}) | 				}) | ||||||
| 				m.Group("/releases", func() { | 				m.Group("/releases", func() { | ||||||
| 					m.Combo("").Get(repo.ListReleases). | 					m.Combo("").Get(repo.ListReleases). | ||||||
| 						Post(reqToken(), reqRepoWriter(unit.TypeReleases), context.ReferencesGitRepo(), bind(api.CreateReleaseOption{}), repo.CreateRelease) | 						Post(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeReleases), context.ReferencesGitRepo(), bind(api.CreateReleaseOption{}), repo.CreateRelease) | ||||||
| 					m.Group("/{id}", func() { | 					m.Group("/{id}", func() { | ||||||
| 						m.Combo("").Get(repo.GetRelease). | 						m.Combo("").Get(repo.GetRelease). | ||||||
| 							Patch(reqToken(), reqRepoWriter(unit.TypeReleases), context.ReferencesGitRepo(), bind(api.EditReleaseOption{}), repo.EditRelease). | 							Patch(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeReleases), context.ReferencesGitRepo(), bind(api.EditReleaseOption{}), repo.EditRelease). | ||||||
| 							Delete(reqToken(), reqRepoWriter(unit.TypeReleases), repo.DeleteRelease) | 							Delete(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeReleases), repo.DeleteRelease) | ||||||
| 						m.Group("/assets", func() { | 						m.Group("/assets", func() { | ||||||
| 							m.Combo("").Get(repo.ListReleaseAttachments). | 							m.Combo("").Get(repo.ListReleaseAttachments). | ||||||
| 								Post(reqToken(), reqRepoWriter(unit.TypeReleases), repo.CreateReleaseAttachment) | 								Post(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeReleases), repo.CreateReleaseAttachment) | ||||||
| 							m.Combo("/{asset}").Get(repo.GetReleaseAttachment). | 							m.Combo("/{asset}").Get(repo.GetReleaseAttachment). | ||||||
| 								Patch(reqToken(), reqRepoWriter(unit.TypeReleases), bind(api.EditAttachmentOptions{}), repo.EditReleaseAttachment). | 								Patch(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeReleases), bind(api.EditAttachmentOptions{}), repo.EditReleaseAttachment). | ||||||
| 								Delete(reqToken(), reqRepoWriter(unit.TypeReleases), repo.DeleteReleaseAttachment) | 								Delete(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeReleases), repo.DeleteReleaseAttachment) | ||||||
| 						}) | 						}) | ||||||
| 					}) | 					}) | ||||||
| 					m.Group("/tags", func() { | 					m.Group("/tags", func() { | ||||||
| 						m.Combo("/{tag}"). | 						m.Combo("/{tag}"). | ||||||
| 							Get(repo.GetReleaseByTag). | 							Get(repo.GetReleaseByTag). | ||||||
| 							Delete(reqToken(), reqRepoWriter(unit.TypeReleases), repo.DeleteReleaseByTag) | 							Delete(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeReleases), repo.DeleteReleaseByTag) | ||||||
| 					}) | 					}) | ||||||
| 				}, reqRepoReader(unit.TypeReleases)) | 				}, reqRepoReader(unit.TypeReleases)) | ||||||
| 				m.Post("/mirror-sync", reqToken(), reqRepoWriter(unit.TypeCode), repo.MirrorSync) | 				m.Post("/mirror-sync", reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeCode), repo.MirrorSync) | ||||||
| 				m.Post("/push_mirrors-sync", reqAdmin(), repo.PushMirrorSync) | 				m.Post("/push_mirrors-sync", reqAdmin(), reqToken(auth_model.AccessTokenScopeRepo), repo.PushMirrorSync) | ||||||
| 				m.Group("/push_mirrors", func() { | 				m.Group("/push_mirrors", func() { | ||||||
| 					m.Combo("").Get(repo.ListPushMirrors). | 					m.Combo("").Get(repo.ListPushMirrors). | ||||||
| 						Post(bind(api.CreatePushMirrorOption{}), repo.AddPushMirror) | 						Post(bind(api.CreatePushMirrorOption{}), repo.AddPushMirror) | ||||||
| 					m.Combo("/{name}"). | 					m.Combo("/{name}"). | ||||||
| 						Delete(repo.DeletePushMirrorByRemoteName). | 						Delete(repo.DeletePushMirrorByRemoteName). | ||||||
| 						Get(repo.GetPushMirrorByName) | 						Get(repo.GetPushMirrorByName) | ||||||
| 				}, reqAdmin()) | 				}, reqAdmin(), reqToken(auth_model.AccessTokenScopeRepo)) | ||||||
|  |  | ||||||
| 				m.Get("/editorconfig/{filename}", context.ReferencesGitRepo(), context.RepoRefForAPI, reqRepoReader(unit.TypeCode), repo.GetEditorconfig) | 				m.Get("/editorconfig/{filename}", context.ReferencesGitRepo(), context.RepoRefForAPI, reqRepoReader(unit.TypeCode), repo.GetEditorconfig) | ||||||
| 				m.Group("/pulls", func() { | 				m.Group("/pulls", func() { | ||||||
| 					m.Combo("").Get(repo.ListPullRequests). | 					m.Combo("").Get(repo.ListPullRequests). | ||||||
| 						Post(reqToken(), mustNotBeArchived, bind(api.CreatePullRequestOption{}), repo.CreatePullRequest) | 						Post(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, bind(api.CreatePullRequestOption{}), repo.CreatePullRequest) | ||||||
| 					m.Group("/{index}", func() { | 					m.Group("/{index}", func() { | ||||||
| 						m.Combo("").Get(repo.GetPullRequest). | 						m.Combo("").Get(repo.GetPullRequest). | ||||||
| 							Patch(reqToken(), bind(api.EditPullRequestOption{}), repo.EditPullRequest) | 							Patch(reqToken(auth_model.AccessTokenScopeRepo), bind(api.EditPullRequestOption{}), repo.EditPullRequest) | ||||||
| 						m.Get(".{diffType:diff|patch}", repo.DownloadPullDiffOrPatch) | 						m.Get(".{diffType:diff|patch}", repo.DownloadPullDiffOrPatch) | ||||||
| 						m.Post("/update", reqToken(), repo.UpdatePullRequest) | 						m.Post("/update", reqToken(auth_model.AccessTokenScopeRepo), repo.UpdatePullRequest) | ||||||
| 						m.Get("/commits", repo.GetPullRequestCommits) | 						m.Get("/commits", repo.GetPullRequestCommits) | ||||||
| 						m.Get("/files", repo.GetPullRequestFiles) | 						m.Get("/files", repo.GetPullRequestFiles) | ||||||
| 						m.Combo("/merge").Get(repo.IsPullRequestMerged). | 						m.Combo("/merge").Get(repo.IsPullRequestMerged). | ||||||
| 							Post(reqToken(), mustNotBeArchived, bind(forms.MergePullRequestForm{}), repo.MergePullRequest). | 							Post(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, bind(forms.MergePullRequestForm{}), repo.MergePullRequest). | ||||||
| 							Delete(reqToken(), mustNotBeArchived, repo.CancelScheduledAutoMerge) | 							Delete(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, repo.CancelScheduledAutoMerge) | ||||||
| 						m.Group("/reviews", func() { | 						m.Group("/reviews", func() { | ||||||
| 							m.Combo(""). | 							m.Combo(""). | ||||||
| 								Get(repo.ListPullReviews). | 								Get(repo.ListPullReviews). | ||||||
| 								Post(reqToken(), bind(api.CreatePullReviewOptions{}), repo.CreatePullReview) | 								Post(reqToken(auth_model.AccessTokenScopeRepo), bind(api.CreatePullReviewOptions{}), repo.CreatePullReview) | ||||||
| 							m.Group("/{id}", func() { | 							m.Group("/{id}", func() { | ||||||
| 								m.Combo(""). | 								m.Combo(""). | ||||||
| 									Get(repo.GetPullReview). | 									Get(repo.GetPullReview). | ||||||
| 									Delete(reqToken(), repo.DeletePullReview). | 									Delete(reqToken(auth_model.AccessTokenScopeRepo), repo.DeletePullReview). | ||||||
| 									Post(reqToken(), bind(api.SubmitPullReviewOptions{}), repo.SubmitPullReview) | 									Post(reqToken(auth_model.AccessTokenScopeRepo), bind(api.SubmitPullReviewOptions{}), repo.SubmitPullReview) | ||||||
| 								m.Combo("/comments"). | 								m.Combo("/comments"). | ||||||
| 									Get(repo.GetPullReviewComments) | 									Get(repo.GetPullReviewComments) | ||||||
| 								m.Post("/dismissals", reqToken(), bind(api.DismissPullReviewOptions{}), repo.DismissPullReview) | 								m.Post("/dismissals", reqToken(auth_model.AccessTokenScopeRepo), bind(api.DismissPullReviewOptions{}), repo.DismissPullReview) | ||||||
| 								m.Post("/undismissals", reqToken(), repo.UnDismissPullReview) | 								m.Post("/undismissals", reqToken(auth_model.AccessTokenScopeRepo), repo.UnDismissPullReview) | ||||||
| 							}) | 							}) | ||||||
| 						}) | 						}) | ||||||
| 						m.Combo("/requested_reviewers"). | 						m.Combo("/requested_reviewers", reqToken(auth_model.AccessTokenScopeRepo)). | ||||||
| 							Delete(reqToken(), bind(api.PullReviewRequestOptions{}), repo.DeleteReviewRequests). | 							Delete(bind(api.PullReviewRequestOptions{}), repo.DeleteReviewRequests). | ||||||
| 							Post(reqToken(), bind(api.PullReviewRequestOptions{}), repo.CreateReviewRequests) | 							Post(bind(api.PullReviewRequestOptions{}), repo.CreateReviewRequests) | ||||||
| 					}) | 					}) | ||||||
| 				}, mustAllowPulls, reqRepoReader(unit.TypeCode), context.ReferencesGitRepo()) | 				}, mustAllowPulls, reqRepoReader(unit.TypeCode), context.ReferencesGitRepo()) | ||||||
| 				m.Group("/statuses", func() { | 				m.Group("/statuses", func() { | ||||||
| 					m.Combo("/{sha}").Get(repo.GetCommitStatuses). | 					m.Combo("/{sha}").Get(repo.GetCommitStatuses). | ||||||
| 						Post(reqToken(), reqRepoWriter(unit.TypeCode), bind(api.CreateStatusOption{}), repo.NewCommitStatus) | 						Post(reqToken(auth_model.AccessTokenScopeRepoStatus), reqRepoWriter(unit.TypeCode), bind(api.CreateStatusOption{}), repo.NewCommitStatus) | ||||||
| 				}, reqRepoReader(unit.TypeCode)) | 				}, reqRepoReader(unit.TypeCode)) | ||||||
| 				m.Group("/commits", func() { | 				m.Group("/commits", func() { | ||||||
| 					m.Get("", context.ReferencesGitRepo(), repo.GetAllCommits) | 					m.Get("", context.ReferencesGitRepo(), repo.GetAllCommits) | ||||||
| @@ -1062,7 +1096,7 @@ func Routes(ctx gocontext.Context) *web.Route { | |||||||
| 					m.Get("/tags/{sha}", repo.GetAnnotatedTag) | 					m.Get("/tags/{sha}", repo.GetAnnotatedTag) | ||||||
| 					m.Get("/notes/{sha}", repo.GetNote) | 					m.Get("/notes/{sha}", repo.GetNote) | ||||||
| 				}, context.ReferencesGitRepo(true), reqRepoReader(unit.TypeCode)) | 				}, context.ReferencesGitRepo(true), reqRepoReader(unit.TypeCode)) | ||||||
| 				m.Post("/diffpatch", reqRepoWriter(unit.TypeCode), reqToken(), bind(api.ApplyDiffPatchFileOptions{}), repo.ApplyDiffPatch) | 				m.Post("/diffpatch", reqRepoWriter(unit.TypeCode), reqToken(auth_model.AccessTokenScopeRepo), bind(api.ApplyDiffPatchFileOptions{}), repo.ApplyDiffPatch) | ||||||
| 				m.Group("/contents", func() { | 				m.Group("/contents", func() { | ||||||
| 					m.Get("", repo.GetContentsList) | 					m.Get("", repo.GetContentsList) | ||||||
| 					m.Get("/*", repo.GetContents) | 					m.Get("/*", repo.GetContents) | ||||||
| @@ -1070,15 +1104,15 @@ func Routes(ctx gocontext.Context) *web.Route { | |||||||
| 						m.Post("", bind(api.CreateFileOptions{}), reqRepoBranchWriter, repo.CreateFile) | 						m.Post("", bind(api.CreateFileOptions{}), reqRepoBranchWriter, repo.CreateFile) | ||||||
| 						m.Put("", bind(api.UpdateFileOptions{}), reqRepoBranchWriter, repo.UpdateFile) | 						m.Put("", bind(api.UpdateFileOptions{}), reqRepoBranchWriter, repo.UpdateFile) | ||||||
| 						m.Delete("", bind(api.DeleteFileOptions{}), reqRepoBranchWriter, repo.DeleteFile) | 						m.Delete("", bind(api.DeleteFileOptions{}), reqRepoBranchWriter, repo.DeleteFile) | ||||||
| 					}, reqToken()) | 					}, reqToken(auth_model.AccessTokenScopeRepo)) | ||||||
| 				}, reqRepoReader(unit.TypeCode)) | 				}, reqRepoReader(unit.TypeCode)) | ||||||
| 				m.Get("/signing-key.gpg", misc.SigningKey) | 				m.Get("/signing-key.gpg", misc.SigningKey) | ||||||
| 				m.Group("/topics", func() { | 				m.Group("/topics", func() { | ||||||
| 					m.Combo("").Get(repo.ListTopics). | 					m.Combo("").Get(repo.ListTopics). | ||||||
| 						Put(reqToken(), reqAdmin(), bind(api.RepoTopicOptions{}), repo.UpdateTopics) | 						Put(reqToken(auth_model.AccessTokenScopeRepo), reqAdmin(), bind(api.RepoTopicOptions{}), repo.UpdateTopics) | ||||||
| 					m.Group("/{topic}", func() { | 					m.Group("/{topic}", func() { | ||||||
| 						m.Combo("").Put(reqToken(), repo.AddTopic). | 						m.Combo("").Put(reqToken(auth_model.AccessTokenScopeRepo), repo.AddTopic). | ||||||
| 							Delete(reqToken(), repo.DeleteTopic) | 							Delete(reqToken(auth_model.AccessTokenScopeRepo), repo.DeleteTopic) | ||||||
| 					}, reqAdmin()) | 					}, reqAdmin()) | ||||||
| 				}, reqAnyRepoReader()) | 				}, reqAnyRepoReader()) | ||||||
| 				m.Get("/issue_templates", context.ReferencesGitRepo(), repo.GetIssueTemplates) | 				m.Get("/issue_templates", context.ReferencesGitRepo(), repo.GetIssueTemplates) | ||||||
| @@ -1089,49 +1123,49 @@ func Routes(ctx gocontext.Context) *web.Route { | |||||||
| 		// NOTE: these are Gitea package management API - see packages.CommonRoutes and packages.DockerContainerRoutes for endpoints that implement package manager APIs | 		// 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("/packages/{username}", func() { | ||||||
| 			m.Group("/{type}/{name}/{version}", func() { | 			m.Group("/{type}/{name}/{version}", func() { | ||||||
| 				m.Get("", packages.GetPackage) | 				m.Get("", reqToken(auth_model.AccessTokenScopeReadPackage), packages.GetPackage) | ||||||
| 				m.Delete("", reqPackageAccess(perm.AccessModeWrite), packages.DeletePackage) | 				m.Delete("", reqToken(auth_model.AccessTokenScopeDeletePackage), reqPackageAccess(perm.AccessModeWrite), packages.DeletePackage) | ||||||
| 				m.Get("/files", packages.ListPackageFiles) | 				m.Get("/files", reqToken(auth_model.AccessTokenScopeReadPackage), packages.ListPackageFiles) | ||||||
| 			}) | 			}) | ||||||
| 			m.Get("/", packages.ListPackages) | 			m.Get("/", reqToken(auth_model.AccessTokenScopeReadPackage), packages.ListPackages) | ||||||
| 		}, context_service.UserAssignmentAPI(), context.PackageAssignmentAPI(), reqPackageAccess(perm.AccessModeRead)) | 		}, context_service.UserAssignmentAPI(), context.PackageAssignmentAPI(), reqPackageAccess(perm.AccessModeRead)) | ||||||
|  |  | ||||||
| 		// Organizations | 		// Organizations | ||||||
| 		m.Get("/user/orgs", reqToken(), org.ListMyOrgs) | 		m.Get("/user/orgs", reqToken(auth_model.AccessTokenScopeReadOrg), org.ListMyOrgs) | ||||||
| 		m.Group("/users/{username}/orgs", func() { | 		m.Group("/users/{username}/orgs", func() { | ||||||
| 			m.Get("", org.ListUserOrgs) | 			m.Get("", reqToken(auth_model.AccessTokenScopeReadOrg), org.ListUserOrgs) | ||||||
| 			m.Get("/{org}/permissions", reqToken(), org.GetUserOrgsPermissions) | 			m.Get("/{org}/permissions", reqToken(auth_model.AccessTokenScopeReadOrg), org.GetUserOrgsPermissions) | ||||||
| 		}, context_service.UserAssignmentAPI()) | 		}, context_service.UserAssignmentAPI()) | ||||||
| 		m.Post("/orgs", reqToken(), bind(api.CreateOrgOption{}), org.Create) | 		m.Post("/orgs", reqToken(auth_model.AccessTokenScopeWriteOrg), bind(api.CreateOrgOption{}), org.Create) | ||||||
| 		m.Get("/orgs", org.GetAll) | 		m.Get("/orgs", reqToken(auth_model.AccessTokenScopeReadOrg), org.GetAll) | ||||||
| 		m.Group("/orgs/{org}", func() { | 		m.Group("/orgs/{org}", func() { | ||||||
| 			m.Combo("").Get(org.Get). | 			m.Combo("").Get(reqToken(auth_model.AccessTokenScopeReadOrg), org.Get). | ||||||
| 				Patch(reqToken(), reqOrgOwnership(), bind(api.EditOrgOption{}), org.Edit). | 				Patch(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), bind(api.EditOrgOption{}), org.Edit). | ||||||
| 				Delete(reqToken(), reqOrgOwnership(), org.Delete) | 				Delete(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), org.Delete) | ||||||
| 			m.Combo("/repos").Get(user.ListOrgRepos). | 			m.Combo("/repos").Get(reqToken(auth_model.AccessTokenScopeReadOrg), user.ListOrgRepos). | ||||||
| 				Post(reqToken(), bind(api.CreateRepoOption{}), repo.CreateOrgRepo) | 				Post(reqToken(auth_model.AccessTokenScopeWriteOrg), bind(api.CreateRepoOption{}), repo.CreateOrgRepo) | ||||||
| 			m.Group("/members", func() { | 			m.Group("/members", func() { | ||||||
| 				m.Get("", org.ListMembers) | 				m.Get("", reqToken(auth_model.AccessTokenScopeReadOrg), org.ListMembers) | ||||||
| 				m.Combo("/{username}").Get(org.IsMember). | 				m.Combo("/{username}").Get(reqToken(auth_model.AccessTokenScopeReadOrg), org.IsMember). | ||||||
| 					Delete(reqToken(), reqOrgOwnership(), org.DeleteMember) | 					Delete(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), org.DeleteMember) | ||||||
| 			}) | 			}) | ||||||
| 			m.Group("/public_members", func() { | 			m.Group("/public_members", func() { | ||||||
| 				m.Get("", org.ListPublicMembers) | 				m.Get("", reqToken(auth_model.AccessTokenScopeReadOrg), org.ListPublicMembers) | ||||||
| 				m.Combo("/{username}").Get(org.IsPublicMember). | 				m.Combo("/{username}").Get(reqToken(auth_model.AccessTokenScopeReadOrg), org.IsPublicMember). | ||||||
| 					Put(reqToken(), reqOrgMembership(), org.PublicizeMember). | 					Put(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgMembership(), org.PublicizeMember). | ||||||
| 					Delete(reqToken(), reqOrgMembership(), org.ConcealMember) | 					Delete(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgMembership(), org.ConcealMember) | ||||||
| 			}) | 			}) | ||||||
| 			m.Group("/teams", func() { | 			m.Group("/teams", func() { | ||||||
| 				m.Get("", org.ListTeams) | 				m.Get("", reqToken(auth_model.AccessTokenScopeReadOrg), org.ListTeams) | ||||||
| 				m.Post("", reqOrgOwnership(), bind(api.CreateTeamOption{}), org.CreateTeam) | 				m.Post("", reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), bind(api.CreateTeamOption{}), org.CreateTeam) | ||||||
| 				m.Get("/search", org.SearchTeam) | 				m.Get("/search", reqToken(auth_model.AccessTokenScopeReadOrg), org.SearchTeam) | ||||||
| 			}, reqToken(), reqOrgMembership()) | 			}, reqOrgMembership()) | ||||||
| 			m.Group("/labels", func() { | 			m.Group("/labels", func() { | ||||||
| 				m.Get("", org.ListLabels) | 				m.Get("", reqToken(auth_model.AccessTokenScopeReadOrg), org.ListLabels) | ||||||
| 				m.Post("", reqToken(), reqOrgOwnership(), bind(api.CreateLabelOption{}), org.CreateLabel) | 				m.Post("", reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), bind(api.CreateLabelOption{}), org.CreateLabel) | ||||||
| 				m.Combo("/{id}").Get(org.GetLabel). | 				m.Combo("/{id}").Get(reqToken(auth_model.AccessTokenScopeReadOrg), org.GetLabel). | ||||||
| 					Patch(reqToken(), reqOrgOwnership(), bind(api.EditLabelOption{}), org.EditLabel). | 					Patch(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), bind(api.EditLabelOption{}), org.EditLabel). | ||||||
| 					Delete(reqToken(), reqOrgOwnership(), org.DeleteLabel) | 					Delete(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), org.DeleteLabel) | ||||||
| 			}) | 			}) | ||||||
| 			m.Group("/hooks", func() { | 			m.Group("/hooks", func() { | ||||||
| 				m.Combo("").Get(org.ListHooks). | 				m.Combo("").Get(org.ListHooks). | ||||||
| @@ -1139,27 +1173,27 @@ func Routes(ctx gocontext.Context) *web.Route { | |||||||
| 				m.Combo("/{id}").Get(org.GetHook). | 				m.Combo("/{id}").Get(org.GetHook). | ||||||
| 					Patch(bind(api.EditHookOption{}), org.EditHook). | 					Patch(bind(api.EditHookOption{}), org.EditHook). | ||||||
| 					Delete(org.DeleteHook) | 					Delete(org.DeleteHook) | ||||||
| 			}, reqToken(), reqOrgOwnership(), reqWebhooksEnabled()) | 			}, reqToken(auth_model.AccessTokenScopeAdminOrgHook), reqOrgOwnership(), reqWebhooksEnabled()) | ||||||
| 		}, orgAssignment(true)) | 		}, orgAssignment(true)) | ||||||
| 		m.Group("/teams/{teamid}", func() { | 		m.Group("/teams/{teamid}", func() { | ||||||
| 			m.Combo("").Get(org.GetTeam). | 			m.Combo("").Get(reqToken(auth_model.AccessTokenScopeReadOrg), org.GetTeam). | ||||||
| 				Patch(reqOrgOwnership(), bind(api.EditTeamOption{}), org.EditTeam). | 				Patch(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), bind(api.EditTeamOption{}), org.EditTeam). | ||||||
| 				Delete(reqOrgOwnership(), org.DeleteTeam) | 				Delete(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), org.DeleteTeam) | ||||||
| 			m.Group("/members", func() { | 			m.Group("/members", func() { | ||||||
| 				m.Get("", org.GetTeamMembers) | 				m.Get("", reqToken(auth_model.AccessTokenScopeReadOrg), org.GetTeamMembers) | ||||||
| 				m.Combo("/{username}"). | 				m.Combo("/{username}"). | ||||||
| 					Get(org.GetTeamMember). | 					Get(reqToken(auth_model.AccessTokenScopeReadOrg), org.GetTeamMember). | ||||||
| 					Put(reqOrgOwnership(), org.AddTeamMember). | 					Put(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), org.AddTeamMember). | ||||||
| 					Delete(reqOrgOwnership(), org.RemoveTeamMember) | 					Delete(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), org.RemoveTeamMember) | ||||||
| 			}) | 			}) | ||||||
| 			m.Group("/repos", func() { | 			m.Group("/repos", func() { | ||||||
| 				m.Get("", org.GetTeamRepos) | 				m.Get("", reqToken(auth_model.AccessTokenScopeReadOrg), org.GetTeamRepos) | ||||||
| 				m.Combo("/{org}/{reponame}"). | 				m.Combo("/{org}/{reponame}"). | ||||||
| 					Put(org.AddTeamRepository). | 					Put(reqToken(auth_model.AccessTokenScopeWriteOrg), org.AddTeamRepository). | ||||||
| 					Delete(org.RemoveTeamRepository). | 					Delete(reqToken(auth_model.AccessTokenScopeWriteOrg), org.RemoveTeamRepository). | ||||||
| 					Get(org.GetTeamRepo) | 					Get(reqToken(auth_model.AccessTokenScopeReadOrg), org.GetTeamRepo) | ||||||
| 			}) | 			}) | ||||||
| 		}, orgAssignment(false, true), reqToken(), reqTeamMembership()) | 		}, orgAssignment(false, true), reqToken(""), reqTeamMembership()) | ||||||
|  |  | ||||||
| 		m.Group("/admin", func() { | 		m.Group("/admin", func() { | ||||||
| 			m.Group("/cron", func() { | 			m.Group("/cron", func() { | ||||||
| @@ -1187,7 +1221,7 @@ func Routes(ctx gocontext.Context) *web.Route { | |||||||
| 				m.Post("/{username}/{reponame}", admin.AdoptRepository) | 				m.Post("/{username}/{reponame}", admin.AdoptRepository) | ||||||
| 				m.Delete("/{username}/{reponame}", admin.DeleteUnadoptedRepository) | 				m.Delete("/{username}/{reponame}", admin.DeleteUnadoptedRepository) | ||||||
| 			}) | 			}) | ||||||
| 		}, reqToken(), reqSiteAdmin()) | 		}, reqToken(auth_model.AccessTokenScopeSudo), reqSiteAdmin()) | ||||||
|  |  | ||||||
| 		m.Group("/topics", func() { | 		m.Group("/topics", func() { | ||||||
| 			m.Get("/search", repo.TopicSearch) | 			m.Get("/search", repo.TopicSearch) | ||||||
|   | |||||||
| @@ -42,9 +42,15 @@ func ApplicationsPost(ctx *context.Context) { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	scope, err := form.GetScope() | ||||||
|  | 	if err != nil { | ||||||
|  | 		ctx.ServerError("GetScope", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
| 	t := &auth_model.AccessToken{ | 	t := &auth_model.AccessToken{ | ||||||
| 		UID:   ctx.Doer.ID, | 		UID:   ctx.Doer.ID, | ||||||
| 		Name:  form.Name, | 		Name:  form.Name, | ||||||
|  | 		Scope: scope, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	exist, err := auth_model.AccessTokenByNameExists(t) | 	exist, err := auth_model.AccessTokenByNameExists(t) | ||||||
|   | |||||||
| @@ -59,6 +59,8 @@ func (o *OAuth2) Name() string { | |||||||
| } | } | ||||||
|  |  | ||||||
| // userIDFromToken returns the user id corresponding to the OAuth token. | // userIDFromToken returns the user id corresponding to the OAuth token. | ||||||
|  | // It will set 'IsApiToken' to true if the token is an API token and | ||||||
|  | // set 'ApiTokenScope' to the scope of the access token | ||||||
| func (o *OAuth2) userIDFromToken(req *http.Request, store DataStore) int64 { | func (o *OAuth2) userIDFromToken(req *http.Request, store DataStore) int64 { | ||||||
| 	_ = req.ParseForm() | 	_ = req.ParseForm() | ||||||
|  |  | ||||||
| @@ -86,6 +88,7 @@ func (o *OAuth2) userIDFromToken(req *http.Request, store DataStore) int64 { | |||||||
| 		uid := CheckOAuthAccessToken(tokenSHA) | 		uid := CheckOAuthAccessToken(tokenSHA) | ||||||
| 		if uid != 0 { | 		if uid != 0 { | ||||||
| 			store.GetData()["IsApiToken"] = true | 			store.GetData()["IsApiToken"] = true | ||||||
|  | 			store.GetData()["ApiTokenScope"] = auth_model.AccessTokenScopeAll // fallback to all | ||||||
| 		} | 		} | ||||||
| 		return uid | 		return uid | ||||||
| 	} | 	} | ||||||
| @@ -101,6 +104,7 @@ func (o *OAuth2) userIDFromToken(req *http.Request, store DataStore) int64 { | |||||||
| 		log.Error("UpdateAccessToken: %v", err) | 		log.Error("UpdateAccessToken: %v", err) | ||||||
| 	} | 	} | ||||||
| 	store.GetData()["IsApiToken"] = true | 	store.GetData()["IsApiToken"] = true | ||||||
|  | 	store.GetData()["ApiTokenScope"] = t.Scope | ||||||
| 	return t.UID | 	return t.UID | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/modules/context" | 	"code.gitea.io/gitea/modules/context" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| 	"code.gitea.io/gitea/modules/structs" | 	"code.gitea.io/gitea/modules/structs" | ||||||
| @@ -378,6 +379,7 @@ func (f *AddSecretForm) Validate(req *http.Request, errs binding.Errors) binding | |||||||
| // NewAccessTokenForm form for creating access token | // NewAccessTokenForm form for creating access token | ||||||
| type NewAccessTokenForm struct { | type NewAccessTokenForm struct { | ||||||
| 	Name  string `binding:"Required;MaxSize(255)"` | 	Name  string `binding:"Required;MaxSize(255)"` | ||||||
|  | 	Scope []string | ||||||
| } | } | ||||||
|  |  | ||||||
| // Validate validates the fields | // Validate validates the fields | ||||||
| @@ -386,6 +388,12 @@ func (f *NewAccessTokenForm) Validate(req *http.Request, errs binding.Errors) bi | |||||||
| 	return middleware.Validate(errs, ctx.Data, f, ctx.Locale) | 	return middleware.Validate(errs, ctx.Data, f, ctx.Locale) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (f *NewAccessTokenForm) GetScope() (auth_model.AccessTokenScope, error) { | ||||||
|  | 	scope := strings.Join(f.Scope, ",") | ||||||
|  | 	s, err := auth_model.AccessTokenScope(scope).Normalize() | ||||||
|  | 	return s, err | ||||||
|  | } | ||||||
|  |  | ||||||
| // EditOAuth2ApplicationForm form for editing oauth2 applications | // EditOAuth2ApplicationForm form for editing oauth2 applications | ||||||
| type EditOAuth2ApplicationForm struct { | type EditOAuth2ApplicationForm struct { | ||||||
| 	Name               string `binding:"Required;MaxSize(255)" form:"application_name"` | 	Name               string `binding:"Required;MaxSize(255)" form:"application_name"` | ||||||
|   | |||||||
| @@ -4,8 +4,10 @@ | |||||||
| package forms | package forms | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"strconv" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
|  |  | ||||||
| 	"github.com/stretchr/testify/assert" | 	"github.com/stretchr/testify/assert" | ||||||
| @@ -83,3 +85,28 @@ func TestRegisterForm_IsDomainAllowed_BlocklistedEmail(t *testing.T) { | |||||||
| 		assert.Equal(t, v.valid, form.IsEmailDomainAllowed()) | 		assert.Equal(t, v.valid, form.IsEmailDomainAllowed()) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestNewAccessTokenForm_GetScope(t *testing.T) { | ||||||
|  | 	tests := []struct { | ||||||
|  | 		form        NewAccessTokenForm | ||||||
|  | 		scope       auth_model.AccessTokenScope | ||||||
|  | 		expectedErr error | ||||||
|  | 	}{ | ||||||
|  | 		{ | ||||||
|  | 			form:  NewAccessTokenForm{Name: "test", Scope: []string{"repo"}}, | ||||||
|  | 			scope: "repo", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			form:  NewAccessTokenForm{Name: "test", Scope: []string{"repo", "user"}}, | ||||||
|  | 			scope: "repo,user", | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for i, test := range tests { | ||||||
|  | 		t.Run(strconv.Itoa(i), func(t *testing.T) { | ||||||
|  | 			scope, err := test.form.GetScope() | ||||||
|  | 			assert.Equal(t, test.expectedErr, err) | ||||||
|  | 			assert.Equal(t, test.scope, scope) | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
| @@ -41,6 +41,207 @@ | |||||||
| 					<label for="name">{{.locale.Tr "settings.token_name"}}</label> | 					<label for="name">{{.locale.Tr "settings.token_name"}}</label> | ||||||
| 					<input id="name" name="name" value="{{.name}}" autofocus required> | 					<input id="name" name="name" value="{{.name}}" autofocus required> | ||||||
| 				</div> | 				</div> | ||||||
|  | 				<details class="ui optional field"> | ||||||
|  | 					<summary class="p-2"> | ||||||
|  | 						{{.locale.Tr "settings.select_scopes"}} | ||||||
|  | 					</summary> | ||||||
|  | 					<div class="field pl-2"> | ||||||
|  | 						<div class="ui checkbox"> | ||||||
|  | 							<input class="enable-system" type="checkbox" name="scope" value="repo"> | ||||||
|  | 							<label>repo</label> | ||||||
|  | 						</div> | ||||||
|  | 					</div> | ||||||
|  | 					<div class="field 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 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: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:public_key"> | ||||||
|  | 							<label>admin:public_key</label> | ||||||
|  | 						</div> | ||||||
|  | 					</div> | ||||||
|  | 					<div class="field 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 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="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 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 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 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 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> | ||||||
|  | 				</details> | ||||||
| 				<button class="ui green button"> | 				<button class="ui green button"> | ||||||
| 					{{.locale.Tr "settings.generate_token"}} | 					{{.locale.Tr "settings.generate_token"}} | ||||||
| 				</button> | 				</button> | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ import ( | |||||||
| 	"strings" | 	"strings" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	api "code.gitea.io/gitea/modules/structs" | 	api "code.gitea.io/gitea/modules/structs" | ||||||
| @@ -20,7 +21,7 @@ import ( | |||||||
| func TestAPIAdminOrgCreate(t *testing.T) { | func TestAPIAdminOrgCreate(t *testing.T) { | ||||||
| 	onGiteaRun(t, func(*testing.T, *url.URL) { | 	onGiteaRun(t, func(*testing.T, *url.URL) { | ||||||
| 		session := loginUser(t, "user1") | 		session := loginUser(t, "user1") | ||||||
| 		token := getTokenForLoggedInUser(t, session) | 		token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeSudo) | ||||||
|  |  | ||||||
| 		org := api.CreateOrgOption{ | 		org := api.CreateOrgOption{ | ||||||
| 			UserName:    "user2_org", | 			UserName:    "user2_org", | ||||||
| @@ -54,7 +55,7 @@ func TestAPIAdminOrgCreate(t *testing.T) { | |||||||
| func TestAPIAdminOrgCreateBadVisibility(t *testing.T) { | func TestAPIAdminOrgCreateBadVisibility(t *testing.T) { | ||||||
| 	onGiteaRun(t, func(*testing.T, *url.URL) { | 	onGiteaRun(t, func(*testing.T, *url.URL) { | ||||||
| 		session := loginUser(t, "user1") | 		session := loginUser(t, "user1") | ||||||
| 		token := getTokenForLoggedInUser(t, session) | 		token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeSudo) | ||||||
|  |  | ||||||
| 		org := api.CreateOrgOption{ | 		org := api.CreateOrgOption{ | ||||||
| 			UserName:    "user2_org", | 			UserName:    "user2_org", | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ import ( | |||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	asymkey_model "code.gitea.io/gitea/models/asymkey" | 	asymkey_model "code.gitea.io/gitea/models/asymkey" | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/json" | 	"code.gitea.io/gitea/modules/json" | ||||||
| @@ -24,7 +25,7 @@ func TestAPIAdminCreateAndDeleteSSHKey(t *testing.T) { | |||||||
| 	session := loginUser(t, "user1") | 	session := loginUser(t, "user1") | ||||||
| 	keyOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user2"}) | 	keyOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user2"}) | ||||||
|  |  | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeSudo) | ||||||
| 	urlStr := fmt.Sprintf("/api/v1/admin/users/%s/keys?token=%s", keyOwner.Name, token) | 	urlStr := fmt.Sprintf("/api/v1/admin/users/%s/keys?token=%s", keyOwner.Name, token) | ||||||
| 	req := NewRequestWithValues(t, "POST", urlStr, map[string]string{ | 	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", | 		"key":   "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC4cn+iXnA4KvcQYSV88vGn0Yi91vG47t1P7okprVmhNTkipNRIHWr6WdCO4VDr/cvsRkuVJAsLO2enwjGWWueOO6BodiBgyAOZ/5t5nJNMCNuLGT5UIo/RI1b0WRQwxEZTRjt6mFNw6lH14wRd8ulsr9toSWBPMOGWoYs1PDeDL0JuTjL+tr1SZi/EyxCngpYszKdXllJEHyI79KQgeD0Vt3pTrkbNVTOEcCNqZePSVmUH8X8Vhugz3bnE0/iE9Pb5fkWO9c4AnM1FgI/8Bvp27Fw2ShryIXuR6kKvUqhVMTuOSDHwu6A8jLE5Owt3GAYugDpDYuwTVNGrHLXKpPzrGGPE/jPmaLCMZcsdkec95dYeU3zKODEm8UQZFhmJmDeWVJ36nGrGZHL4J5aTTaeFUJmmXDaJYiJ+K2/ioKgXqnXvltu0A9R8/LGy4nrTJRr4JMLuJFoUXvGm1gXQ70w2LSpk6yl71RNC0hCtsBe8BP8IhYCM0EP5jh7eCMQZNvM= nocomment\n", | ||||||
| @@ -51,7 +52,7 @@ func TestAPIAdminDeleteMissingSSHKey(t *testing.T) { | |||||||
| 	defer tests.PrepareTestEnv(t)() | 	defer tests.PrepareTestEnv(t)() | ||||||
|  |  | ||||||
| 	// user1 is an admin user | 	// user1 is an admin user | ||||||
| 	token := getUserToken(t, "user1") | 	token := getUserToken(t, "user1", auth_model.AccessTokenScopeSudo) | ||||||
| 	req := NewRequestf(t, "DELETE", "/api/v1/admin/users/user1/keys/%d?token=%s", unittest.NonexistentID, token) | 	req := NewRequestf(t, "DELETE", "/api/v1/admin/users/user1/keys/%d?token=%s", unittest.NonexistentID, token) | ||||||
| 	MakeRequest(t, req, http.StatusNotFound) | 	MakeRequest(t, req, http.StatusNotFound) | ||||||
| } | } | ||||||
| @@ -60,7 +61,7 @@ func TestAPIAdminDeleteUnauthorizedKey(t *testing.T) { | |||||||
| 	defer tests.PrepareTestEnv(t)() | 	defer tests.PrepareTestEnv(t)() | ||||||
| 	adminUsername := "user1" | 	adminUsername := "user1" | ||||||
| 	normalUsername := "user2" | 	normalUsername := "user2" | ||||||
| 	token := getUserToken(t, adminUsername) | 	token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeSudo) | ||||||
|  |  | ||||||
| 	urlStr := fmt.Sprintf("/api/v1/admin/users/%s/keys?token=%s", adminUsername, token) | 	urlStr := fmt.Sprintf("/api/v1/admin/users/%s/keys?token=%s", adminUsername, token) | ||||||
| 	req := NewRequestWithValues(t, "POST", urlStr, map[string]string{ | 	req := NewRequestWithValues(t, "POST", urlStr, map[string]string{ | ||||||
| @@ -81,7 +82,7 @@ func TestAPISudoUser(t *testing.T) { | |||||||
| 	defer tests.PrepareTestEnv(t)() | 	defer tests.PrepareTestEnv(t)() | ||||||
| 	adminUsername := "user1" | 	adminUsername := "user1" | ||||||
| 	normalUsername := "user2" | 	normalUsername := "user2" | ||||||
| 	token := getUserToken(t, adminUsername) | 	token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeSudo) | ||||||
|  |  | ||||||
| 	urlStr := fmt.Sprintf("/api/v1/user?sudo=%s&token=%s", normalUsername, token) | 	urlStr := fmt.Sprintf("/api/v1/user?sudo=%s&token=%s", normalUsername, token) | ||||||
| 	req := NewRequest(t, "GET", urlStr) | 	req := NewRequest(t, "GET", urlStr) | ||||||
| @@ -97,7 +98,7 @@ func TestAPISudoUserForbidden(t *testing.T) { | |||||||
| 	adminUsername := "user1" | 	adminUsername := "user1" | ||||||
| 	normalUsername := "user2" | 	normalUsername := "user2" | ||||||
|  |  | ||||||
| 	token := getUserToken(t, normalUsername) | 	token := getUserToken(t, normalUsername, auth_model.AccessTokenScopeSudo) | ||||||
| 	urlStr := fmt.Sprintf("/api/v1/user?sudo=%s&token=%s", adminUsername, token) | 	urlStr := fmt.Sprintf("/api/v1/user?sudo=%s&token=%s", adminUsername, token) | ||||||
| 	req := NewRequest(t, "GET", urlStr) | 	req := NewRequest(t, "GET", urlStr) | ||||||
| 	MakeRequest(t, req, http.StatusForbidden) | 	MakeRequest(t, req, http.StatusForbidden) | ||||||
| @@ -106,7 +107,7 @@ func TestAPISudoUserForbidden(t *testing.T) { | |||||||
| func TestAPIListUsers(t *testing.T) { | func TestAPIListUsers(t *testing.T) { | ||||||
| 	defer tests.PrepareTestEnv(t)() | 	defer tests.PrepareTestEnv(t)() | ||||||
| 	adminUsername := "user1" | 	adminUsername := "user1" | ||||||
| 	token := getUserToken(t, adminUsername) | 	token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeSudo) | ||||||
|  |  | ||||||
| 	urlStr := fmt.Sprintf("/api/v1/admin/users?token=%s", token) | 	urlStr := fmt.Sprintf("/api/v1/admin/users?token=%s", token) | ||||||
| 	req := NewRequest(t, "GET", urlStr) | 	req := NewRequest(t, "GET", urlStr) | ||||||
| @@ -142,7 +143,7 @@ func TestAPIListUsersNonAdmin(t *testing.T) { | |||||||
| func TestAPICreateUserInvalidEmail(t *testing.T) { | func TestAPICreateUserInvalidEmail(t *testing.T) { | ||||||
| 	defer tests.PrepareTestEnv(t)() | 	defer tests.PrepareTestEnv(t)() | ||||||
| 	adminUsername := "user1" | 	adminUsername := "user1" | ||||||
| 	token := getUserToken(t, adminUsername) | 	token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeSudo) | ||||||
| 	urlStr := fmt.Sprintf("/api/v1/admin/users?token=%s", token) | 	urlStr := fmt.Sprintf("/api/v1/admin/users?token=%s", token) | ||||||
| 	req := NewRequestWithValues(t, "POST", urlStr, map[string]string{ | 	req := NewRequestWithValues(t, "POST", urlStr, map[string]string{ | ||||||
| 		"email":                "invalid_email@domain.com\r\n", | 		"email":                "invalid_email@domain.com\r\n", | ||||||
| @@ -160,7 +161,7 @@ func TestAPICreateUserInvalidEmail(t *testing.T) { | |||||||
| func TestAPICreateAndDeleteUser(t *testing.T) { | func TestAPICreateAndDeleteUser(t *testing.T) { | ||||||
| 	defer tests.PrepareTestEnv(t)() | 	defer tests.PrepareTestEnv(t)() | ||||||
| 	adminUsername := "user1" | 	adminUsername := "user1" | ||||||
| 	token := getUserToken(t, adminUsername) | 	token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeSudo) | ||||||
|  |  | ||||||
| 	req := NewRequestWithValues( | 	req := NewRequestWithValues( | ||||||
| 		t, | 		t, | ||||||
| @@ -186,7 +187,7 @@ func TestAPICreateAndDeleteUser(t *testing.T) { | |||||||
| func TestAPIEditUser(t *testing.T) { | func TestAPIEditUser(t *testing.T) { | ||||||
| 	defer tests.PrepareTestEnv(t)() | 	defer tests.PrepareTestEnv(t)() | ||||||
| 	adminUsername := "user1" | 	adminUsername := "user1" | ||||||
| 	token := getUserToken(t, adminUsername) | 	token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeSudo) | ||||||
| 	urlStr := fmt.Sprintf("/api/v1/admin/users/%s?token=%s", "user2", token) | 	urlStr := fmt.Sprintf("/api/v1/admin/users/%s?token=%s", "user2", token) | ||||||
|  |  | ||||||
| 	req := NewRequestWithValues(t, "PATCH", urlStr, map[string]string{ | 	req := NewRequestWithValues(t, "PATCH", urlStr, map[string]string{ | ||||||
| @@ -228,7 +229,7 @@ func TestAPIEditUser(t *testing.T) { | |||||||
| func TestAPICreateRepoForUser(t *testing.T) { | func TestAPICreateRepoForUser(t *testing.T) { | ||||||
| 	defer tests.PrepareTestEnv(t)() | 	defer tests.PrepareTestEnv(t)() | ||||||
| 	adminUsername := "user1" | 	adminUsername := "user1" | ||||||
| 	token := getUserToken(t, adminUsername) | 	token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeSudo) | ||||||
|  |  | ||||||
| 	req := NewRequestWithJSON( | 	req := NewRequestWithJSON( | ||||||
| 		t, | 		t, | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ import ( | |||||||
| 	"net/url" | 	"net/url" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	api "code.gitea.io/gitea/modules/structs" | 	api "code.gitea.io/gitea/modules/structs" | ||||||
| 	"code.gitea.io/gitea/tests" | 	"code.gitea.io/gitea/tests" | ||||||
|  |  | ||||||
| @@ -15,7 +16,7 @@ import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| func testAPIGetBranch(t *testing.T, branchName string, exists bool) { | func testAPIGetBranch(t *testing.T, branchName string, exists bool) { | ||||||
| 	token := getUserToken(t, "user2") | 	token := getUserToken(t, "user2", auth_model.AccessTokenScopeRepo) | ||||||
| 	req := NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/branches/%s?token=%s", branchName, token) | 	req := NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/branches/%s?token=%s", branchName, token) | ||||||
| 	resp := MakeRequest(t, req, NoExpectedStatus) | 	resp := MakeRequest(t, req, NoExpectedStatus) | ||||||
| 	if !exists { | 	if !exists { | ||||||
| @@ -31,7 +32,7 @@ func testAPIGetBranch(t *testing.T, branchName string, exists bool) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func testAPIGetBranchProtection(t *testing.T, branchName string, expectedHTTPStatus int) { | func testAPIGetBranchProtection(t *testing.T, branchName string, expectedHTTPStatus int) { | ||||||
| 	token := getUserToken(t, "user2") | 	token := getUserToken(t, "user2", auth_model.AccessTokenScopeRepo) | ||||||
| 	req := NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/branch_protections/%s?token=%s", branchName, token) | 	req := NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/branch_protections/%s?token=%s", branchName, token) | ||||||
| 	resp := MakeRequest(t, req, expectedHTTPStatus) | 	resp := MakeRequest(t, req, expectedHTTPStatus) | ||||||
|  |  | ||||||
| @@ -43,7 +44,7 @@ func testAPIGetBranchProtection(t *testing.T, branchName string, expectedHTTPSta | |||||||
| } | } | ||||||
|  |  | ||||||
| func testAPICreateBranchProtection(t *testing.T, branchName string, expectedHTTPStatus int) { | func testAPICreateBranchProtection(t *testing.T, branchName string, expectedHTTPStatus int) { | ||||||
| 	token := getUserToken(t, "user2") | 	token := getUserToken(t, "user2", auth_model.AccessTokenScopeRepo) | ||||||
| 	req := NewRequestWithJSON(t, "POST", "/api/v1/repos/user2/repo1/branch_protections?token="+token, &api.BranchProtection{ | 	req := NewRequestWithJSON(t, "POST", "/api/v1/repos/user2/repo1/branch_protections?token="+token, &api.BranchProtection{ | ||||||
| 		RuleName: branchName, | 		RuleName: branchName, | ||||||
| 	}) | 	}) | ||||||
| @@ -57,7 +58,7 @@ func testAPICreateBranchProtection(t *testing.T, branchName string, expectedHTTP | |||||||
| } | } | ||||||
|  |  | ||||||
| func testAPIEditBranchProtection(t *testing.T, branchName string, body *api.BranchProtection, expectedHTTPStatus int) { | func testAPIEditBranchProtection(t *testing.T, branchName string, body *api.BranchProtection, expectedHTTPStatus int) { | ||||||
| 	token := getUserToken(t, "user2") | 	token := getUserToken(t, "user2", auth_model.AccessTokenScopeRepo) | ||||||
| 	req := NewRequestWithJSON(t, "PATCH", "/api/v1/repos/user2/repo1/branch_protections/"+branchName+"?token="+token, body) | 	req := NewRequestWithJSON(t, "PATCH", "/api/v1/repos/user2/repo1/branch_protections/"+branchName+"?token="+token, body) | ||||||
| 	resp := MakeRequest(t, req, expectedHTTPStatus) | 	resp := MakeRequest(t, req, expectedHTTPStatus) | ||||||
|  |  | ||||||
| @@ -69,13 +70,13 @@ func testAPIEditBranchProtection(t *testing.T, branchName string, body *api.Bran | |||||||
| } | } | ||||||
|  |  | ||||||
| func testAPIDeleteBranchProtection(t *testing.T, branchName string, expectedHTTPStatus int) { | func testAPIDeleteBranchProtection(t *testing.T, branchName string, expectedHTTPStatus int) { | ||||||
| 	token := getUserToken(t, "user2") | 	token := getUserToken(t, "user2", auth_model.AccessTokenScopeRepo) | ||||||
| 	req := NewRequestf(t, "DELETE", "/api/v1/repos/user2/repo1/branch_protections/%s?token=%s", branchName, token) | 	req := NewRequestf(t, "DELETE", "/api/v1/repos/user2/repo1/branch_protections/%s?token=%s", branchName, token) | ||||||
| 	MakeRequest(t, req, expectedHTTPStatus) | 	MakeRequest(t, req, expectedHTTPStatus) | ||||||
| } | } | ||||||
|  |  | ||||||
| func testAPIDeleteBranch(t *testing.T, branchName string, expectedHTTPStatus int) { | func testAPIDeleteBranch(t *testing.T, branchName string, expectedHTTPStatus int) { | ||||||
| 	token := getUserToken(t, "user2") | 	token := getUserToken(t, "user2", auth_model.AccessTokenScopeRepo) | ||||||
| 	req := NewRequestf(t, "DELETE", "/api/v1/repos/user2/repo1/branches/%s?token=%s", branchName, token) | 	req := NewRequestf(t, "DELETE", "/api/v1/repos/user2/repo1/branches/%s?token=%s", branchName, token) | ||||||
| 	MakeRequest(t, req, expectedHTTPStatus) | 	MakeRequest(t, req, expectedHTTPStatus) | ||||||
| } | } | ||||||
| @@ -101,7 +102,7 @@ func TestAPICreateBranch(t *testing.T) { | |||||||
|  |  | ||||||
| func testAPICreateBranches(t *testing.T, giteaURL *url.URL) { | func testAPICreateBranches(t *testing.T, giteaURL *url.URL) { | ||||||
| 	username := "user2" | 	username := "user2" | ||||||
| 	ctx := NewAPITestContext(t, username, "my-noo-repo") | 	ctx := NewAPITestContext(t, username, "my-noo-repo", auth_model.AccessTokenScopeRepo) | ||||||
| 	giteaURL.Path = ctx.GitPath() | 	giteaURL.Path = ctx.GitPath() | ||||||
|  |  | ||||||
| 	t.Run("CreateRepo", doAPICreateRepository(ctx, false)) | 	t.Run("CreateRepo", doAPICreateRepository(ctx, false)) | ||||||
| @@ -149,7 +150,7 @@ func testAPICreateBranches(t *testing.T, giteaURL *url.URL) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func testAPICreateBranch(t testing.TB, session *TestSession, user, repo, oldBranch, newBranch string, status int) bool { | func testAPICreateBranch(t testing.TB, session *TestSession, user, repo, oldBranch, newBranch string, status int) bool { | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
| 	req := NewRequestWithJSON(t, "POST", "/api/v1/repos/"+user+"/"+repo+"/branches?token="+token, &api.CreateBranchRepoOption{ | 	req := NewRequestWithJSON(t, "POST", "/api/v1/repos/"+user+"/"+repo+"/branches?token="+token, &api.CreateBranchRepoOption{ | ||||||
| 		BranchName:    newBranch, | 		BranchName:    newBranch, | ||||||
| 		OldBranchName: oldBranch, | 		OldBranchName: oldBranch, | ||||||
|   | |||||||
| @@ -12,6 +12,7 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	issues_model "code.gitea.io/gitea/models/issues" | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| @@ -81,7 +82,7 @@ func TestAPICreateCommentAttachment(t *testing.T) { | |||||||
| 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | ||||||
|  |  | ||||||
| 	session := loginUser(t, repoOwner.Name) | 	session := loginUser(t, repoOwner.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
| 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/comments/%d/assets?token=%s", | 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/comments/%d/assets?token=%s", | ||||||
| 		repoOwner.Name, repo.Name, comment.ID, token) | 		repoOwner.Name, repo.Name, comment.ID, token) | ||||||
|  |  | ||||||
| @@ -120,7 +121,7 @@ func TestAPIEditCommentAttachment(t *testing.T) { | |||||||
| 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | ||||||
|  |  | ||||||
| 	session := loginUser(t, repoOwner.Name) | 	session := loginUser(t, repoOwner.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
| 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/comments/%d/assets/%d?token=%s", | 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/comments/%d/assets/%d?token=%s", | ||||||
| 		repoOwner.Name, repo.Name, comment.ID, attachment.ID, token) | 		repoOwner.Name, repo.Name, comment.ID, attachment.ID, token) | ||||||
| 	req := NewRequestWithValues(t, "PATCH", urlStr, map[string]string{ | 	req := NewRequestWithValues(t, "PATCH", urlStr, map[string]string{ | ||||||
| @@ -143,7 +144,7 @@ func TestAPIDeleteCommentAttachment(t *testing.T) { | |||||||
| 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | ||||||
|  |  | ||||||
| 	session := loginUser(t, repoOwner.Name) | 	session := loginUser(t, repoOwner.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
| 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/comments/%d/assets/%d?token=%s", | 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/comments/%d/assets/%d?token=%s", | ||||||
| 		repoOwner.Name, repo.Name, comment.ID, attachment.ID, token) | 		repoOwner.Name, repo.Name, comment.ID, attachment.ID, token) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ import ( | |||||||
| 	"net/url" | 	"net/url" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	issues_model "code.gitea.io/gitea/models/issues" | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| @@ -75,8 +76,9 @@ func TestAPIListIssueComments(t *testing.T) { | |||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}) | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}) | ||||||
| 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | ||||||
|  |  | ||||||
| 	req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/issues/%d/comments", | 	token := getUserToken(t, repoOwner.Name, auth_model.AccessTokenScopeRepo) | ||||||
| 		repoOwner.Name, repo.Name, issue.Index) | 	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) | 	resp := MakeRequest(t, req, http.StatusOK) | ||||||
|  |  | ||||||
| 	var comments []*api.Comment | 	var comments []*api.Comment | ||||||
| @@ -94,7 +96,7 @@ func TestAPICreateComment(t *testing.T) { | |||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}) | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}) | ||||||
| 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | ||||||
|  |  | ||||||
| 	token := getUserToken(t, repoOwner.Name) | 	token := getUserToken(t, repoOwner.Name, auth_model.AccessTokenScopeRepo) | ||||||
| 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/comments?token=%s", | 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/comments?token=%s", | ||||||
| 		repoOwner.Name, repo.Name, issue.Index, token) | 		repoOwner.Name, repo.Name, issue.Index, token) | ||||||
| 	req := NewRequestWithValues(t, "POST", urlStr, map[string]string{ | 	req := NewRequestWithValues(t, "POST", urlStr, map[string]string{ | ||||||
| @@ -116,7 +118,7 @@ func TestAPIGetComment(t *testing.T) { | |||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: comment.Issue.RepoID}) | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: comment.Issue.RepoID}) | ||||||
| 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | ||||||
|  |  | ||||||
| 	token := getUserToken(t, repoOwner.Name) | 	token := getUserToken(t, repoOwner.Name, auth_model.AccessTokenScopeRepo) | ||||||
| 	req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/issues/comments/%d", repoOwner.Name, repo.Name, comment.ID) | 	req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/issues/comments/%d", repoOwner.Name, repo.Name, comment.ID) | ||||||
| 	MakeRequest(t, req, http.StatusOK) | 	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) | 	req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/issues/comments/%d?token=%s", repoOwner.Name, repo.Name, comment.ID, token) | ||||||
| @@ -144,7 +146,7 @@ func TestAPIEditComment(t *testing.T) { | |||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}) | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}) | ||||||
| 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | ||||||
|  |  | ||||||
| 	token := getUserToken(t, repoOwner.Name) | 	token := getUserToken(t, repoOwner.Name, auth_model.AccessTokenScopeRepo) | ||||||
| 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/comments/%d?token=%s", | 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/comments/%d?token=%s", | ||||||
| 		repoOwner.Name, repo.Name, comment.ID, token) | 		repoOwner.Name, repo.Name, comment.ID, token) | ||||||
| 	req := NewRequestWithValues(t, "PATCH", urlStr, map[string]string{ | 	req := NewRequestWithValues(t, "PATCH", urlStr, map[string]string{ | ||||||
| @@ -168,7 +170,7 @@ func TestAPIDeleteComment(t *testing.T) { | |||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}) | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}) | ||||||
| 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | ||||||
|  |  | ||||||
| 	token := getUserToken(t, repoOwner.Name) | 	token := getUserToken(t, repoOwner.Name, auth_model.AccessTokenScopeRepo) | ||||||
| 	req := NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/issues/comments/%d?token=%s", | 	req := NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/issues/comments/%d?token=%s", | ||||||
| 		repoOwner.Name, repo.Name, comment.ID, token) | 		repoOwner.Name, repo.Name, comment.ID, token) | ||||||
| 	MakeRequest(t, req, http.StatusNoContent) | 	MakeRequest(t, req, http.StatusNoContent) | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ import ( | |||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	api "code.gitea.io/gitea/modules/structs" | 	api "code.gitea.io/gitea/modules/structs" | ||||||
| 	"code.gitea.io/gitea/tests" | 	"code.gitea.io/gitea/tests" | ||||||
|  |  | ||||||
| @@ -20,7 +21,8 @@ type makeRequestFunc func(testing.TB, *http.Request, int) *httptest.ResponseReco | |||||||
| func TestGPGKeys(t *testing.T) { | func TestGPGKeys(t *testing.T) { | ||||||
| 	defer tests.PrepareTestEnv(t)() | 	defer tests.PrepareTestEnv(t)() | ||||||
| 	session := loginUser(t, "user2") | 	session := loginUser(t, "user2") | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
|  | 	tokenWithGPGKeyScope := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAdminGPGKey, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 	tt := []struct { | 	tt := []struct { | ||||||
| 		name        string | 		name        string | ||||||
| @@ -34,6 +36,10 @@ func TestGPGKeys(t *testing.T) { | |||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name: "LoggedAsUser2", makeRequest: session.MakeRequest, token: token, | 			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}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "LoggedAsUser2WithScope", makeRequest: session.MakeRequest, token: tokenWithGPGKeyScope, | ||||||
| 			results: []int{http.StatusOK, http.StatusOK, http.StatusNotFound, http.StatusNoContent, http.StatusUnprocessableEntity, http.StatusNotFound, http.StatusCreated, http.StatusNotFound, http.StatusCreated}, | 			results: []int{http.StatusOK, http.StatusOK, http.StatusNotFound, http.StatusNoContent, http.StatusUnprocessableEntity, http.StatusNotFound, http.StatusCreated, http.StatusNotFound, http.StatusCreated}, | ||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
| @@ -73,7 +79,7 @@ func TestGPGKeys(t *testing.T) { | |||||||
| 	t.Run("CheckState", func(t *testing.T) { | 	t.Run("CheckState", func(t *testing.T) { | ||||||
| 		var keys []*api.GPGKey | 		var keys []*api.GPGKey | ||||||
|  |  | ||||||
| 		req := NewRequest(t, "GET", "/api/v1/user/gpg_keys?token="+token) // GET all keys | 		req := NewRequest(t, "GET", "/api/v1/user/gpg_keys?token="+tokenWithGPGKeyScope) // GET all keys | ||||||
| 		resp := MakeRequest(t, req, http.StatusOK) | 		resp := MakeRequest(t, req, http.StatusOK) | ||||||
| 		DecodeJSON(t, resp, &keys) | 		DecodeJSON(t, resp, &keys) | ||||||
| 		assert.Len(t, keys, 1) | 		assert.Len(t, keys, 1) | ||||||
| @@ -89,7 +95,7 @@ func TestGPGKeys(t *testing.T) { | |||||||
| 		assert.Empty(t, subKey.Emails) | 		assert.Empty(t, subKey.Emails) | ||||||
|  |  | ||||||
| 		var key api.GPGKey | 		var key api.GPGKey | ||||||
| 		req = NewRequest(t, "GET", "/api/v1/user/gpg_keys/"+strconv.FormatInt(primaryKey1.ID, 10)+"?token="+token) // Primary key 1 | 		req = NewRequest(t, "GET", "/api/v1/user/gpg_keys/"+strconv.FormatInt(primaryKey1.ID, 10)+"?token="+tokenWithGPGKeyScope) // Primary key 1 | ||||||
| 		resp = MakeRequest(t, req, http.StatusOK) | 		resp = MakeRequest(t, req, http.StatusOK) | ||||||
| 		DecodeJSON(t, resp, &key) | 		DecodeJSON(t, resp, &key) | ||||||
| 		assert.EqualValues(t, "38EA3BCED732982C", key.KeyID) | 		assert.EqualValues(t, "38EA3BCED732982C", key.KeyID) | ||||||
| @@ -97,7 +103,7 @@ func TestGPGKeys(t *testing.T) { | |||||||
| 		assert.EqualValues(t, "user2@example.com", key.Emails[0].Email) | 		assert.EqualValues(t, "user2@example.com", key.Emails[0].Email) | ||||||
| 		assert.True(t, key.Emails[0].Verified) | 		assert.True(t, key.Emails[0].Verified) | ||||||
|  |  | ||||||
| 		req = NewRequest(t, "GET", "/api/v1/user/gpg_keys/"+strconv.FormatInt(subKey.ID, 10)+"?token="+token) // Subkey of 38EA3BCED732982C | 		req = NewRequest(t, "GET", "/api/v1/user/gpg_keys/"+strconv.FormatInt(subKey.ID, 10)+"?token="+tokenWithGPGKeyScope) // Subkey of 38EA3BCED732982C | ||||||
| 		resp = MakeRequest(t, req, http.StatusOK) | 		resp = MakeRequest(t, req, http.StatusOK) | ||||||
| 		DecodeJSON(t, resp, &key) | 		DecodeJSON(t, resp, &key) | ||||||
| 		assert.EqualValues(t, "70D7C694D17D03AD", key.KeyID) | 		assert.EqualValues(t, "70D7C694D17D03AD", key.KeyID) | ||||||
|   | |||||||
| @@ -13,6 +13,7 @@ import ( | |||||||
| 	"testing" | 	"testing" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
|  | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/perm" | 	"code.gitea.io/gitea/models/perm" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/modules/json" | 	"code.gitea.io/gitea/modules/json" | ||||||
| @@ -31,9 +32,9 @@ type APITestContext struct { | |||||||
| 	ExpectedCode int | 	ExpectedCode int | ||||||
| } | } | ||||||
|  |  | ||||||
| func NewAPITestContext(t *testing.T, username, reponame string) APITestContext { | func NewAPITestContext(t *testing.T, username, reponame string, scope ...auth.AccessTokenScope) APITestContext { | ||||||
| 	session := loginUser(t, username) | 	session := loginUser(t, username) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, scope...) | ||||||
| 	return APITestContext{ | 	return APITestContext{ | ||||||
| 		Session:  session, | 		Session:  session, | ||||||
| 		Token:    token, | 		Token:    token, | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ import ( | |||||||
| 	"net/url" | 	"net/url" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	api "code.gitea.io/gitea/modules/structs" | 	api "code.gitea.io/gitea/modules/structs" | ||||||
| 	"code.gitea.io/gitea/tests" | 	"code.gitea.io/gitea/tests" | ||||||
|  |  | ||||||
| @@ -52,7 +53,7 @@ func TestHTTPSigPubKey(t *testing.T) { | |||||||
| 	// Add our public key to user1 | 	// Add our public key to user1 | ||||||
| 	defer tests.PrepareTestEnv(t)() | 	defer tests.PrepareTestEnv(t)() | ||||||
| 	session := loginUser(t, "user1") | 	session := loginUser(t, "user1") | ||||||
| 	token := url.QueryEscape(getTokenForLoggedInUser(t, session)) | 	token := url.QueryEscape(getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAdminPublicKey, auth_model.AccessTokenScopeSudo)) | ||||||
| 	keysURL := fmt.Sprintf("/api/v1/user/keys?token=%s", token) | 	keysURL := fmt.Sprintf("/api/v1/user/keys?token=%s", token) | ||||||
| 	keyType := "ssh-rsa" | 	keyType := "ssh-rsa" | ||||||
| 	keyContent := "AAAAB3NzaC1yc2EAAAADAQABAAABAQCqOZB5vkRvXFXups1/0StDRdG8plbNSwsWEnNnP4Bvurxa0+z3W9B8GLKnDiLw5MbpbMNyBlpXw13GfuIeciy10DWTz0xUbiy3J3KabCaT36asIw2y7k6Z0jL0UBnrVENwq5/lUbZYqSZ4rRU744wkhh8TULpzM14npQCZwg6aEbG+MwjzddQ72fR+3BPBrKn5dTmmu8rH99O+U+Nuto81Tg7PA+NUupcHOmhdiEGq49plgVFXK98Vks5tiybL4GuzFyWgyX73Dg/QBMn2eMHt1EMv5Gs3i6GFhKKGo4rjDi9qI6PX5oDR4LTNe6cR8td8YhVD8WFZwLLl/vaYyIqd" | 	keyContent := "AAAAB3NzaC1yc2EAAAADAQABAAABAQCqOZB5vkRvXFXups1/0StDRdG8plbNSwsWEnNnP4Bvurxa0+z3W9B8GLKnDiLw5MbpbMNyBlpXw13GfuIeciy10DWTz0xUbiy3J3KabCaT36asIw2y7k6Z0jL0UBnrVENwq5/lUbZYqSZ4rRU744wkhh8TULpzM14npQCZwg6aEbG+MwjzddQ72fR+3BPBrKn5dTmmu8rH99O+U+Nuto81Tg7PA+NUupcHOmhdiEGq49plgVFXK98Vks5tiybL4GuzFyWgyX73Dg/QBMn2eMHt1EMv5Gs3i6GFhKKGo4rjDi9qI6PX5oDR4LTNe6cR8td8YhVD8WFZwLLl/vaYyIqd" | ||||||
|   | |||||||
| @@ -12,6 +12,7 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	issues_model "code.gitea.io/gitea/models/issues" | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| @@ -72,7 +73,7 @@ func TestAPICreateIssueAttachment(t *testing.T) { | |||||||
| 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | ||||||
|  |  | ||||||
| 	session := loginUser(t, repoOwner.Name) | 	session := loginUser(t, repoOwner.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
| 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/assets?token=%s", | 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/assets?token=%s", | ||||||
| 		repoOwner.Name, repo.Name, issue.Index, token) | 		repoOwner.Name, repo.Name, issue.Index, token) | ||||||
|  |  | ||||||
| @@ -110,7 +111,7 @@ func TestAPIEditIssueAttachment(t *testing.T) { | |||||||
| 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | ||||||
|  |  | ||||||
| 	session := loginUser(t, repoOwner.Name) | 	session := loginUser(t, repoOwner.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
| 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/assets/%d?token=%s", | 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/assets/%d?token=%s", | ||||||
| 		repoOwner.Name, repo.Name, issue.Index, attachment.ID, token) | 		repoOwner.Name, repo.Name, issue.Index, attachment.ID, token) | ||||||
| 	req := NewRequestWithValues(t, "PATCH", urlStr, map[string]string{ | 	req := NewRequestWithValues(t, "PATCH", urlStr, map[string]string{ | ||||||
| @@ -132,7 +133,7 @@ func TestAPIDeleteIssueAttachment(t *testing.T) { | |||||||
| 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | ||||||
|  |  | ||||||
| 	session := loginUser(t, repoOwner.Name) | 	session := loginUser(t, repoOwner.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
| 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/assets/%d?token=%s", | 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/assets/%d?token=%s", | ||||||
| 		repoOwner.Name, repo.Name, issue.Index, attachment.ID, token) | 		repoOwner.Name, repo.Name, issue.Index, attachment.ID, token) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ import ( | |||||||
| 	"strings" | 	"strings" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	issues_model "code.gitea.io/gitea/models/issues" | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| @@ -24,7 +25,7 @@ func TestAPIModifyLabels(t *testing.T) { | |||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) | ||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | ||||||
| 	session := loginUser(t, owner.Name) | 	session := loginUser(t, owner.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
| 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/labels?token=%s", owner.Name, repo.Name, token) | 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/labels?token=%s", owner.Name, repo.Name, token) | ||||||
|  |  | ||||||
| 	// CreateLabel | 	// CreateLabel | ||||||
| @@ -96,7 +97,7 @@ func TestAPIAddIssueLabels(t *testing.T) { | |||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | ||||||
|  |  | ||||||
| 	session := loginUser(t, owner.Name) | 	session := loginUser(t, owner.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
| 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/labels?token=%s", | 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/labels?token=%s", | ||||||
| 		repo.OwnerName, repo.Name, issue.Index, token) | 		repo.OwnerName, repo.Name, issue.Index, token) | ||||||
| 	req := NewRequestWithJSON(t, "POST", urlStr, &api.IssueLabelsOption{ | 	req := NewRequestWithJSON(t, "POST", urlStr, &api.IssueLabelsOption{ | ||||||
| @@ -119,7 +120,7 @@ func TestAPIReplaceIssueLabels(t *testing.T) { | |||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | ||||||
|  |  | ||||||
| 	session := loginUser(t, owner.Name) | 	session := loginUser(t, owner.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
| 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/labels?token=%s", | 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/labels?token=%s", | ||||||
| 		owner.Name, repo.Name, issue.Index, token) | 		owner.Name, repo.Name, issue.Index, token) | ||||||
| 	req := NewRequestWithJSON(t, "PUT", urlStr, &api.IssueLabelsOption{ | 	req := NewRequestWithJSON(t, "PUT", urlStr, &api.IssueLabelsOption{ | ||||||
| @@ -143,7 +144,7 @@ func TestAPIModifyOrgLabels(t *testing.T) { | |||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | ||||||
| 	user := "user1" | 	user := "user1" | ||||||
| 	session := loginUser(t, user) | 	session := loginUser(t, user) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo, auth_model.AccessTokenScopeAdminOrg) | ||||||
| 	urlStr := fmt.Sprintf("/api/v1/orgs/%s/labels?token=%s", owner.Name, token) | 	urlStr := fmt.Sprintf("/api/v1/orgs/%s/labels?token=%s", owner.Name, token) | ||||||
|  |  | ||||||
| 	// CreateLabel | 	// CreateLabel | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	issues_model "code.gitea.io/gitea/models/issues" | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| @@ -28,7 +29,7 @@ func TestAPIIssuesMilestone(t *testing.T) { | |||||||
| 	assert.Equal(t, structs.StateOpen, milestone.State()) | 	assert.Equal(t, structs.StateOpen, milestone.State()) | ||||||
|  |  | ||||||
| 	session := loginUser(t, owner.Name) | 	session := loginUser(t, owner.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 	// update values of issue | 	// update values of issue | ||||||
| 	milestoneState := "closed" | 	milestoneState := "closed" | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ import ( | |||||||
| 	"testing" | 	"testing" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	issues_model "code.gitea.io/gitea/models/issues" | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| @@ -28,7 +29,7 @@ func TestAPIIssuesReactions(t *testing.T) { | |||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue.Repo.OwnerID}) | 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue.Repo.OwnerID}) | ||||||
|  |  | ||||||
| 	session := loginUser(t, owner.Name) | 	session := loginUser(t, owner.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | 	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | ||||||
| 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/reactions?token=%s", | 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/reactions?token=%s", | ||||||
| @@ -87,7 +88,7 @@ func TestAPICommentReactions(t *testing.T) { | |||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue.Repo.OwnerID}) | 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue.Repo.OwnerID}) | ||||||
|  |  | ||||||
| 	session := loginUser(t, owner.Name) | 	session := loginUser(t, owner.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 	user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) | 	user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) | ||||||
| 	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | 	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | ||||||
|   | |||||||
| @@ -7,6 +7,7 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	issues_model "code.gitea.io/gitea/models/issues" | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| @@ -25,7 +26,7 @@ func TestAPIListStopWatches(t *testing.T) { | |||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | ||||||
|  |  | ||||||
| 	session := loginUser(t, owner.Name) | 	session := loginUser(t, owner.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
| 	req := NewRequestf(t, "GET", "/api/v1/user/stopwatches?token=%s", token) | 	req := NewRequestf(t, "GET", "/api/v1/user/stopwatches?token=%s", token) | ||||||
| 	resp := MakeRequest(t, req, http.StatusOK) | 	resp := MakeRequest(t, req, http.StatusOK) | ||||||
| 	var apiWatches []*api.StopWatch | 	var apiWatches []*api.StopWatch | ||||||
| @@ -51,7 +52,7 @@ func TestAPIStopStopWatches(t *testing.T) { | |||||||
| 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | ||||||
|  |  | ||||||
| 	session := loginUser(t, user.Name) | 	session := loginUser(t, user.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 	req := NewRequestf(t, "POST", "/api/v1/repos/%s/%s/issues/%d/stopwatch/stop?token=%s", owner.Name, issue.Repo.Name, issue.Index, token) | 	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) | 	MakeRequest(t, req, http.StatusCreated) | ||||||
| @@ -67,7 +68,7 @@ func TestAPICancelStopWatches(t *testing.T) { | |||||||
| 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) | 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) | ||||||
|  |  | ||||||
| 	session := loginUser(t, user.Name) | 	session := loginUser(t, user.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 	req := NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/issues/%d/stopwatch/delete?token=%s", owner.Name, issue.Repo.Name, issue.Index, token) | 	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) | 	MakeRequest(t, req, http.StatusNoContent) | ||||||
| @@ -83,7 +84,7 @@ func TestAPIStartStopWatches(t *testing.T) { | |||||||
| 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | ||||||
|  |  | ||||||
| 	session := loginUser(t, user.Name) | 	session := loginUser(t, user.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 	req := NewRequestf(t, "POST", "/api/v1/repos/%s/%s/issues/%d/stopwatch/start?token=%s", owner.Name, issue.Repo.Name, issue.Index, token) | 	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) | 	MakeRequest(t, req, http.StatusCreated) | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	issues_model "code.gitea.io/gitea/models/issues" | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| @@ -30,7 +31,7 @@ func TestAPIIssueSubscriptions(t *testing.T) { | |||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue1.PosterID}) | 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue1.PosterID}) | ||||||
|  |  | ||||||
| 	session := loginUser(t, owner.Name) | 	session := loginUser(t, owner.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 	testSubscription := func(issue *issues_model.Issue, isWatching bool) { | 	testSubscription := func(issue *issues_model.Issue, isWatching bool) { | ||||||
| 		issueRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}) | 		issueRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}) | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ import ( | |||||||
| 	"testing" | 	"testing" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	issues_model "code.gitea.io/gitea/models/issues" | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| @@ -29,7 +30,7 @@ func TestAPIListIssues(t *testing.T) { | |||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | ||||||
|  |  | ||||||
| 	session := loginUser(t, owner.Name) | 	session := loginUser(t, owner.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
| 	link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/issues", owner.Name, repo.Name)) | 	link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/issues", owner.Name, repo.Name)) | ||||||
|  |  | ||||||
| 	link.RawQuery = url.Values{"token": {token}, "state": {"all"}}.Encode() | 	link.RawQuery = url.Values{"token": {token}, "state": {"all"}}.Encode() | ||||||
| @@ -80,7 +81,7 @@ func TestAPICreateIssue(t *testing.T) { | |||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repoBefore.OwnerID}) | 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repoBefore.OwnerID}) | ||||||
|  |  | ||||||
| 	session := loginUser(t, owner.Name) | 	session := loginUser(t, owner.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
| 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues?state=all&token=%s", owner.Name, repoBefore.Name, token) | 	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{ | 	req := NewRequestWithJSON(t, "POST", urlStr, &api.CreateIssueOption{ | ||||||
| 		Body:     body, | 		Body:     body, | ||||||
| @@ -116,7 +117,7 @@ func TestAPIEditIssue(t *testing.T) { | |||||||
| 	assert.Equal(t, api.StateOpen, issueBefore.State()) | 	assert.Equal(t, api.StateOpen, issueBefore.State()) | ||||||
|  |  | ||||||
| 	session := loginUser(t, owner.Name) | 	session := loginUser(t, owner.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 	// update values of issue | 	// update values of issue | ||||||
| 	issueState := "closed" | 	issueState := "closed" | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ import ( | |||||||
| 	"testing" | 	"testing" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	issues_model "code.gitea.io/gitea/models/issues" | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| @@ -27,7 +28,7 @@ func TestAPIGetTrackedTimes(t *testing.T) { | |||||||
| 	assert.NoError(t, issue2.LoadRepo(db.DefaultContext)) | 	assert.NoError(t, issue2.LoadRepo(db.DefaultContext)) | ||||||
|  |  | ||||||
| 	session := loginUser(t, user2.Name) | 	session := loginUser(t, user2.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 	req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/issues/%d/times?token=%s", user2.Name, issue2.Repo.Name, issue2.Index, token) | 	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) | 	resp := MakeRequest(t, req, http.StatusOK) | ||||||
| @@ -70,7 +71,7 @@ func TestAPIDeleteTrackedTime(t *testing.T) { | |||||||
| 	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | 	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | ||||||
|  |  | ||||||
| 	session := loginUser(t, user2.Name) | 	session := loginUser(t, user2.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 	// Deletion not allowed | 	// 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) | 	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) | ||||||
| @@ -105,7 +106,7 @@ func TestAPIAddTrackedTimes(t *testing.T) { | |||||||
| 	admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) | 	admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) | ||||||
|  |  | ||||||
| 	session := loginUser(t, admin.Name) | 	session := loginUser(t, admin.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/times?token=%s", user2.Name, issue2.Repo.Name, issue2.Index, token) | 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/times?token=%s", user2.Name, issue2.Repo.Name, issue2.Index, token) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ import ( | |||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	asymkey_model "code.gitea.io/gitea/models/asymkey" | 	asymkey_model "code.gitea.io/gitea/models/asymkey" | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/perm" | 	"code.gitea.io/gitea/models/perm" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| @@ -53,7 +54,7 @@ func TestCreateReadOnlyDeployKey(t *testing.T) { | |||||||
| 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | ||||||
|  |  | ||||||
| 	session := loginUser(t, repoOwner.Name) | 	session := loginUser(t, repoOwner.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
| 	keysURL := fmt.Sprintf("/api/v1/repos/%s/%s/keys?token=%s", repoOwner.Name, repo.Name, token) | 	keysURL := fmt.Sprintf("/api/v1/repos/%s/%s/keys?token=%s", repoOwner.Name, repo.Name, token) | ||||||
| 	rawKeyBody := api.CreateKeyOption{ | 	rawKeyBody := api.CreateKeyOption{ | ||||||
| 		Title:    "read-only", | 		Title:    "read-only", | ||||||
| @@ -79,7 +80,7 @@ func TestCreateReadWriteDeployKey(t *testing.T) { | |||||||
| 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | ||||||
|  |  | ||||||
| 	session := loginUser(t, repoOwner.Name) | 	session := loginUser(t, repoOwner.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
| 	keysURL := fmt.Sprintf("/api/v1/repos/%s/%s/keys?token=%s", repoOwner.Name, repo.Name, token) | 	keysURL := fmt.Sprintf("/api/v1/repos/%s/%s/keys?token=%s", repoOwner.Name, repo.Name, token) | ||||||
| 	rawKeyBody := api.CreateKeyOption{ | 	rawKeyBody := api.CreateKeyOption{ | ||||||
| 		Title: "read-write", | 		Title: "read-write", | ||||||
| @@ -103,7 +104,7 @@ func TestCreateUserKey(t *testing.T) { | |||||||
| 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user1"}) | 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user1"}) | ||||||
|  |  | ||||||
| 	session := loginUser(t, "user1") | 	session := loginUser(t, "user1") | ||||||
| 	token := url.QueryEscape(getTokenForLoggedInUser(t, session)) | 	token := url.QueryEscape(getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAdminPublicKey)) | ||||||
| 	keysURL := fmt.Sprintf("/api/v1/user/keys?token=%s", token) | 	keysURL := fmt.Sprintf("/api/v1/user/keys?token=%s", token) | ||||||
| 	keyType := "ssh-rsa" | 	keyType := "ssh-rsa" | ||||||
| 	keyContent := "AAAAB3NzaC1yc2EAAAADAQABAAABgQC4cn+iXnA4KvcQYSV88vGn0Yi91vG47t1P7okprVmhNTkipNRIHWr6WdCO4VDr/cvsRkuVJAsLO2enwjGWWueOO6BodiBgyAOZ/5t5nJNMCNuLGT5UIo/RI1b0WRQwxEZTRjt6mFNw6lH14wRd8ulsr9toSWBPMOGWoYs1PDeDL0JuTjL+tr1SZi/EyxCngpYszKdXllJEHyI79KQgeD0Vt3pTrkbNVTOEcCNqZePSVmUH8X8Vhugz3bnE0/iE9Pb5fkWO9c4AnM1FgI/8Bvp27Fw2ShryIXuR6kKvUqhVMTuOSDHwu6A8jLE5Owt3GAYugDpDYuwTVNGrHLXKpPzrGGPE/jPmaLCMZcsdkec95dYeU3zKODEm8UQZFhmJmDeWVJ36nGrGZHL4J5aTTaeFUJmmXDaJYiJ+K2/ioKgXqnXvltu0A9R8/LGy4nrTJRr4JMLuJFoUXvGm1gXQ70w2LSpk6yl71RNC0hCtsBe8BP8IhYCM0EP5jh7eCMQZNvM=" | 	keyContent := "AAAAB3NzaC1yc2EAAAADAQABAAABgQC4cn+iXnA4KvcQYSV88vGn0Yi91vG47t1P7okprVmhNTkipNRIHWr6WdCO4VDr/cvsRkuVJAsLO2enwjGWWueOO6BodiBgyAOZ/5t5nJNMCNuLGT5UIo/RI1b0WRQwxEZTRjt6mFNw6lH14wRd8ulsr9toSWBPMOGWoYs1PDeDL0JuTjL+tr1SZi/EyxCngpYszKdXllJEHyI79KQgeD0Vt3pTrkbNVTOEcCNqZePSVmUH8X8Vhugz3bnE0/iE9Pb5fkWO9c4AnM1FgI/8Bvp27Fw2ShryIXuR6kKvUqhVMTuOSDHwu6A8jLE5Owt3GAYugDpDYuwTVNGrHLXKpPzrGGPE/jPmaLCMZcsdkec95dYeU3zKODEm8UQZFhmJmDeWVJ36nGrGZHL4J5aTTaeFUJmmXDaJYiJ+K2/ioKgXqnXvltu0A9R8/LGy4nrTJRr4JMLuJFoUXvGm1gXQ70w2LSpk6yl71RNC0hCtsBe8BP8IhYCM0EP5jh7eCMQZNvM=" | ||||||
| @@ -167,7 +168,7 @@ func TestCreateUserKey(t *testing.T) { | |||||||
|  |  | ||||||
| 	// Now login as user 2 | 	// Now login as user 2 | ||||||
| 	session2 := loginUser(t, "user2") | 	session2 := loginUser(t, "user2") | ||||||
| 	token2 := url.QueryEscape(getTokenForLoggedInUser(t, session2)) | 	token2 := url.QueryEscape(getTokenForLoggedInUser(t, session2, auth_model.AccessTokenScopeAdminPublicKey)) | ||||||
|  |  | ||||||
| 	// Should find key even though not ours, but we shouldn't know whose it is | 	// 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) | 	fingerprintURL = fmt.Sprintf("/api/v1/user/keys?token=%s&fingerprint=%s", token2, newPublicKey.Fingerprint) | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ import ( | |||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	activities_model "code.gitea.io/gitea/models/activities" | 	activities_model "code.gitea.io/gitea/models/activities" | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| @@ -27,7 +28,7 @@ func TestAPINotification(t *testing.T) { | |||||||
| 	thread5 := unittest.AssertExistsAndLoadBean(t, &activities_model.Notification{ID: 5}) | 	thread5 := unittest.AssertExistsAndLoadBean(t, &activities_model.Notification{ID: 5}) | ||||||
| 	assert.NoError(t, thread5.LoadAttributes(db.DefaultContext)) | 	assert.NoError(t, thread5.LoadAttributes(db.DefaultContext)) | ||||||
| 	session := loginUser(t, user2.Name) | 	session := loginUser(t, user2.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeNotification) | ||||||
|  |  | ||||||
| 	// -- GET /notifications -- | 	// -- GET /notifications -- | ||||||
| 	// test filter | 	// test filter | ||||||
| @@ -145,7 +146,7 @@ func TestAPINotificationPUT(t *testing.T) { | |||||||
| 	thread5 := unittest.AssertExistsAndLoadBean(t, &activities_model.Notification{ID: 5}) | 	thread5 := unittest.AssertExistsAndLoadBean(t, &activities_model.Notification{ID: 5}) | ||||||
| 	assert.NoError(t, thread5.LoadAttributes(db.DefaultContext)) | 	assert.NoError(t, thread5.LoadAttributes(db.DefaultContext)) | ||||||
| 	session := loginUser(t, user2.Name) | 	session := loginUser(t, user2.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeNotification) | ||||||
|  |  | ||||||
| 	// Check notifications are as expected | 	// Check notifications are as expected | ||||||
| 	req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications?all=true&token=%s", token)) | 	req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications?all=true&token=%s", token)) | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/auth" | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	api "code.gitea.io/gitea/modules/structs" | 	api "code.gitea.io/gitea/modules/structs" | ||||||
| @@ -49,15 +49,15 @@ func testAPICreateOAuth2Application(t *testing.T) { | |||||||
| 	assert.True(t, createdApp.ConfidentialClient) | 	assert.True(t, createdApp.ConfidentialClient) | ||||||
| 	assert.NotEmpty(t, createdApp.Created) | 	assert.NotEmpty(t, createdApp.Created) | ||||||
| 	assert.EqualValues(t, appBody.RedirectURIs[0], createdApp.RedirectURIs[0]) | 	assert.EqualValues(t, appBody.RedirectURIs[0], createdApp.RedirectURIs[0]) | ||||||
| 	unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{UID: user.ID, Name: createdApp.Name}) | 	unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{UID: user.ID, Name: createdApp.Name}) | ||||||
| } | } | ||||||
|  |  | ||||||
| func testAPIListOAuth2Applications(t *testing.T) { | func testAPIListOAuth2Applications(t *testing.T) { | ||||||
| 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | ||||||
| 	session := loginUser(t, user.Name) | 	session := loginUser(t, user.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadApplication) | ||||||
|  |  | ||||||
| 	existApp := unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ | 	existApp := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ | ||||||
| 		UID:  user.ID, | 		UID:  user.ID, | ||||||
| 		Name: "test-app-1", | 		Name: "test-app-1", | ||||||
| 		RedirectURIs: []string{ | 		RedirectURIs: []string{ | ||||||
| @@ -80,15 +80,15 @@ func testAPIListOAuth2Applications(t *testing.T) { | |||||||
| 	assert.Len(t, expectedApp.ClientID, 36) | 	assert.Len(t, expectedApp.ClientID, 36) | ||||||
| 	assert.Empty(t, expectedApp.ClientSecret) | 	assert.Empty(t, expectedApp.ClientSecret) | ||||||
| 	assert.EqualValues(t, existApp.RedirectURIs[0], expectedApp.RedirectURIs[0]) | 	assert.EqualValues(t, existApp.RedirectURIs[0], expectedApp.RedirectURIs[0]) | ||||||
| 	unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ID: expectedApp.ID, Name: expectedApp.Name}) | 	unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: expectedApp.ID, Name: expectedApp.Name}) | ||||||
| } | } | ||||||
|  |  | ||||||
| func testAPIDeleteOAuth2Application(t *testing.T) { | func testAPIDeleteOAuth2Application(t *testing.T) { | ||||||
| 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | ||||||
| 	session := loginUser(t, user.Name) | 	session := loginUser(t, user.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteApplication) | ||||||
|  |  | ||||||
| 	oldApp := unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ | 	oldApp := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ | ||||||
| 		UID:  user.ID, | 		UID:  user.ID, | ||||||
| 		Name: "test-app-1", | 		Name: "test-app-1", | ||||||
| 	}) | 	}) | ||||||
| @@ -97,7 +97,7 @@ func testAPIDeleteOAuth2Application(t *testing.T) { | |||||||
| 	req := NewRequest(t, "DELETE", urlStr) | 	req := NewRequest(t, "DELETE", urlStr) | ||||||
| 	MakeRequest(t, req, http.StatusNoContent) | 	MakeRequest(t, req, http.StatusNoContent) | ||||||
|  |  | ||||||
| 	unittest.AssertNotExistsBean(t, &auth.OAuth2Application{UID: oldApp.UID, Name: oldApp.Name}) | 	unittest.AssertNotExistsBean(t, &auth_model.OAuth2Application{UID: oldApp.UID, Name: oldApp.Name}) | ||||||
|  |  | ||||||
| 	// Delete again will return not found | 	// Delete again will return not found | ||||||
| 	req = NewRequest(t, "DELETE", urlStr) | 	req = NewRequest(t, "DELETE", urlStr) | ||||||
| @@ -107,9 +107,9 @@ func testAPIDeleteOAuth2Application(t *testing.T) { | |||||||
| func testAPIGetOAuth2Application(t *testing.T) { | func testAPIGetOAuth2Application(t *testing.T) { | ||||||
| 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | ||||||
| 	session := loginUser(t, user.Name) | 	session := loginUser(t, user.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadApplication) | ||||||
|  |  | ||||||
| 	existApp := unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ | 	existApp := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ | ||||||
| 		UID:  user.ID, | 		UID:  user.ID, | ||||||
| 		Name: "test-app-1", | 		Name: "test-app-1", | ||||||
| 		RedirectURIs: []string{ | 		RedirectURIs: []string{ | ||||||
| @@ -133,13 +133,13 @@ func testAPIGetOAuth2Application(t *testing.T) { | |||||||
| 	assert.Empty(t, expectedApp.ClientSecret) | 	assert.Empty(t, expectedApp.ClientSecret) | ||||||
| 	assert.Len(t, expectedApp.RedirectURIs, 1) | 	assert.Len(t, expectedApp.RedirectURIs, 1) | ||||||
| 	assert.EqualValues(t, existApp.RedirectURIs[0], expectedApp.RedirectURIs[0]) | 	assert.EqualValues(t, existApp.RedirectURIs[0], expectedApp.RedirectURIs[0]) | ||||||
| 	unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ID: expectedApp.ID, Name: expectedApp.Name}) | 	unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: expectedApp.ID, Name: expectedApp.Name}) | ||||||
| } | } | ||||||
|  |  | ||||||
| func testAPIUpdateOAuth2Application(t *testing.T) { | func testAPIUpdateOAuth2Application(t *testing.T) { | ||||||
| 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | ||||||
|  |  | ||||||
| 	existApp := unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ | 	existApp := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ | ||||||
| 		UID:  user.ID, | 		UID:  user.ID, | ||||||
| 		Name: "test-app-1", | 		Name: "test-app-1", | ||||||
| 		RedirectURIs: []string{ | 		RedirectURIs: []string{ | ||||||
| @@ -169,5 +169,5 @@ func testAPIUpdateOAuth2Application(t *testing.T) { | |||||||
| 	assert.EqualValues(t, expectedApp.RedirectURIs[0], appBody.RedirectURIs[0]) | 	assert.EqualValues(t, expectedApp.RedirectURIs[0], appBody.RedirectURIs[0]) | ||||||
| 	assert.EqualValues(t, expectedApp.RedirectURIs[1], appBody.RedirectURIs[1]) | 	assert.EqualValues(t, expectedApp.RedirectURIs[1], appBody.RedirectURIs[1]) | ||||||
| 	assert.Equal(t, expectedApp.ConfidentialClient, appBody.ConfidentialClient) | 	assert.Equal(t, expectedApp.ConfidentialClient, appBody.ConfidentialClient) | ||||||
| 	unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ID: expectedApp.ID, Name: expectedApp.Name}) | 	unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: expectedApp.ID, Name: expectedApp.Name}) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ import ( | |||||||
| 	"strings" | 	"strings" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| @@ -21,7 +22,7 @@ import ( | |||||||
|  |  | ||||||
| func TestAPIOrgCreate(t *testing.T) { | func TestAPIOrgCreate(t *testing.T) { | ||||||
| 	onGiteaRun(t, func(*testing.T, *url.URL) { | 	onGiteaRun(t, func(*testing.T, *url.URL) { | ||||||
| 		token := getUserToken(t, "user1") | 		token := getUserToken(t, "user1", auth_model.AccessTokenScopeWriteOrg) | ||||||
|  |  | ||||||
| 		org := api.CreateOrgOption{ | 		org := api.CreateOrgOption{ | ||||||
| 			UserName:    "user1_org", | 			UserName:    "user1_org", | ||||||
| @@ -79,7 +80,7 @@ func TestAPIOrgEdit(t *testing.T) { | |||||||
| 	onGiteaRun(t, func(*testing.T, *url.URL) { | 	onGiteaRun(t, func(*testing.T, *url.URL) { | ||||||
| 		session := loginUser(t, "user1") | 		session := loginUser(t, "user1") | ||||||
|  |  | ||||||
| 		token := getTokenForLoggedInUser(t, session) | 		token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteOrg) | ||||||
| 		org := api.EditOrgOption{ | 		org := api.EditOrgOption{ | ||||||
| 			FullName:    "User3 organization new full name", | 			FullName:    "User3 organization new full name", | ||||||
| 			Description: "A new description", | 			Description: "A new description", | ||||||
| @@ -106,7 +107,7 @@ func TestAPIOrgEditBadVisibility(t *testing.T) { | |||||||
| 	onGiteaRun(t, func(*testing.T, *url.URL) { | 	onGiteaRun(t, func(*testing.T, *url.URL) { | ||||||
| 		session := loginUser(t, "user1") | 		session := loginUser(t, "user1") | ||||||
|  |  | ||||||
| 		token := getTokenForLoggedInUser(t, session) | 		token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteOrg) | ||||||
| 		org := api.EditOrgOption{ | 		org := api.EditOrgOption{ | ||||||
| 			FullName:    "User3 organization new full name", | 			FullName:    "User3 organization new full name", | ||||||
| 			Description: "A new description", | 			Description: "A new description", | ||||||
| @@ -126,14 +127,16 @@ func TestAPIOrgDeny(t *testing.T) { | |||||||
| 			setting.Service.RequireSignInView = false | 			setting.Service.RequireSignInView = false | ||||||
| 		}() | 		}() | ||||||
|  |  | ||||||
|  | 		token := getUserToken(t, "user1", auth_model.AccessTokenScopeReadOrg) | ||||||
|  |  | ||||||
| 		orgName := "user1_org" | 		orgName := "user1_org" | ||||||
| 		req := NewRequestf(t, "GET", "/api/v1/orgs/%s", orgName) | 		req := NewRequestf(t, "GET", "/api/v1/orgs/%s?token=%s", orgName, token) | ||||||
| 		MakeRequest(t, req, http.StatusNotFound) | 		MakeRequest(t, req, http.StatusNotFound) | ||||||
|  |  | ||||||
| 		req = NewRequestf(t, "GET", "/api/v1/orgs/%s/repos", orgName) | 		req = NewRequestf(t, "GET", "/api/v1/orgs/%s/repos?token=%s", orgName, token) | ||||||
| 		MakeRequest(t, req, http.StatusNotFound) | 		MakeRequest(t, req, http.StatusNotFound) | ||||||
|  |  | ||||||
| 		req = NewRequestf(t, "GET", "/api/v1/orgs/%s/members", orgName) | 		req = NewRequestf(t, "GET", "/api/v1/orgs/%s/members?token=%s", orgName, token) | ||||||
| 		MakeRequest(t, req, http.StatusNotFound) | 		MakeRequest(t, req, http.StatusNotFound) | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
| @@ -141,20 +144,23 @@ func TestAPIOrgDeny(t *testing.T) { | |||||||
| func TestAPIGetAll(t *testing.T) { | func TestAPIGetAll(t *testing.T) { | ||||||
| 	defer tests.PrepareTestEnv(t)() | 	defer tests.PrepareTestEnv(t)() | ||||||
|  |  | ||||||
| 	req := NewRequestf(t, "GET", "/api/v1/orgs") | 	token := getUserToken(t, "user1", auth_model.AccessTokenScopeReadOrg) | ||||||
|  |  | ||||||
|  | 	req := NewRequestf(t, "GET", "/api/v1/orgs?token=%s", token) | ||||||
| 	resp := MakeRequest(t, req, http.StatusOK) | 	resp := MakeRequest(t, req, http.StatusOK) | ||||||
|  |  | ||||||
| 	var apiOrgList []*api.Organization | 	var apiOrgList []*api.Organization | ||||||
| 	DecodeJSON(t, resp, &apiOrgList) | 	DecodeJSON(t, resp, &apiOrgList) | ||||||
|  |  | ||||||
| 	assert.Len(t, apiOrgList, 7) | 	// accessing with a token will return all orgs | ||||||
| 	assert.Equal(t, "org25", apiOrgList[0].FullName) | 	assert.Len(t, apiOrgList, 9) | ||||||
| 	assert.Equal(t, "public", apiOrgList[0].Visibility) | 	assert.Equal(t, "org25", apiOrgList[1].FullName) | ||||||
|  | 	assert.Equal(t, "public", apiOrgList[1].Visibility) | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestAPIOrgSearchEmptyTeam(t *testing.T) { | func TestAPIOrgSearchEmptyTeam(t *testing.T) { | ||||||
| 	onGiteaRun(t, func(*testing.T, *url.URL) { | 	onGiteaRun(t, func(*testing.T, *url.URL) { | ||||||
| 		token := getUserToken(t, "user1") | 		token := getUserToken(t, "user1", auth_model.AccessTokenScopeAdminOrg) | ||||||
| 		orgName := "org_with_empty_team" | 		orgName := "org_with_empty_team" | ||||||
|  |  | ||||||
| 		// create org | 		// create org | ||||||
|   | |||||||
| @@ -13,6 +13,7 @@ import ( | |||||||
| 	"sync" | 	"sync" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	packages_model "code.gitea.io/gitea/models/packages" | 	packages_model "code.gitea.io/gitea/models/packages" | ||||||
| 	container_model "code.gitea.io/gitea/models/packages/container" | 	container_model "code.gitea.io/gitea/models/packages/container" | ||||||
| @@ -31,6 +32,8 @@ func TestPackageContainer(t *testing.T) { | |||||||
| 	defer tests.PrepareTestEnv(t)() | 	defer tests.PrepareTestEnv(t)() | ||||||
|  |  | ||||||
| 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | ||||||
|  | 	session := loginUser(t, user.Name) | ||||||
|  | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadPackage) | ||||||
|  |  | ||||||
| 	has := func(l packages_model.PackagePropertyList, name string) bool { | 	has := func(l packages_model.PackagePropertyList, name string) bool { | ||||||
| 		for _, pp := range l { | 		for _, pp := range l { | ||||||
| @@ -558,7 +561,7 @@ func TestPackageContainer(t *testing.T) { | |||||||
| 					assert.Equal(t, c.ExpectedLink, resp.Header().Get("Link")) | 					assert.Equal(t, c.ExpectedLink, resp.Header().Get("Link")) | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 				req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s?type=container&q=%s", user.Name, image)) | 				req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s?type=container&q=%s&token=%s", user.Name, image, token)) | ||||||
| 				resp := MakeRequest(t, req, http.StatusOK) | 				resp := MakeRequest(t, req, http.StatusOK) | ||||||
|  |  | ||||||
| 				var apiPackages []*api.Package | 				var apiPackages []*api.Package | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ import ( | |||||||
| 	"testing" | 	"testing" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	packages_model "code.gitea.io/gitea/models/packages" | 	packages_model "code.gitea.io/gitea/models/packages" | ||||||
| 	container_model "code.gitea.io/gitea/models/packages/container" | 	container_model "code.gitea.io/gitea/models/packages/container" | ||||||
| @@ -28,7 +29,8 @@ func TestPackageAPI(t *testing.T) { | |||||||
|  |  | ||||||
| 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) | 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) | ||||||
| 	session := loginUser(t, user.Name) | 	session := loginUser(t, user.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	tokenReadPackage := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadPackage) | ||||||
|  | 	tokenDeletePackage := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeDeletePackage) | ||||||
|  |  | ||||||
| 	packageName := "test-package" | 	packageName := "test-package" | ||||||
| 	packageVersion := "1.0.3" | 	packageVersion := "1.0.3" | ||||||
| @@ -42,7 +44,7 @@ func TestPackageAPI(t *testing.T) { | |||||||
| 	t.Run("ListPackages", func(t *testing.T) { | 	t.Run("ListPackages", func(t *testing.T) { | ||||||
| 		defer tests.PrintCurrentTest(t)() | 		defer tests.PrintCurrentTest(t)() | ||||||
|  |  | ||||||
| 		req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s?token=%s", user.Name, token)) | 		req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s?token=%s", user.Name, tokenReadPackage)) | ||||||
| 		resp := MakeRequest(t, req, http.StatusOK) | 		resp := MakeRequest(t, req, http.StatusOK) | ||||||
|  |  | ||||||
| 		var apiPackages []*api.Package | 		var apiPackages []*api.Package | ||||||
| @@ -59,10 +61,10 @@ func TestPackageAPI(t *testing.T) { | |||||||
| 	t.Run("GetPackage", func(t *testing.T) { | 	t.Run("GetPackage", func(t *testing.T) { | ||||||
| 		defer tests.PrintCurrentTest(t)() | 		defer tests.PrintCurrentTest(t)() | ||||||
|  |  | ||||||
| 		req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/dummy/%s/%s?token=%s", user.Name, packageName, packageVersion, token)) | 		req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/dummy/%s/%s?token=%s", user.Name, packageName, packageVersion, tokenReadPackage)) | ||||||
| 		MakeRequest(t, req, http.StatusNotFound) | 		MakeRequest(t, req, http.StatusNotFound) | ||||||
|  |  | ||||||
| 		req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s?token=%s", user.Name, packageName, packageVersion, token)) | 		req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s?token=%s", user.Name, packageName, packageVersion, tokenReadPackage)) | ||||||
| 		resp := MakeRequest(t, req, http.StatusOK) | 		resp := MakeRequest(t, req, http.StatusOK) | ||||||
|  |  | ||||||
| 		var p *api.Package | 		var p *api.Package | ||||||
| @@ -81,7 +83,7 @@ func TestPackageAPI(t *testing.T) { | |||||||
| 			assert.NoError(t, err) | 			assert.NoError(t, err) | ||||||
|  |  | ||||||
| 			// no repository link | 			// no repository link | ||||||
| 			req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s?token=%s", user.Name, packageName, packageVersion, token)) | 			req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s?token=%s", user.Name, packageName, packageVersion, tokenReadPackage)) | ||||||
| 			resp := MakeRequest(t, req, http.StatusOK) | 			resp := MakeRequest(t, req, http.StatusOK) | ||||||
|  |  | ||||||
| 			var ap1 *api.Package | 			var ap1 *api.Package | ||||||
| @@ -91,7 +93,7 @@ func TestPackageAPI(t *testing.T) { | |||||||
| 			// link to public repository | 			// link to public repository | ||||||
| 			assert.NoError(t, packages_model.SetRepositoryLink(db.DefaultContext, p.ID, 1)) | 			assert.NoError(t, packages_model.SetRepositoryLink(db.DefaultContext, p.ID, 1)) | ||||||
|  |  | ||||||
| 			req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s?token=%s", user.Name, packageName, packageVersion, token)) | 			req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s?token=%s", user.Name, packageName, packageVersion, tokenReadPackage)) | ||||||
| 			resp = MakeRequest(t, req, http.StatusOK) | 			resp = MakeRequest(t, req, http.StatusOK) | ||||||
|  |  | ||||||
| 			var ap2 *api.Package | 			var ap2 *api.Package | ||||||
| @@ -102,7 +104,7 @@ func TestPackageAPI(t *testing.T) { | |||||||
| 			// link to private repository | 			// link to private repository | ||||||
| 			assert.NoError(t, packages_model.SetRepositoryLink(db.DefaultContext, p.ID, 2)) | 			assert.NoError(t, packages_model.SetRepositoryLink(db.DefaultContext, p.ID, 2)) | ||||||
|  |  | ||||||
| 			req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s?token=%s", user.Name, packageName, packageVersion, token)) | 			req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s?token=%s", user.Name, packageName, packageVersion, tokenReadPackage)) | ||||||
| 			resp = MakeRequest(t, req, http.StatusOK) | 			resp = MakeRequest(t, req, http.StatusOK) | ||||||
|  |  | ||||||
| 			var ap3 *api.Package | 			var ap3 *api.Package | ||||||
| @@ -116,10 +118,10 @@ func TestPackageAPI(t *testing.T) { | |||||||
| 	t.Run("ListPackageFiles", func(t *testing.T) { | 	t.Run("ListPackageFiles", func(t *testing.T) { | ||||||
| 		defer tests.PrintCurrentTest(t)() | 		defer tests.PrintCurrentTest(t)() | ||||||
|  |  | ||||||
| 		req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/dummy/%s/%s/files?token=%s", user.Name, packageName, packageVersion, token)) | 		req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/dummy/%s/%s/files?token=%s", user.Name, packageName, packageVersion, tokenReadPackage)) | ||||||
| 		MakeRequest(t, req, http.StatusNotFound) | 		MakeRequest(t, req, http.StatusNotFound) | ||||||
|  |  | ||||||
| 		req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s/files?token=%s", user.Name, packageName, packageVersion, token)) | 		req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s/files?token=%s", user.Name, packageName, packageVersion, tokenReadPackage)) | ||||||
| 		resp := MakeRequest(t, req, http.StatusOK) | 		resp := MakeRequest(t, req, http.StatusOK) | ||||||
|  |  | ||||||
| 		var files []*api.PackageFile | 		var files []*api.PackageFile | ||||||
| @@ -137,10 +139,10 @@ func TestPackageAPI(t *testing.T) { | |||||||
| 	t.Run("DeletePackage", func(t *testing.T) { | 	t.Run("DeletePackage", func(t *testing.T) { | ||||||
| 		defer tests.PrintCurrentTest(t)() | 		defer tests.PrintCurrentTest(t)() | ||||||
|  |  | ||||||
| 		req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/packages/%s/dummy/%s/%s?token=%s", user.Name, packageName, packageVersion, token)) | 		req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/packages/%s/dummy/%s/%s?token=%s", user.Name, packageName, packageVersion, tokenDeletePackage)) | ||||||
| 		MakeRequest(t, req, http.StatusNotFound) | 		MakeRequest(t, req, http.StatusNotFound) | ||||||
|  |  | ||||||
| 		req = NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s?token=%s", user.Name, packageName, packageVersion, token)) | 		req = NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s?token=%s", user.Name, packageName, packageVersion, tokenDeletePackage)) | ||||||
| 		MakeRequest(t, req, http.StatusNoContent) | 		MakeRequest(t, req, http.StatusNoContent) | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	issues_model "code.gitea.io/gitea/models/issues" | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| @@ -27,7 +28,7 @@ func TestAPIPullReview(t *testing.T) { | |||||||
|  |  | ||||||
| 	// test ListPullReviews | 	// test ListPullReviews | ||||||
| 	session := loginUser(t, "user2") | 	session := loginUser(t, "user2") | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
| 	req := NewRequestf(t, http.MethodGet, "/api/v1/repos/%s/%s/pulls/%d/reviews?token=%s", repo.OwnerName, repo.Name, pullIssue.Index, token) | 	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) | 	resp := MakeRequest(t, req, http.StatusOK) | ||||||
|  |  | ||||||
| @@ -230,7 +231,7 @@ func TestAPIPullReviewRequest(t *testing.T) { | |||||||
|  |  | ||||||
| 	// Test add Review Request | 	// Test add Review Request | ||||||
| 	session := loginUser(t, "user2") | 	session := loginUser(t, "user2") | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
| 	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{ | 	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"}, | 		Reviewers: []string{"user4@example.com", "user8"}, | ||||||
| 	}) | 	}) | ||||||
| @@ -250,7 +251,7 @@ func TestAPIPullReviewRequest(t *testing.T) { | |||||||
|  |  | ||||||
| 	// Test Remove Review Request | 	// Test Remove Review Request | ||||||
| 	session2 := loginUser(t, "user4") | 	session2 := loginUser(t, "user4") | ||||||
| 	token2 := getTokenForLoggedInUser(t, session2) | 	token2 := getTokenForLoggedInUser(t, session2, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 	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{ | 	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"}, | 		Reviewers: []string{"user4"}, | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	issues_model "code.gitea.io/gitea/models/issues" | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| @@ -28,7 +29,7 @@ func TestAPIViewPulls(t *testing.T) { | |||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) | ||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | ||||||
|  |  | ||||||
| 	ctx := NewAPITestContext(t, "user2", repo.Name) | 	ctx := NewAPITestContext(t, "user2", repo.Name, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 	req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/pulls?state=all&token="+ctx.Token, owner.Name, repo.Name) | 	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) | 	resp := ctx.Session.MakeRequest(t, req, http.StatusOK) | ||||||
| @@ -74,7 +75,7 @@ func TestAPIMergePullWIP(t *testing.T) { | |||||||
| 	assert.Contains(t, pr.Issue.Title, setting.Repository.PullRequest.WorkInProgressPrefixes[0]) | 	assert.Contains(t, pr.Issue.Title, setting.Repository.PullRequest.WorkInProgressPrefixes[0]) | ||||||
|  |  | ||||||
| 	session := loginUser(t, owner.Name) | 	session := loginUser(t, owner.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
| 	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{ | 	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, | 		MergeMessageField: pr.Issue.Title, | ||||||
| 		Do:                string(repo_model.MergeStyleMerge), | 		Do:                string(repo_model.MergeStyleMerge), | ||||||
| @@ -93,7 +94,7 @@ func TestAPICreatePullSuccess(t *testing.T) { | |||||||
| 	owner11 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo11.OwnerID}) | 	owner11 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo11.OwnerID}) | ||||||
|  |  | ||||||
| 	session := loginUser(t, owner11.Name) | 	session := loginUser(t, owner11.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
| 	req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls?token=%s", owner10.Name, repo10.Name, token), &api.CreatePullRequestOption{ | 	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), | 		Head:  fmt.Sprintf("%s:master", owner11.Name), | ||||||
| 		Base:  "master", | 		Base:  "master", | ||||||
| @@ -113,7 +114,7 @@ func TestAPICreatePullWithFieldsSuccess(t *testing.T) { | |||||||
| 	owner11 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo11.OwnerID}) | 	owner11 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo11.OwnerID}) | ||||||
|  |  | ||||||
| 	session := loginUser(t, owner11.Name) | 	session := loginUser(t, owner11.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 	opts := &api.CreatePullRequestOption{ | 	opts := &api.CreatePullRequestOption{ | ||||||
| 		Head:      fmt.Sprintf("%s:master", owner11.Name), | 		Head:      fmt.Sprintf("%s:master", owner11.Name), | ||||||
| @@ -150,7 +151,7 @@ func TestAPICreatePullWithFieldsFailure(t *testing.T) { | |||||||
| 	owner11 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo11.OwnerID}) | 	owner11 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo11.OwnerID}) | ||||||
|  |  | ||||||
| 	session := loginUser(t, owner11.Name) | 	session := loginUser(t, owner11.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 	opts := &api.CreatePullRequestOption{ | 	opts := &api.CreatePullRequestOption{ | ||||||
| 		Head: fmt.Sprintf("%s:master", owner11.Name), | 		Head: fmt.Sprintf("%s:master", owner11.Name), | ||||||
| @@ -180,7 +181,7 @@ func TestAPIEditPull(t *testing.T) { | |||||||
| 	owner10 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo10.OwnerID}) | 	owner10 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo10.OwnerID}) | ||||||
|  |  | ||||||
| 	session := loginUser(t, owner10.Name) | 	session := loginUser(t, owner10.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
| 	req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls?token=%s", owner10.Name, repo10.Name, token), &api.CreatePullRequestOption{ | 	req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls?token=%s", owner10.Name, repo10.Name, token), &api.CreatePullRequestOption{ | ||||||
| 		Head:  "develop", | 		Head:  "develop", | ||||||
| 		Base:  "master", | 		Base:  "master", | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ import ( | |||||||
| 	"net/url" | 	"net/url" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| @@ -24,7 +25,7 @@ func TestAPIListReleases(t *testing.T) { | |||||||
|  |  | ||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) | ||||||
| 	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | 	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | ||||||
| 	token := getUserToken(t, user2.LowerName) | 	token := getUserToken(t, user2.LowerName, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 	link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/releases", user2.Name, repo.Name)) | 	link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/releases", user2.Name, repo.Name)) | ||||||
| 	link.RawQuery = url.Values{"token": {token}}.Encode() | 	link.RawQuery = url.Values{"token": {token}}.Encode() | ||||||
| @@ -100,7 +101,7 @@ func TestAPICreateAndUpdateRelease(t *testing.T) { | |||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) | ||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | ||||||
| 	session := loginUser(t, owner.LowerName) | 	session := loginUser(t, owner.LowerName) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 	gitRepo, err := git.OpenRepository(git.DefaultContext, repo.RepoPath()) | 	gitRepo, err := git.OpenRepository(git.DefaultContext, repo.RepoPath()) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| @@ -152,7 +153,7 @@ func TestAPICreateReleaseToDefaultBranch(t *testing.T) { | |||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) | ||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | ||||||
| 	session := loginUser(t, owner.LowerName) | 	session := loginUser(t, owner.LowerName) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 	createNewReleaseUsingAPI(t, session, token, owner, repo, "v0.0.1", "", "v0.0.1", "test") | 	createNewReleaseUsingAPI(t, session, token, owner, repo, "v0.0.1", "", "v0.0.1", "test") | ||||||
| } | } | ||||||
| @@ -163,7 +164,7 @@ func TestAPICreateReleaseToDefaultBranchOnExistingTag(t *testing.T) { | |||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) | ||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | ||||||
| 	session := loginUser(t, owner.LowerName) | 	session := loginUser(t, owner.LowerName) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 	gitRepo, err := git.OpenRepository(git.DefaultContext, repo.RepoPath()) | 	gitRepo, err := git.OpenRepository(git.DefaultContext, repo.RepoPath()) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| @@ -213,7 +214,7 @@ func TestAPIDeleteReleaseByTagName(t *testing.T) { | |||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) | ||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | ||||||
| 	session := loginUser(t, owner.LowerName) | 	session := loginUser(t, owner.LowerName) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 	createNewReleaseUsingAPI(t, session, token, owner, repo, "release-tag", "", "Release Tag", "test") | 	createNewReleaseUsingAPI(t, session, token, owner, repo, "release-tag", "", "Release Tag", "test") | ||||||
|  |  | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ import ( | |||||||
| 	"net/url" | 	"net/url" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| @@ -24,7 +25,7 @@ func TestAPIDownloadArchive(t *testing.T) { | |||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) | ||||||
| 	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | 	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | ||||||
| 	session := loginUser(t, user2.LowerName) | 	session := loginUser(t, user2.LowerName) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 	link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/archive/master.zip", user2.Name, repo.Name)) | 	link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/archive/master.zip", user2.Name, repo.Name)) | ||||||
| 	link.RawQuery = url.Values{"token": {token}}.Encode() | 	link.RawQuery = url.Values{"token": {token}}.Encode() | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ import ( | |||||||
| 	"net/url" | 	"net/url" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/perm" | 	"code.gitea.io/gitea/models/perm" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| @@ -27,7 +28,7 @@ func TestAPIRepoCollaboratorPermission(t *testing.T) { | |||||||
| 		user10 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 10}) | 		user10 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 10}) | ||||||
| 		user11 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 11}) | 		user11 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 11}) | ||||||
|  |  | ||||||
| 		testCtx := NewAPITestContext(t, repo2Owner.Name, repo2.Name) | 		testCtx := NewAPITestContext(t, repo2Owner.Name, repo2.Name, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 		t.Run("RepoOwnerShouldBeOwner", func(t *testing.T) { | 		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) | 			req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission?token=%s", repo2Owner.Name, repo2.Name, repo2Owner.Name, testCtx.Token) | ||||||
| @@ -84,7 +85,7 @@ func TestAPIRepoCollaboratorPermission(t *testing.T) { | |||||||
| 			t.Run("AddUserAsCollaboratorWithReadAccess", doAPIAddCollaborator(testCtx, user5.Name, perm.AccessModeRead)) | 			t.Run("AddUserAsCollaboratorWithReadAccess", doAPIAddCollaborator(testCtx, user5.Name, perm.AccessModeRead)) | ||||||
|  |  | ||||||
| 			_session := loginUser(t, user5.Name) | 			_session := loginUser(t, user5.Name) | ||||||
| 			_testCtx := NewAPITestContext(t, user5.Name, repo2.Name) | 			_testCtx := NewAPITestContext(t, user5.Name, repo2.Name, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 			req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission?token=%s", repo2Owner.Name, repo2.Name, user5.Name, _testCtx.Token) | 			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) | 			resp := _session.MakeRequest(t, req, http.StatusOK) | ||||||
| @@ -99,7 +100,7 @@ func TestAPIRepoCollaboratorPermission(t *testing.T) { | |||||||
| 			t.Run("AddUserAsCollaboratorWithReadAccess", doAPIAddCollaborator(testCtx, user5.Name, perm.AccessModeRead)) | 			t.Run("AddUserAsCollaboratorWithReadAccess", doAPIAddCollaborator(testCtx, user5.Name, perm.AccessModeRead)) | ||||||
|  |  | ||||||
| 			_session := loginUser(t, user5.Name) | 			_session := loginUser(t, user5.Name) | ||||||
| 			_testCtx := NewAPITestContext(t, user5.Name, repo2.Name) | 			_testCtx := NewAPITestContext(t, user5.Name, repo2.Name, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 			req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission?token=%s", repo2Owner.Name, repo2.Name, user5.Name, _testCtx.Token) | 			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) | 			resp := _session.MakeRequest(t, req, http.StatusOK) | ||||||
| @@ -115,7 +116,7 @@ func TestAPIRepoCollaboratorPermission(t *testing.T) { | |||||||
| 			t.Run("AddUserAsCollaboratorWithReadAccess", doAPIAddCollaborator(testCtx, user11.Name, perm.AccessModeRead)) | 			t.Run("AddUserAsCollaboratorWithReadAccess", doAPIAddCollaborator(testCtx, user11.Name, perm.AccessModeRead)) | ||||||
|  |  | ||||||
| 			_session := loginUser(t, user10.Name) | 			_session := loginUser(t, user10.Name) | ||||||
| 			_testCtx := NewAPITestContext(t, user10.Name, repo2.Name) | 			_testCtx := NewAPITestContext(t, user10.Name, repo2.Name, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 			req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission?token=%s", repo2Owner.Name, repo2.Name, user11.Name, _testCtx.Token) | 			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) | 			resp := _session.MakeRequest(t, req, http.StatusOK) | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ import ( | |||||||
| 	"net/url" | 	"net/url" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	unit_model "code.gitea.io/gitea/models/unit" | 	unit_model "code.gitea.io/gitea/models/unit" | ||||||
| @@ -146,10 +147,10 @@ func TestAPIRepoEdit(t *testing.T) { | |||||||
|  |  | ||||||
| 		// Get user2's token | 		// Get user2's token | ||||||
| 		session := loginUser(t, user2.Name) | 		session := loginUser(t, user2.Name) | ||||||
| 		token2 := getTokenForLoggedInUser(t, session) | 		token2 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
| 		// Get user4's token | 		// Get user4's token | ||||||
| 		session = loginUser(t, user4.Name) | 		session = loginUser(t, user4.Name) | ||||||
| 		token4 := getTokenForLoggedInUser(t, session) | 		token4 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 		// Test editing a repo1 which user2 owns, changing name and many properties | 		// Test editing a repo1 which user2 owns, changing name and many properties | ||||||
| 		origRepoEditOption := getRepoEditOptionFromRepo(repo1) | 		origRepoEditOption := getRepoEditOptionFromRepo(repo1) | ||||||
|   | |||||||
| @@ -13,6 +13,7 @@ import ( | |||||||
| 	"testing" | 	"testing" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| @@ -150,10 +151,10 @@ func TestAPICreateFile(t *testing.T) { | |||||||
|  |  | ||||||
| 		// Get user2's token | 		// Get user2's token | ||||||
| 		session := loginUser(t, user2.Name) | 		session := loginUser(t, user2.Name) | ||||||
| 		token2 := getTokenForLoggedInUser(t, session) | 		token2 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
| 		// Get user4's token | 		// Get user4's token | ||||||
| 		session = loginUser(t, user4.Name) | 		session = loginUser(t, user4.Name) | ||||||
| 		token4 := getTokenForLoggedInUser(t, session) | 		token4 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 		// Test creating a file in repo1 which user2 owns, try both with branch and empty branch | 		// Test creating a file in repo1 which user2 owns, try both with branch and empty branch | ||||||
| 		for _, branch := range [...]string{ | 		for _, branch := range [...]string{ | ||||||
| @@ -279,7 +280,7 @@ func TestAPICreateFile(t *testing.T) { | |||||||
| 		MakeRequest(t, req, http.StatusForbidden) | 		MakeRequest(t, req, http.StatusForbidden) | ||||||
|  |  | ||||||
| 		// Test creating a file in an empty repository | 		// Test creating a file in an empty repository | ||||||
| 		doAPICreateRepository(NewAPITestContext(t, "user2", "empty-repo"), true)(t) | 		doAPICreateRepository(NewAPITestContext(t, "user2", "empty-repo", auth_model.AccessTokenScopeRepo), true)(t) | ||||||
| 		createFileOptions = getCreateFileOptions() | 		createFileOptions = getCreateFileOptions() | ||||||
| 		fileID++ | 		fileID++ | ||||||
| 		treePath = fmt.Sprintf("new/file%d.txt", fileID) | 		treePath = fmt.Sprintf("new/file%d.txt", fileID) | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ import ( | |||||||
| 	"net/url" | 	"net/url" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| @@ -48,10 +49,10 @@ func TestAPIDeleteFile(t *testing.T) { | |||||||
|  |  | ||||||
| 		// Get user2's token | 		// Get user2's token | ||||||
| 		session := loginUser(t, user2.Name) | 		session := loginUser(t, user2.Name) | ||||||
| 		token2 := getTokenForLoggedInUser(t, session) | 		token2 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
| 		// Get user4's token | 		// Get user4's token | ||||||
| 		session = loginUser(t, user4.Name) | 		session = loginUser(t, user4.Name) | ||||||
| 		token4 := getTokenForLoggedInUser(t, session) | 		token4 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 		// Test deleting a file in repo1 which user2 owns, try both with branch and empty branch | 		// Test deleting a file in repo1 which user2 owns, try both with branch and empty branch | ||||||
| 		for _, branch := range [...]string{ | 		for _, branch := range [...]string{ | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ import ( | |||||||
| 	"net/url" | 	"net/url" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	api "code.gitea.io/gitea/modules/structs" | 	api "code.gitea.io/gitea/modules/structs" | ||||||
| 	"code.gitea.io/gitea/tests" | 	"code.gitea.io/gitea/tests" | ||||||
|  |  | ||||||
| @@ -24,7 +25,7 @@ func TestAPIGetRawFileOrLFS(t *testing.T) { | |||||||
|  |  | ||||||
| 	// Test with LFS | 	// Test with LFS | ||||||
| 	onGiteaRun(t, func(t *testing.T, u *url.URL) { | 	onGiteaRun(t, func(t *testing.T, u *url.URL) { | ||||||
| 		httpContext := NewAPITestContext(t, "user2", "repo-lfs-test") | 		httpContext := NewAPITestContext(t, "user2", "repo-lfs-test", auth_model.AccessTokenScopeRepo, auth_model.AccessTokenScopeDeleteRepo) | ||||||
| 		doAPICreateRepository(httpContext, false, func(t *testing.T, repository api.Repository) { | 		doAPICreateRepository(httpContext, false, func(t *testing.T, repository api.Repository) { | ||||||
| 			u.Path = httpContext.GitPath() | 			u.Path = httpContext.GitPath() | ||||||
| 			dstPath := t.TempDir() | 			dstPath := t.TempDir() | ||||||
|   | |||||||
| @@ -12,6 +12,7 @@ import ( | |||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| @@ -116,10 +117,10 @@ func TestAPIUpdateFile(t *testing.T) { | |||||||
|  |  | ||||||
| 		// Get user2's token | 		// Get user2's token | ||||||
| 		session := loginUser(t, user2.Name) | 		session := loginUser(t, user2.Name) | ||||||
| 		token2 := getTokenForLoggedInUser(t, session) | 		token2 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
| 		// Get user4's token | 		// Get user4's token | ||||||
| 		session = loginUser(t, user4.Name) | 		session = loginUser(t, user4.Name) | ||||||
| 		token4 := getTokenForLoggedInUser(t, session) | 		token4 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 		// Test updating a file in repo1 which user2 owns, try both with branch and empty branch | 		// Test updating a file in repo1 which user2 owns, try both with branch and empty branch | ||||||
| 		for _, branch := range [...]string{ | 		for _, branch := range [...]string{ | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| @@ -30,7 +31,7 @@ func TestAPIListGitHooks(t *testing.T) { | |||||||
|  |  | ||||||
| 	// user1 is an admin user | 	// user1 is an admin user | ||||||
| 	session := loginUser(t, "user1") | 	session := loginUser(t, "user1") | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepoHook) | ||||||
| 	req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/hooks/git?token=%s", | 	req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/hooks/git?token=%s", | ||||||
| 		owner.Name, repo.Name, token) | 		owner.Name, repo.Name, token) | ||||||
| 	resp := MakeRequest(t, req, http.StatusOK) | 	resp := MakeRequest(t, req, http.StatusOK) | ||||||
| @@ -56,7 +57,7 @@ func TestAPIListGitHooksNoHooks(t *testing.T) { | |||||||
|  |  | ||||||
| 	// user1 is an admin user | 	// user1 is an admin user | ||||||
| 	session := loginUser(t, "user1") | 	session := loginUser(t, "user1") | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepoHook) | ||||||
| 	req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/hooks/git?token=%s", | 	req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/hooks/git?token=%s", | ||||||
| 		owner.Name, repo.Name, token) | 		owner.Name, repo.Name, token) | ||||||
| 	resp := MakeRequest(t, req, http.StatusOK) | 	resp := MakeRequest(t, req, http.StatusOK) | ||||||
| @@ -76,7 +77,7 @@ func TestAPIListGitHooksNoAccess(t *testing.T) { | |||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | ||||||
|  |  | ||||||
| 	session := loginUser(t, owner.Name) | 	session := loginUser(t, owner.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepoHook) | ||||||
| 	req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/hooks/git?token=%s", | 	req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/hooks/git?token=%s", | ||||||
| 		owner.Name, repo.Name, token) | 		owner.Name, repo.Name, token) | ||||||
| 	MakeRequest(t, req, http.StatusForbidden) | 	MakeRequest(t, req, http.StatusForbidden) | ||||||
| @@ -90,7 +91,7 @@ func TestAPIGetGitHook(t *testing.T) { | |||||||
|  |  | ||||||
| 	// user1 is an admin user | 	// user1 is an admin user | ||||||
| 	session := loginUser(t, "user1") | 	session := loginUser(t, "user1") | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepoHook) | ||||||
| 	req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/hooks/git/pre-receive?token=%s", | 	req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/hooks/git/pre-receive?token=%s", | ||||||
| 		owner.Name, repo.Name, token) | 		owner.Name, repo.Name, token) | ||||||
| 	resp := MakeRequest(t, req, http.StatusOK) | 	resp := MakeRequest(t, req, http.StatusOK) | ||||||
| @@ -107,7 +108,7 @@ func TestAPIGetGitHookNoAccess(t *testing.T) { | |||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | ||||||
|  |  | ||||||
| 	session := loginUser(t, owner.Name) | 	session := loginUser(t, owner.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepoHook) | ||||||
| 	req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/hooks/git/pre-receive?token=%s", | 	req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/hooks/git/pre-receive?token=%s", | ||||||
| 		owner.Name, repo.Name, token) | 		owner.Name, repo.Name, token) | ||||||
| 	MakeRequest(t, req, http.StatusForbidden) | 	MakeRequest(t, req, http.StatusForbidden) | ||||||
| @@ -121,7 +122,7 @@ func TestAPIEditGitHook(t *testing.T) { | |||||||
|  |  | ||||||
| 	// user1 is an admin user | 	// user1 is an admin user | ||||||
| 	session := loginUser(t, "user1") | 	session := loginUser(t, "user1") | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAdminRepoHook) | ||||||
|  |  | ||||||
| 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/hooks/git/pre-receive?token=%s", | 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/hooks/git/pre-receive?token=%s", | ||||||
| 		owner.Name, repo.Name, token) | 		owner.Name, repo.Name, token) | ||||||
| @@ -150,7 +151,7 @@ func TestAPIEditGitHookNoAccess(t *testing.T) { | |||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | ||||||
|  |  | ||||||
| 	session := loginUser(t, owner.Name) | 	session := loginUser(t, owner.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepoHook) | ||||||
| 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/hooks/git/pre-receive?token=%s", | 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/hooks/git/pre-receive?token=%s", | ||||||
| 		owner.Name, repo.Name, token) | 		owner.Name, repo.Name, token) | ||||||
| 	req := NewRequestWithJSON(t, "PATCH", urlStr, &api.EditGitHookOption{ | 	req := NewRequestWithJSON(t, "PATCH", urlStr, &api.EditGitHookOption{ | ||||||
| @@ -167,7 +168,7 @@ func TestAPIDeleteGitHook(t *testing.T) { | |||||||
|  |  | ||||||
| 	// user1 is an admin user | 	// user1 is an admin user | ||||||
| 	session := loginUser(t, "user1") | 	session := loginUser(t, "user1") | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAdminRepoHook) | ||||||
|  |  | ||||||
| 	req := NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/hooks/git/pre-receive?token=%s", | 	req := NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/hooks/git/pre-receive?token=%s", | ||||||
| 		owner.Name, repo.Name, token) | 		owner.Name, repo.Name, token) | ||||||
| @@ -189,7 +190,7 @@ func TestAPIDeleteGitHookNoAccess(t *testing.T) { | |||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | ||||||
|  |  | ||||||
| 	session := loginUser(t, owner.Name) | 	session := loginUser(t, owner.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepoHook) | ||||||
| 	req := NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/hooks/git/pre-receive?token=%s", | 	req := NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/hooks/git/pre-receive?token=%s", | ||||||
| 		owner.Name, repo.Name, token) | 		owner.Name, repo.Name, token) | ||||||
| 	MakeRequest(t, req, http.StatusForbidden) | 	MakeRequest(t, req, http.StatusForbidden) | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| @@ -69,7 +70,7 @@ func TestAPIDeleteTagByName(t *testing.T) { | |||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) | ||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | ||||||
| 	session := loginUser(t, owner.LowerName) | 	session := loginUser(t, owner.LowerName) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/tags/delete-tag?token=%s", | 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/tags/delete-tag?token=%s", | ||||||
| 		owner.Name, repo.Name, token) | 		owner.Name, repo.Name, token) | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| @@ -25,7 +26,7 @@ func TestAPICreateHook(t *testing.T) { | |||||||
|  |  | ||||||
| 	// user1 is an admin user | 	// user1 is an admin user | ||||||
| 	session := loginUser(t, "user1") | 	session := loginUser(t, "user1") | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepoHook) | ||||||
| 	completeURL := func(lastSegment string) string { | 	completeURL := func(lastSegment string) string { | ||||||
| 		return fmt.Sprintf("/api/v1/repos/%s/%s/%s?token=%s", owner.Name, repo.Name, lastSegment, token) | 		return fmt.Sprintf("/api/v1/repos/%s/%s/%s?token=%s", owner.Name, repo.Name, lastSegment, token) | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ import ( | |||||||
| 	"path" | 	"path" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/lfs" | 	"code.gitea.io/gitea/modules/lfs" | ||||||
| @@ -30,7 +31,7 @@ func TestAPIRepoLFSMigrateLocal(t *testing.T) { | |||||||
|  |  | ||||||
| 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) | 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) | ||||||
| 	session := loginUser(t, user.Name) | 	session := loginUser(t, user.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 	req := NewRequestWithJSON(t, "POST", "/api/v1/repos/migrate?token="+token, &api.MigrateRepoOptions{ | 	req := NewRequestWithJSON(t, "POST", "/api/v1/repos/migrate?token="+token, &api.MigrateRepoOptions{ | ||||||
| 		CloneAddr:   path.Join(setting.RepoRootPath, "migration/lfs-test.git"), | 		CloneAddr:   path.Join(setting.RepoRootPath, "migration/lfs-test.git"), | ||||||
|   | |||||||
| @@ -11,6 +11,7 @@ import ( | |||||||
| 	"strings" | 	"strings" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	git_model "code.gitea.io/gitea/models/git" | 	git_model "code.gitea.io/gitea/models/git" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| @@ -59,7 +60,7 @@ func TestAPILFSMediaType(t *testing.T) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func createLFSTestRepository(t *testing.T, name string) *repo_model.Repository { | func createLFSTestRepository(t *testing.T, name string) *repo_model.Repository { | ||||||
| 	ctx := NewAPITestContext(t, "user2", "lfs-"+name+"-repo") | 	ctx := NewAPITestContext(t, "user2", "lfs-"+name+"-repo", auth_model.AccessTokenScopeRepo) | ||||||
| 	t.Run("CreateRepo", doAPICreateRepository(ctx, false)) | 	t.Run("CreateRepo", doAPICreateRepository(ctx, false)) | ||||||
|  |  | ||||||
| 	repo, err := repo_model.GetRepositoryByOwnerAndName(db.DefaultContext, "user2", "lfs-"+name+"-repo") | 	repo, err := repo_model.GetRepositoryByOwnerAndName(db.DefaultContext, "user2", "lfs-"+name+"-repo") | ||||||
|   | |||||||
| @@ -7,6 +7,7 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/tests" | 	"code.gitea.io/gitea/tests" | ||||||
| @@ -19,7 +20,7 @@ func TestAPIReposRaw(t *testing.T) { | |||||||
| 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | ||||||
| 	// Login as User2. | 	// Login as User2. | ||||||
| 	session := loginUser(t, user.Name) | 	session := loginUser(t, user.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 	for _, ref := range [...]string{ | 	for _, ref := range [...]string{ | ||||||
| 		"master", // Branch | 		"master", // Branch | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| @@ -22,7 +23,7 @@ func TestAPIRepoTags(t *testing.T) { | |||||||
| 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | ||||||
| 	// Login as User2. | 	// Login as User2. | ||||||
| 	session := loginUser(t, user.Name) | 	session := loginUser(t, user.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 	repoName := "repo1" | 	repoName := "repo1" | ||||||
|  |  | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unit" | 	"code.gitea.io/gitea/models/unit" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| @@ -27,7 +28,7 @@ func TestAPIRepoTeams(t *testing.T) { | |||||||
| 	// user4 | 	// user4 | ||||||
| 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) | 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) | ||||||
| 	session := loginUser(t, user.Name) | 	session := loginUser(t, user.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 	// ListTeams | 	// ListTeams | ||||||
| 	url := fmt.Sprintf("/api/v1/repos/%s/teams?token=%s", publicOrgRepo.FullName(), token) | 	url := fmt.Sprintf("/api/v1/repos/%s/teams?token=%s", publicOrgRepo.FullName(), token) | ||||||
| @@ -67,7 +68,7 @@ func TestAPIRepoTeams(t *testing.T) { | |||||||
| 	// AddTeam with user2 | 	// AddTeam with user2 | ||||||
| 	user = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | 	user = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | ||||||
| 	session = loginUser(t, user.Name) | 	session = loginUser(t, user.Name) | ||||||
| 	token = getTokenForLoggedInUser(t, session) | 	token = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
| 	url = fmt.Sprintf("/api/v1/repos/%s/teams/%s?token=%s", publicOrgRepo.FullName(), "team1", token) | 	url = fmt.Sprintf("/api/v1/repos/%s/teams/%s?token=%s", publicOrgRepo.FullName(), "team1", token) | ||||||
| 	req = NewRequest(t, "PUT", url) | 	req = NewRequest(t, "PUT", url) | ||||||
| 	MakeRequest(t, req, http.StatusNoContent) | 	MakeRequest(t, req, http.StatusNoContent) | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ import ( | |||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	access_model "code.gitea.io/gitea/models/perm/access" | 	access_model "code.gitea.io/gitea/models/perm/access" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| @@ -286,24 +287,17 @@ func TestAPIOrgRepos(t *testing.T) { | |||||||
| 		count           int | 		count           int | ||||||
| 		includesPrivate bool | 		includesPrivate bool | ||||||
| 	}{ | 	}{ | ||||||
| 		nil:   {count: 1}, | 		user:  {count: 1}, | ||||||
| 		user:  {count: 3, includesPrivate: true}, | 		user:  {count: 3, includesPrivate: true}, | ||||||
| 		user2: {count: 3, includesPrivate: true}, | 		user2: {count: 3, includesPrivate: true}, | ||||||
| 		user3: {count: 1}, | 		user3: {count: 1}, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for userToLogin, expected := range expectedResults { | 	for userToLogin, expected := range expectedResults { | ||||||
| 		var session *TestSession | 		testName := fmt.Sprintf("LoggedUser%d", userToLogin.ID) | ||||||
| 		var testName string | 		session := loginUser(t, userToLogin.Name) | ||||||
| 		var token string | 		token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadOrg) | ||||||
| 		if userToLogin != nil && userToLogin.ID > 0 { |  | ||||||
| 			testName = fmt.Sprintf("LoggedUser%d", userToLogin.ID) |  | ||||||
| 			session = loginUser(t, userToLogin.Name) |  | ||||||
| 			token = getTokenForLoggedInUser(t, session) |  | ||||||
| 		} else { |  | ||||||
| 			testName = "AnonymousUser" |  | ||||||
| 			session = emptyTestSession(t) |  | ||||||
| 		} |  | ||||||
| 		t.Run(testName, func(t *testing.T) { | 		t.Run(testName, func(t *testing.T) { | ||||||
| 			req := NewRequestf(t, "GET", "/api/v1/orgs/%s/repos?token="+token, sourceOrg.Name) | 			req := NewRequestf(t, "GET", "/api/v1/orgs/%s/repos?token="+token, sourceOrg.Name) | ||||||
| 			resp := MakeRequest(t, req, http.StatusOK) | 			resp := MakeRequest(t, req, http.StatusOK) | ||||||
| @@ -324,7 +318,7 @@ func TestAPIGetRepoByIDUnauthorized(t *testing.T) { | |||||||
| 	defer tests.PrepareTestEnv(t)() | 	defer tests.PrepareTestEnv(t)() | ||||||
| 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) | 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) | ||||||
| 	session := loginUser(t, user.Name) | 	session := loginUser(t, user.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
| 	req := NewRequestf(t, "GET", "/api/v1/repositories/2?token="+token) | 	req := NewRequestf(t, "GET", "/api/v1/repositories/2?token="+token) | ||||||
| 	MakeRequest(t, req, http.StatusNotFound) | 	MakeRequest(t, req, http.StatusNotFound) | ||||||
| } | } | ||||||
| @@ -348,7 +342,7 @@ func TestAPIRepoMigrate(t *testing.T) { | |||||||
| 	for _, testCase := range testCases { | 	for _, testCase := range testCases { | ||||||
| 		user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: testCase.ctxUserID}) | 		user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: testCase.ctxUserID}) | ||||||
| 		session := loginUser(t, user.Name) | 		session := loginUser(t, user.Name) | ||||||
| 		token := getTokenForLoggedInUser(t, session) | 		token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
| 		req := NewRequestWithJSON(t, "POST", "/api/v1/repos/migrate?token="+token, &api.MigrateRepoOptions{ | 		req := NewRequestWithJSON(t, "POST", "/api/v1/repos/migrate?token="+token, &api.MigrateRepoOptions{ | ||||||
| 			CloneAddr:   testCase.cloneURL, | 			CloneAddr:   testCase.cloneURL, | ||||||
| 			RepoOwnerID: testCase.userID, | 			RepoOwnerID: testCase.userID, | ||||||
| @@ -378,7 +372,7 @@ func TestAPIRepoMigrateConflict(t *testing.T) { | |||||||
|  |  | ||||||
| func testAPIRepoMigrateConflict(t *testing.T, u *url.URL) { | func testAPIRepoMigrateConflict(t *testing.T, u *url.URL) { | ||||||
| 	username := "user2" | 	username := "user2" | ||||||
| 	baseAPITestContext := NewAPITestContext(t, username, "repo1") | 	baseAPITestContext := NewAPITestContext(t, username, "repo1", auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 	u.Path = baseAPITestContext.GitPath() | 	u.Path = baseAPITestContext.GitPath() | ||||||
|  |  | ||||||
| @@ -413,7 +407,7 @@ func TestAPIMirrorSyncNonMirrorRepo(t *testing.T) { | |||||||
| 	defer tests.PrepareTestEnv(t)() | 	defer tests.PrepareTestEnv(t)() | ||||||
|  |  | ||||||
| 	session := loginUser(t, "user2") | 	session := loginUser(t, "user2") | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 	var repo api.Repository | 	var repo api.Repository | ||||||
| 	req := NewRequest(t, "GET", "/api/v1/repos/user2/repo1") | 	req := NewRequest(t, "GET", "/api/v1/repos/user2/repo1") | ||||||
| @@ -445,7 +439,7 @@ func TestAPIOrgRepoCreate(t *testing.T) { | |||||||
| 	for _, testCase := range testCases { | 	for _, testCase := range testCases { | ||||||
| 		user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: testCase.ctxUserID}) | 		user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: testCase.ctxUserID}) | ||||||
| 		session := loginUser(t, user.Name) | 		session := loginUser(t, user.Name) | ||||||
| 		token := getTokenForLoggedInUser(t, session) | 		token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAdminOrg) | ||||||
| 		req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/org/%s/repos?token="+token, testCase.orgName), &api.CreateRepoOption{ | 		req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/org/%s/repos?token="+token, testCase.orgName), &api.CreateRepoOption{ | ||||||
| 			Name: testCase.repoName, | 			Name: testCase.repoName, | ||||||
| 		}) | 		}) | ||||||
| @@ -459,7 +453,7 @@ func TestAPIRepoCreateConflict(t *testing.T) { | |||||||
|  |  | ||||||
| func testAPIRepoCreateConflict(t *testing.T, u *url.URL) { | func testAPIRepoCreateConflict(t *testing.T, u *url.URL) { | ||||||
| 	username := "user2" | 	username := "user2" | ||||||
| 	baseAPITestContext := NewAPITestContext(t, username, "repo1") | 	baseAPITestContext := NewAPITestContext(t, username, "repo1", auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 	u.Path = baseAPITestContext.GitPath() | 	u.Path = baseAPITestContext.GitPath() | ||||||
|  |  | ||||||
| @@ -509,7 +503,7 @@ func TestAPIRepoTransfer(t *testing.T) { | |||||||
| 	// create repo to move | 	// create repo to move | ||||||
| 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) | 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) | ||||||
| 	session := loginUser(t, user.Name) | 	session := loginUser(t, user.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
| 	repoName := "moveME" | 	repoName := "moveME" | ||||||
| 	apiRepo := new(api.Repository) | 	apiRepo := new(api.Repository) | ||||||
| 	req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/user/repos?token=%s", token), &api.CreateRepoOption{ | 	req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/user/repos?token=%s", token), &api.CreateRepoOption{ | ||||||
| @@ -527,7 +521,7 @@ func TestAPIRepoTransfer(t *testing.T) { | |||||||
| 		user = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: testCase.ctxUserID}) | 		user = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: testCase.ctxUserID}) | ||||||
| 		repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: apiRepo.ID}) | 		repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: apiRepo.ID}) | ||||||
| 		session = loginUser(t, user.Name) | 		session = loginUser(t, user.Name) | ||||||
| 		token = getTokenForLoggedInUser(t, session) | 		token = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
| 		req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer?token=%s", repo.OwnerName, repo.Name, token), &api.TransferRepoOption{ | 		req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer?token=%s", repo.OwnerName, repo.Name, token), &api.TransferRepoOption{ | ||||||
| 			NewOwner: testCase.newOwner, | 			NewOwner: testCase.newOwner, | ||||||
| 			TeamIDs:  testCase.teams, | 			TeamIDs:  testCase.teams, | ||||||
| @@ -544,7 +538,7 @@ func transfer(t *testing.T) *repo_model.Repository { | |||||||
| 	// create repo to move | 	// create repo to move | ||||||
| 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | ||||||
| 	session := loginUser(t, user.Name) | 	session := loginUser(t, user.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
| 	repoName := "moveME" | 	repoName := "moveME" | ||||||
| 	apiRepo := new(api.Repository) | 	apiRepo := new(api.Repository) | ||||||
| 	req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/user/repos?token=%s", token), &api.CreateRepoOption{ | 	req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/user/repos?token=%s", token), &api.CreateRepoOption{ | ||||||
| @@ -574,7 +568,7 @@ func TestAPIAcceptTransfer(t *testing.T) { | |||||||
|  |  | ||||||
| 	// try to accept with not authorized user | 	// try to accept with not authorized user | ||||||
| 	session := loginUser(t, "user2") | 	session := loginUser(t, "user2") | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
| 	req := NewRequest(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer/reject?token=%s", repo.OwnerName, repo.Name, token)) | 	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) | 	MakeRequest(t, req, http.StatusForbidden) | ||||||
|  |  | ||||||
| @@ -584,7 +578,7 @@ func TestAPIAcceptTransfer(t *testing.T) { | |||||||
|  |  | ||||||
| 	// accept transfer | 	// accept transfer | ||||||
| 	session = loginUser(t, "user4") | 	session = loginUser(t, "user4") | ||||||
| 	token = getTokenForLoggedInUser(t, session) | 	token = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 	req = NewRequest(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer/accept?token=%s", repo.OwnerName, repo.Name, token)) | 	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) | 	resp := MakeRequest(t, req, http.StatusAccepted) | ||||||
| @@ -600,7 +594,7 @@ func TestAPIRejectTransfer(t *testing.T) { | |||||||
|  |  | ||||||
| 	// try to reject with not authorized user | 	// try to reject with not authorized user | ||||||
| 	session := loginUser(t, "user2") | 	session := loginUser(t, "user2") | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
| 	req := NewRequest(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer/reject?token=%s", repo.OwnerName, repo.Name, token)) | 	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) | 	MakeRequest(t, req, http.StatusForbidden) | ||||||
|  |  | ||||||
| @@ -610,7 +604,7 @@ func TestAPIRejectTransfer(t *testing.T) { | |||||||
|  |  | ||||||
| 	// reject transfer | 	// reject transfer | ||||||
| 	session = loginUser(t, "user4") | 	session = loginUser(t, "user4") | ||||||
| 	token = getTokenForLoggedInUser(t, session) | 	token = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 	req = NewRequest(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer/reject?token=%s", repo.OwnerName, repo.Name, token)) | 	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) | 	resp := MakeRequest(t, req, http.StatusOK) | ||||||
| @@ -624,7 +618,7 @@ func TestAPIGenerateRepo(t *testing.T) { | |||||||
|  |  | ||||||
| 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) | 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) | ||||||
| 	session := loginUser(t, user.Name) | 	session := loginUser(t, user.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 	templateRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 44}) | 	templateRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 44}) | ||||||
|  |  | ||||||
| @@ -660,7 +654,7 @@ func TestAPIRepoGetReviewers(t *testing.T) { | |||||||
| 	defer tests.PrepareTestEnv(t)() | 	defer tests.PrepareTestEnv(t)() | ||||||
| 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | ||||||
| 	session := loginUser(t, user.Name) | 	session := loginUser(t, user.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) | 	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) | 	req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/reviewers?token=%s", user.Name, repo.Name, token) | ||||||
| @@ -674,7 +668,7 @@ func TestAPIRepoGetAssignees(t *testing.T) { | |||||||
| 	defer tests.PrepareTestEnv(t)() | 	defer tests.PrepareTestEnv(t)() | ||||||
| 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | ||||||
| 	session := loginUser(t, user.Name) | 	session := loginUser(t, user.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) | 	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) | 	req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/assignees?token=%s", user.Name, repo.Name, token) | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ import ( | |||||||
| 	"net/url" | 	"net/url" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| @@ -59,7 +60,7 @@ func TestAPIRepoTopic(t *testing.T) { | |||||||
| 	repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}) | 	repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}) | ||||||
|  |  | ||||||
| 	// Get user2's token | 	// Get user2's token | ||||||
| 	token2 := getUserToken(t, user2.Name) | 	token2 := getUserToken(t, user2.Name, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 	// Test read topics using login | 	// Test read topics using login | ||||||
| 	url := fmt.Sprintf("/api/v1/repos/%s/%s/topics", user2.Name, repo2.Name) | 	url := fmt.Sprintf("/api/v1/repos/%s/%s/topics", user2.Name, repo2.Name) | ||||||
| @@ -139,7 +140,7 @@ func TestAPIRepoTopic(t *testing.T) { | |||||||
| 	MakeRequest(t, req, http.StatusNotFound) | 	MakeRequest(t, req, http.StatusNotFound) | ||||||
|  |  | ||||||
| 	// Get user4's token | 	// Get user4's token | ||||||
| 	token4 := getUserToken(t, user4.Name) | 	token4 := getUserToken(t, user4.Name, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 	// Test read topics with write access | 	// Test read topics with write access | ||||||
| 	url = fmt.Sprintf("/api/v1/repos/%s/%s/topics?token=%s", user3.Name, repo3.Name, token4) | 	url = fmt.Sprintf("/api/v1/repos/%s/%s/topics?token=%s", user3.Name, repo3.Name, token4) | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ import ( | |||||||
| 	"sort" | 	"sort" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/organization" | 	"code.gitea.io/gitea/models/organization" | ||||||
| 	"code.gitea.io/gitea/models/repo" | 	"code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unit" | 	"code.gitea.io/gitea/models/unit" | ||||||
| @@ -29,7 +30,7 @@ func TestAPITeam(t *testing.T) { | |||||||
| 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: teamUser.UID}) | 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: teamUser.UID}) | ||||||
|  |  | ||||||
| 	session := loginUser(t, user.Name) | 	session := loginUser(t, user.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAdminOrg) | ||||||
| 	req := NewRequestf(t, "GET", "/api/v1/teams/%d?token="+token, teamUser.TeamID) | 	req := NewRequestf(t, "GET", "/api/v1/teams/%d?token="+token, teamUser.TeamID) | ||||||
| 	resp := MakeRequest(t, req, http.StatusOK) | 	resp := MakeRequest(t, req, http.StatusOK) | ||||||
|  |  | ||||||
| @@ -43,7 +44,7 @@ func TestAPITeam(t *testing.T) { | |||||||
| 	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: teamUser2.UID}) | 	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: teamUser2.UID}) | ||||||
|  |  | ||||||
| 	session = loginUser(t, user2.Name) | 	session = loginUser(t, user2.Name) | ||||||
| 	token = getTokenForLoggedInUser(t, session) | 	token = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadOrg) | ||||||
| 	req = NewRequestf(t, "GET", "/api/v1/teams/%d?token="+token, teamUser.TeamID) | 	req = NewRequestf(t, "GET", "/api/v1/teams/%d?token="+token, teamUser.TeamID) | ||||||
| 	_ = MakeRequest(t, req, http.StatusForbidden) | 	_ = MakeRequest(t, req, http.StatusForbidden) | ||||||
|  |  | ||||||
| @@ -53,7 +54,7 @@ func TestAPITeam(t *testing.T) { | |||||||
| 	// Get an admin user able to create, update and delete teams. | 	// Get an admin user able to create, update and delete teams. | ||||||
| 	user = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) | 	user = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) | ||||||
| 	session = loginUser(t, user.Name) | 	session = loginUser(t, user.Name) | ||||||
| 	token = getTokenForLoggedInUser(t, session) | 	token = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAdminOrg) | ||||||
|  |  | ||||||
| 	org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 6}) | 	org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 6}) | ||||||
|  |  | ||||||
| @@ -227,7 +228,7 @@ func TestAPITeamSearch(t *testing.T) { | |||||||
|  |  | ||||||
| 	var results TeamSearchResults | 	var results TeamSearchResults | ||||||
|  |  | ||||||
| 	token := getUserToken(t, user.Name) | 	token := getUserToken(t, user.Name, auth_model.AccessTokenScopeReadOrg) | ||||||
| 	req := NewRequestf(t, "GET", "/api/v1/orgs/%s/teams/search?q=%s&token=%s", org.Name, "_team", token) | 	req := NewRequestf(t, "GET", "/api/v1/orgs/%s/teams/search?q=%s&token=%s", org.Name, "_team", token) | ||||||
| 	resp := MakeRequest(t, req, http.StatusOK) | 	resp := MakeRequest(t, req, http.StatusOK) | ||||||
| 	DecodeJSON(t, resp, &results) | 	DecodeJSON(t, resp, &results) | ||||||
| @@ -237,7 +238,7 @@ func TestAPITeamSearch(t *testing.T) { | |||||||
|  |  | ||||||
| 	// no access if not organization member | 	// no access if not organization member | ||||||
| 	user5 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}) | 	user5 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}) | ||||||
| 	token5 := getUserToken(t, user5.Name) | 	token5 := getUserToken(t, user5.Name, auth_model.AccessTokenScopeReadOrg) | ||||||
|  |  | ||||||
| 	req = NewRequestf(t, "GET", "/api/v1/orgs/%s/teams/search?q=%s&token=%s", org.Name, "team", token5) | 	req = NewRequestf(t, "GET", "/api/v1/orgs/%s/teams/search?q=%s&token=%s", org.Name, "team", token5) | ||||||
| 	MakeRequest(t, req, http.StatusForbidden) | 	MakeRequest(t, req, http.StatusForbidden) | ||||||
| @@ -252,7 +253,7 @@ func TestAPIGetTeamRepo(t *testing.T) { | |||||||
|  |  | ||||||
| 	var results api.Repository | 	var results api.Repository | ||||||
|  |  | ||||||
| 	token := getUserToken(t, user.Name) | 	token := getUserToken(t, user.Name, auth_model.AccessTokenScopeReadOrg) | ||||||
| 	req := NewRequestf(t, "GET", "/api/v1/teams/%d/repos/%s/?token=%s", team.ID, teamRepo.FullName(), token) | 	req := NewRequestf(t, "GET", "/api/v1/teams/%d/repos/%s/?token=%s", team.ID, teamRepo.FullName(), token) | ||||||
| 	resp := MakeRequest(t, req, http.StatusOK) | 	resp := MakeRequest(t, req, http.StatusOK) | ||||||
| 	DecodeJSON(t, resp, &results) | 	DecodeJSON(t, resp, &results) | ||||||
| @@ -260,7 +261,7 @@ func TestAPIGetTeamRepo(t *testing.T) { | |||||||
|  |  | ||||||
| 	// no access if not organization member | 	// no access if not organization member | ||||||
| 	user5 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}) | 	user5 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}) | ||||||
| 	token5 := getUserToken(t, user5.Name) | 	token5 := getUserToken(t, user5.Name, auth_model.AccessTokenScopeReadOrg) | ||||||
|  |  | ||||||
| 	req = NewRequestf(t, "GET", "/api/v1/teams/%d/repos/%s/?token=%s", team.ID, teamRepo.FullName(), token5) | 	req = NewRequestf(t, "GET", "/api/v1/teams/%d/repos/%s/?token=%s", team.ID, teamRepo.FullName(), token5) | ||||||
| 	MakeRequest(t, req, http.StatusNotFound) | 	MakeRequest(t, req, http.StatusNotFound) | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ import ( | |||||||
| 	"testing" | 	"testing" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	api "code.gitea.io/gitea/modules/structs" | 	api "code.gitea.io/gitea/modules/structs" | ||||||
| @@ -22,7 +23,7 @@ func TestAPITeamUser(t *testing.T) { | |||||||
|  |  | ||||||
| 	normalUsername := "user2" | 	normalUsername := "user2" | ||||||
| 	session := loginUser(t, normalUsername) | 	session := loginUser(t, normalUsername) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadOrg) | ||||||
| 	req := NewRequest(t, "GET", "/api/v1/teams/1/members/user1?token="+token) | 	req := NewRequest(t, "GET", "/api/v1/teams/1/members/user1?token="+token) | ||||||
| 	MakeRequest(t, req, http.StatusNotFound) | 	MakeRequest(t, req, http.StatusNotFound) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -7,6 +7,7 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	api "code.gitea.io/gitea/modules/structs" | 	api "code.gitea.io/gitea/modules/structs" | ||||||
| 	"code.gitea.io/gitea/tests" | 	"code.gitea.io/gitea/tests" | ||||||
|  |  | ||||||
| @@ -18,7 +19,7 @@ func TestAPIListEmails(t *testing.T) { | |||||||
|  |  | ||||||
| 	normalUsername := "user2" | 	normalUsername := "user2" | ||||||
| 	session := loginUser(t, normalUsername) | 	session := loginUser(t, normalUsername) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadUser) | ||||||
|  |  | ||||||
| 	req := NewRequest(t, "GET", "/api/v1/user/emails?token="+token) | 	req := NewRequest(t, "GET", "/api/v1/user/emails?token="+token) | ||||||
| 	resp := MakeRequest(t, req, http.StatusOK) | 	resp := MakeRequest(t, req, http.StatusOK) | ||||||
| @@ -45,7 +46,7 @@ func TestAPIAddEmail(t *testing.T) { | |||||||
|  |  | ||||||
| 	normalUsername := "user2" | 	normalUsername := "user2" | ||||||
| 	session := loginUser(t, normalUsername) | 	session := loginUser(t, normalUsername) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeUser) | ||||||
|  |  | ||||||
| 	opts := api.CreateEmailOption{ | 	opts := api.CreateEmailOption{ | ||||||
| 		Emails: []string{"user101@example.com"}, | 		Emails: []string{"user101@example.com"}, | ||||||
| @@ -82,7 +83,7 @@ func TestAPIDeleteEmail(t *testing.T) { | |||||||
|  |  | ||||||
| 	normalUsername := "user2" | 	normalUsername := "user2" | ||||||
| 	session := loginUser(t, normalUsername) | 	session := loginUser(t, normalUsername) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeUser) | ||||||
|  |  | ||||||
| 	opts := api.DeleteEmailOption{ | 	opts := api.DeleteEmailOption{ | ||||||
| 		Emails: []string{"user2-3@example.com"}, | 		Emails: []string{"user2-3@example.com"}, | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	api "code.gitea.io/gitea/modules/structs" | 	api "code.gitea.io/gitea/modules/structs" | ||||||
| 	"code.gitea.io/gitea/tests" | 	"code.gitea.io/gitea/tests" | ||||||
|  |  | ||||||
| @@ -24,7 +25,7 @@ func TestAPIFollow(t *testing.T) { | |||||||
| 	token1 := getTokenForLoggedInUser(t, session1) | 	token1 := getTokenForLoggedInUser(t, session1) | ||||||
|  |  | ||||||
| 	session2 := loginUser(t, user2) | 	session2 := loginUser(t, user2) | ||||||
| 	token2 := getTokenForLoggedInUser(t, session2) | 	token2 := getTokenForLoggedInUser(t, session2, auth_model.AccessTokenScopeUserFollow) | ||||||
|  |  | ||||||
| 	t.Run("Follow", func(t *testing.T) { | 	t.Run("Follow", func(t *testing.T) { | ||||||
| 		defer tests.PrintCurrentTest(t)() | 		defer tests.PrintCurrentTest(t)() | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	api "code.gitea.io/gitea/modules/structs" | 	api "code.gitea.io/gitea/modules/structs" | ||||||
| 	"code.gitea.io/gitea/tests" | 	"code.gitea.io/gitea/tests" | ||||||
|  |  | ||||||
| @@ -32,7 +33,7 @@ func sampleTest(t *testing.T, auoptc apiUserOrgPermTestCase) { | |||||||
| 	defer tests.PrepareTestEnv(t)() | 	defer tests.PrepareTestEnv(t)() | ||||||
|  |  | ||||||
| 	session := loginUser(t, auoptc.LoginUser) | 	session := loginUser(t, auoptc.LoginUser) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadOrg) | ||||||
|  |  | ||||||
| 	req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/users/%s/orgs/%s/permissions?token=%s", auoptc.User, auoptc.Organization, token)) | 	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) | 	resp := MakeRequest(t, req, http.StatusOK) | ||||||
| @@ -125,7 +126,7 @@ func TestUnknowUser(t *testing.T) { | |||||||
| 	defer tests.PrepareTestEnv(t)() | 	defer tests.PrepareTestEnv(t)() | ||||||
|  |  | ||||||
| 	session := loginUser(t, "user1") | 	session := loginUser(t, "user1") | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadOrg) | ||||||
|  |  | ||||||
| 	req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/users/unknow/orgs/org25/permissions?token=%s", token)) | 	req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/users/unknow/orgs/org25/permissions?token=%s", token)) | ||||||
| 	resp := MakeRequest(t, req, http.StatusNotFound) | 	resp := MakeRequest(t, req, http.StatusNotFound) | ||||||
| @@ -139,7 +140,7 @@ func TestUnknowOrganization(t *testing.T) { | |||||||
| 	defer tests.PrepareTestEnv(t)() | 	defer tests.PrepareTestEnv(t)() | ||||||
|  |  | ||||||
| 	session := loginUser(t, "user1") | 	session := loginUser(t, "user1") | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadOrg) | ||||||
|  |  | ||||||
| 	req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/users/user1/orgs/unknow/permissions?token=%s", token)) | 	req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/users/user1/orgs/unknow/permissions?token=%s", token)) | ||||||
| 	resp := MakeRequest(t, req, http.StatusNotFound) | 	resp := MakeRequest(t, req, http.StatusNotFound) | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	api "code.gitea.io/gitea/modules/structs" | 	api "code.gitea.io/gitea/modules/structs" | ||||||
| @@ -61,15 +62,14 @@ func TestUserOrgs(t *testing.T) { | |||||||
| 	orgs = getUserOrgs(t, unrelatedUsername, privateMemberUsername) | 	orgs = getUserOrgs(t, unrelatedUsername, privateMemberUsername) | ||||||
| 	assert.Len(t, orgs, 0) | 	assert.Len(t, orgs, 0) | ||||||
|  |  | ||||||
| 	// not authenticated call also should hide org membership | 	// not authenticated call should not be allowed | ||||||
| 	orgs = getUserOrgs(t, "", privateMemberUsername) | 	testUserOrgsUnauthenticated(t, privateMemberUsername) | ||||||
| 	assert.Len(t, orgs, 0) |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func getUserOrgs(t *testing.T, userDoer, userCheck string) (orgs []*api.Organization) { | func getUserOrgs(t *testing.T, userDoer, userCheck string) (orgs []*api.Organization) { | ||||||
| 	token := "" | 	token := "" | ||||||
| 	if len(userDoer) != 0 { | 	if len(userDoer) != 0 { | ||||||
| 		token = getUserToken(t, userDoer) | 		token = getUserToken(t, userDoer, auth_model.AccessTokenScopeReadOrg) | ||||||
| 	} | 	} | ||||||
| 	urlStr := fmt.Sprintf("/api/v1/users/%s/orgs?token=%s", userCheck, token) | 	urlStr := fmt.Sprintf("/api/v1/users/%s/orgs?token=%s", userCheck, token) | ||||||
| 	req := NewRequest(t, "GET", urlStr) | 	req := NewRequest(t, "GET", urlStr) | ||||||
| @@ -78,6 +78,12 @@ func getUserOrgs(t *testing.T, userDoer, userCheck string) (orgs []*api.Organiza | |||||||
| 	return orgs | 	return orgs | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func testUserOrgsUnauthenticated(t *testing.T, userCheck string) { | ||||||
|  | 	session := emptyTestSession(t) | ||||||
|  | 	req := NewRequestf(t, "GET", "/api/v1/users/%s/orgs", userCheck) | ||||||
|  | 	session.MakeRequest(t, req, http.StatusUnauthorized) | ||||||
|  | } | ||||||
|  |  | ||||||
| func TestMyOrgs(t *testing.T) { | func TestMyOrgs(t *testing.T) { | ||||||
| 	defer tests.PrepareTestEnv(t)() | 	defer tests.PrepareTestEnv(t)() | ||||||
|  |  | ||||||
| @@ -85,7 +91,7 @@ func TestMyOrgs(t *testing.T) { | |||||||
| 	MakeRequest(t, req, http.StatusUnauthorized) | 	MakeRequest(t, req, http.StatusUnauthorized) | ||||||
|  |  | ||||||
| 	normalUsername := "user2" | 	normalUsername := "user2" | ||||||
| 	token := getUserToken(t, normalUsername) | 	token := getUserToken(t, normalUsername, auth_model.AccessTokenScopeReadOrg) | ||||||
| 	req = NewRequest(t, "GET", "/api/v1/user/orgs?token="+token) | 	req = NewRequest(t, "GET", "/api/v1/user/orgs?token="+token) | ||||||
| 	resp := MakeRequest(t, req, http.StatusOK) | 	resp := MakeRequest(t, req, http.StatusOK) | ||||||
| 	var orgs []*api.Organization | 	var orgs []*api.Organization | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	api "code.gitea.io/gitea/modules/structs" | 	api "code.gitea.io/gitea/modules/structs" | ||||||
| 	"code.gitea.io/gitea/tests" | 	"code.gitea.io/gitea/tests" | ||||||
|  |  | ||||||
| @@ -22,11 +23,12 @@ func TestAPIStar(t *testing.T) { | |||||||
|  |  | ||||||
| 	session := loginUser(t, user) | 	session := loginUser(t, user) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session) | ||||||
|  | 	tokenWithRepoScope := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 	t.Run("Star", func(t *testing.T) { | 	t.Run("Star", func(t *testing.T) { | ||||||
| 		defer tests.PrintCurrentTest(t)() | 		defer tests.PrintCurrentTest(t)() | ||||||
|  |  | ||||||
| 		req := NewRequest(t, "PUT", fmt.Sprintf("/api/v1/user/starred/%s?token=%s", repo, token)) | 		req := NewRequest(t, "PUT", fmt.Sprintf("/api/v1/user/starred/%s?token=%s", repo, tokenWithRepoScope)) | ||||||
| 		MakeRequest(t, req, http.StatusNoContent) | 		MakeRequest(t, req, http.StatusNoContent) | ||||||
| 	}) | 	}) | ||||||
|  |  | ||||||
| @@ -47,7 +49,7 @@ func TestAPIStar(t *testing.T) { | |||||||
| 	t.Run("GetMyStarredRepos", func(t *testing.T) { | 	t.Run("GetMyStarredRepos", func(t *testing.T) { | ||||||
| 		defer tests.PrintCurrentTest(t)() | 		defer tests.PrintCurrentTest(t)() | ||||||
|  |  | ||||||
| 		req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/starred?token=%s", token)) | 		req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/starred?token=%s", tokenWithRepoScope)) | ||||||
| 		resp := MakeRequest(t, req, http.StatusOK) | 		resp := MakeRequest(t, req, http.StatusOK) | ||||||
|  |  | ||||||
| 		assert.Equal(t, "1", resp.Header().Get("X-Total-Count")) | 		assert.Equal(t, "1", resp.Header().Get("X-Total-Count")) | ||||||
| @@ -61,17 +63,17 @@ func TestAPIStar(t *testing.T) { | |||||||
| 	t.Run("IsStarring", func(t *testing.T) { | 	t.Run("IsStarring", func(t *testing.T) { | ||||||
| 		defer tests.PrintCurrentTest(t)() | 		defer tests.PrintCurrentTest(t)() | ||||||
|  |  | ||||||
| 		req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/starred/%s?token=%s", repo, token)) | 		req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/starred/%s?token=%s", repo, tokenWithRepoScope)) | ||||||
| 		MakeRequest(t, req, http.StatusNoContent) | 		MakeRequest(t, req, http.StatusNoContent) | ||||||
|  |  | ||||||
| 		req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/starred/%s?token=%s", repo+"notexisting", token)) | 		req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/starred/%s?token=%s", repo+"notexisting", tokenWithRepoScope)) | ||||||
| 		MakeRequest(t, req, http.StatusNotFound) | 		MakeRequest(t, req, http.StatusNotFound) | ||||||
| 	}) | 	}) | ||||||
|  |  | ||||||
| 	t.Run("Unstar", func(t *testing.T) { | 	t.Run("Unstar", func(t *testing.T) { | ||||||
| 		defer tests.PrintCurrentTest(t)() | 		defer tests.PrintCurrentTest(t)() | ||||||
|  |  | ||||||
| 		req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/user/starred/%s?token=%s", repo, token)) | 		req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/user/starred/%s?token=%s", repo, tokenWithRepoScope)) | ||||||
| 		MakeRequest(t, req, http.StatusNoContent) | 		MakeRequest(t, req, http.StatusNoContent) | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	api "code.gitea.io/gitea/modules/structs" | 	api "code.gitea.io/gitea/modules/structs" | ||||||
| 	"code.gitea.io/gitea/tests" | 	"code.gitea.io/gitea/tests" | ||||||
|  |  | ||||||
| @@ -22,11 +23,12 @@ func TestAPIWatch(t *testing.T) { | |||||||
|  |  | ||||||
| 	session := loginUser(t, user) | 	session := loginUser(t, user) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session) | ||||||
|  | 	tokenWithRepoScope := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 	t.Run("Watch", func(t *testing.T) { | 	t.Run("Watch", func(t *testing.T) { | ||||||
| 		defer tests.PrintCurrentTest(t)() | 		defer tests.PrintCurrentTest(t)() | ||||||
|  |  | ||||||
| 		req := NewRequest(t, "PUT", fmt.Sprintf("/api/v1/repos/%s/subscription?token=%s", repo, token)) | 		req := NewRequest(t, "PUT", fmt.Sprintf("/api/v1/repos/%s/subscription?token=%s", repo, tokenWithRepoScope)) | ||||||
| 		MakeRequest(t, req, http.StatusOK) | 		MakeRequest(t, req, http.StatusOK) | ||||||
| 	}) | 	}) | ||||||
|  |  | ||||||
| @@ -47,7 +49,7 @@ func TestAPIWatch(t *testing.T) { | |||||||
| 	t.Run("GetMyWatchedRepos", func(t *testing.T) { | 	t.Run("GetMyWatchedRepos", func(t *testing.T) { | ||||||
| 		defer tests.PrintCurrentTest(t)() | 		defer tests.PrintCurrentTest(t)() | ||||||
|  |  | ||||||
| 		req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/subscriptions?token=%s", token)) | 		req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/subscriptions?token=%s", tokenWithRepoScope)) | ||||||
| 		resp := MakeRequest(t, req, http.StatusOK) | 		resp := MakeRequest(t, req, http.StatusOK) | ||||||
|  |  | ||||||
| 		assert.Equal(t, "1", resp.Header().Get("X-Total-Count")) | 		assert.Equal(t, "1", resp.Header().Get("X-Total-Count")) | ||||||
| @@ -61,17 +63,17 @@ func TestAPIWatch(t *testing.T) { | |||||||
| 	t.Run("IsWatching", func(t *testing.T) { | 	t.Run("IsWatching", func(t *testing.T) { | ||||||
| 		defer tests.PrintCurrentTest(t)() | 		defer tests.PrintCurrentTest(t)() | ||||||
|  |  | ||||||
| 		req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/subscription?token=%s", repo, token)) | 		req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/subscription?token=%s", repo, tokenWithRepoScope)) | ||||||
| 		MakeRequest(t, req, http.StatusOK) | 		MakeRequest(t, req, http.StatusOK) | ||||||
|  |  | ||||||
| 		req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/subscription?token=%s", repo+"notexisting", token)) | 		req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/subscription?token=%s", repo+"notexisting", tokenWithRepoScope)) | ||||||
| 		MakeRequest(t, req, http.StatusNotFound) | 		MakeRequest(t, req, http.StatusNotFound) | ||||||
| 	}) | 	}) | ||||||
|  |  | ||||||
| 	t.Run("Unwatch", func(t *testing.T) { | 	t.Run("Unwatch", func(t *testing.T) { | ||||||
| 		defer tests.PrintCurrentTest(t)() | 		defer tests.PrintCurrentTest(t)() | ||||||
|  |  | ||||||
| 		req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/repos/%s/subscription?token=%s", repo, token)) | 		req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/repos/%s/subscription?token=%s", repo, tokenWithRepoScope)) | ||||||
| 		MakeRequest(t, req, http.StatusNoContent) | 		MakeRequest(t, req, http.StatusNoContent) | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	api "code.gitea.io/gitea/modules/structs" | 	api "code.gitea.io/gitea/modules/structs" | ||||||
| 	"code.gitea.io/gitea/tests" | 	"code.gitea.io/gitea/tests" | ||||||
|  |  | ||||||
| @@ -179,7 +180,7 @@ func TestAPINewWikiPage(t *testing.T) { | |||||||
| 		defer tests.PrepareTestEnv(t)() | 		defer tests.PrepareTestEnv(t)() | ||||||
| 		username := "user2" | 		username := "user2" | ||||||
| 		session := loginUser(t, username) | 		session := loginUser(t, username) | ||||||
| 		token := getTokenForLoggedInUser(t, session) | 		token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 		urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/wiki/new?token=%s", username, "repo1", token) | 		urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/wiki/new?token=%s", username, "repo1", token) | ||||||
|  |  | ||||||
| @@ -196,7 +197,7 @@ func TestAPIEditWikiPage(t *testing.T) { | |||||||
| 	defer tests.PrepareTestEnv(t)() | 	defer tests.PrepareTestEnv(t)() | ||||||
| 	username := "user2" | 	username := "user2" | ||||||
| 	session := loginUser(t, username) | 	session := loginUser(t, username) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/wiki/page/Page-With-Spaced-Name?token=%s", username, "repo1", token) | 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/wiki/page/Page-With-Spaced-Name?token=%s", username, "repo1", token) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -14,6 +14,7 @@ import ( | |||||||
| 	"strings" | 	"strings" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| @@ -50,7 +51,7 @@ func TestDumpRestore(t *testing.T) { | |||||||
| 		repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: reponame}) | 		repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: reponame}) | ||||||
| 		repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | 		repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | ||||||
| 		session := loginUser(t, repoOwner.Name) | 		session := loginUser(t, repoOwner.Name) | ||||||
| 		token := getTokenForLoggedInUser(t, session) | 		token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 		// | 		// | ||||||
| 		// Phase 1: dump repo1 from the Gitea instance to the filesystem | 		// Phase 1: dump repo1 from the Gitea instance to the filesystem | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ import ( | |||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	activities_model "code.gitea.io/gitea/models/activities" | 	activities_model "code.gitea.io/gitea/models/activities" | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| @@ -59,7 +60,7 @@ func TestEventSourceManagerRun(t *testing.T) { | |||||||
| 	thread5 := unittest.AssertExistsAndLoadBean(t, &activities_model.Notification{ID: 5}) | 	thread5 := unittest.AssertExistsAndLoadBean(t, &activities_model.Notification{ID: 5}) | ||||||
| 	assert.NoError(t, thread5.LoadAttributes(db.DefaultContext)) | 	assert.NoError(t, thread5.LoadAttributes(db.DefaultContext)) | ||||||
| 	session := loginUser(t, user2.Name) | 	session := loginUser(t, user2.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeNotification) | ||||||
|  |  | ||||||
| 	var apiNL []api.NotificationThread | 	var apiNL []api.NotificationThread | ||||||
|  |  | ||||||
|   | |||||||
| @@ -16,6 +16,7 @@ import ( | |||||||
| 	"testing" | 	"testing" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	issues_model "code.gitea.io/gitea/models/issues" | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	"code.gitea.io/gitea/models/perm" | 	"code.gitea.io/gitea/models/perm" | ||||||
| @@ -42,11 +43,11 @@ func TestGit(t *testing.T) { | |||||||
|  |  | ||||||
| func testGit(t *testing.T, u *url.URL) { | func testGit(t *testing.T, u *url.URL) { | ||||||
| 	username := "user2" | 	username := "user2" | ||||||
| 	baseAPITestContext := NewAPITestContext(t, username, "repo1") | 	baseAPITestContext := NewAPITestContext(t, username, "repo1", auth_model.AccessTokenScopeRepo, auth_model.AccessTokenScopeWritePublicKey, auth_model.AccessTokenScopeDeleteRepo) | ||||||
|  |  | ||||||
| 	u.Path = baseAPITestContext.GitPath() | 	u.Path = baseAPITestContext.GitPath() | ||||||
|  |  | ||||||
| 	forkedUserCtx := NewAPITestContext(t, "user4", "repo1") | 	forkedUserCtx := NewAPITestContext(t, "user4", "repo1", auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 	t.Run("HTTP", func(t *testing.T) { | 	t.Run("HTTP", func(t *testing.T) { | ||||||
| 		defer tests.PrintCurrentTest(t)() | 		defer tests.PrintCurrentTest(t)() | ||||||
| @@ -357,7 +358,7 @@ func doBranchProtectPRMerge(baseCtx *APITestContext, dstPath string) func(t *tes | |||||||
| 		t.Run("CreateBranchProtected", doGitCreateBranch(dstPath, "protected")) | 		t.Run("CreateBranchProtected", doGitCreateBranch(dstPath, "protected")) | ||||||
| 		t.Run("PushProtectedBranch", doGitPushTestRepository(dstPath, "origin", "protected")) | 		t.Run("PushProtectedBranch", doGitPushTestRepository(dstPath, "origin", "protected")) | ||||||
|  |  | ||||||
| 		ctx := NewAPITestContext(t, baseCtx.Username, baseCtx.Reponame) | 		ctx := NewAPITestContext(t, baseCtx.Username, baseCtx.Reponame, auth_model.AccessTokenScopeRepo) | ||||||
| 		t.Run("ProtectProtectedBranchNoWhitelist", doProtectBranch(ctx, "protected", "", "")) | 		t.Run("ProtectProtectedBranchNoWhitelist", doProtectBranch(ctx, "protected", "", "")) | ||||||
| 		t.Run("GenerateCommit", func(t *testing.T) { | 		t.Run("GenerateCommit", func(t *testing.T) { | ||||||
| 			_, err := generateCommitWithNewData(littleSize, dstPath, "user2@example.com", "User Two", "branch-data-file-") | 			_, err := generateCommitWithNewData(littleSize, dstPath, "user2@example.com", "User Two", "branch-data-file-") | ||||||
| @@ -601,7 +602,7 @@ func doAutoPRMerge(baseCtx *APITestContext, dstPath string) func(t *testing.T) { | |||||||
| 	return func(t *testing.T) { | 	return func(t *testing.T) { | ||||||
| 		defer tests.PrintCurrentTest(t)() | 		defer tests.PrintCurrentTest(t)() | ||||||
|  |  | ||||||
| 		ctx := NewAPITestContext(t, baseCtx.Username, baseCtx.Reponame) | 		ctx := NewAPITestContext(t, baseCtx.Username, baseCtx.Reponame, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 		t.Run("CheckoutProtected", doGitCheckoutBranch(dstPath, "protected")) | 		t.Run("CheckoutProtected", doGitCheckoutBranch(dstPath, "protected")) | ||||||
| 		t.Run("PullProtected", doGitPull(dstPath, "origin", "protected")) | 		t.Run("PullProtected", doGitPull(dstPath, "origin", "protected")) | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ import ( | |||||||
| 	"os" | 	"os" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/process" | 	"code.gitea.io/gitea/modules/process" | ||||||
| @@ -69,7 +70,7 @@ func TestGPGGit(t *testing.T) { | |||||||
|  |  | ||||||
| 		t.Run("Unsigned-Initial", func(t *testing.T) { | 		t.Run("Unsigned-Initial", func(t *testing.T) { | ||||||
| 			defer tests.PrintCurrentTest(t)() | 			defer tests.PrintCurrentTest(t)() | ||||||
| 			testCtx := NewAPITestContext(t, username, "initial-unsigned") | 			testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeRepo) | ||||||
| 			t.Run("CreateRepository", doAPICreateRepository(testCtx, false)) | 			t.Run("CreateRepository", doAPICreateRepository(testCtx, false)) | ||||||
| 			t.Run("CheckMasterBranchUnsigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) { | 			t.Run("CheckMasterBranchUnsigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) { | ||||||
| 				assert.NotNil(t, branch.Commit) | 				assert.NotNil(t, branch.Commit) | ||||||
| @@ -93,7 +94,7 @@ func TestGPGGit(t *testing.T) { | |||||||
|  |  | ||||||
| 		t.Run("Unsigned-Initial-CRUD-ParentSigned", func(t *testing.T) { | 		t.Run("Unsigned-Initial-CRUD-ParentSigned", func(t *testing.T) { | ||||||
| 			defer tests.PrintCurrentTest(t)() | 			defer tests.PrintCurrentTest(t)() | ||||||
| 			testCtx := NewAPITestContext(t, username, "initial-unsigned") | 			testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeRepo) | ||||||
| 			t.Run("CreateCRUDFile-ParentSigned", crudActionCreateFile( | 			t.Run("CreateCRUDFile-ParentSigned", crudActionCreateFile( | ||||||
| 				t, testCtx, user, "master", "parentsigned", "signed-parent.txt", func(t *testing.T, response api.FileResponse) { | 				t, testCtx, user, "master", "parentsigned", "signed-parent.txt", func(t *testing.T, response api.FileResponse) { | ||||||
| 					assert.False(t, response.Verification.Verified) | 					assert.False(t, response.Verification.Verified) | ||||||
| @@ -110,7 +111,7 @@ func TestGPGGit(t *testing.T) { | |||||||
|  |  | ||||||
| 		t.Run("Unsigned-Initial-CRUD-Never", func(t *testing.T) { | 		t.Run("Unsigned-Initial-CRUD-Never", func(t *testing.T) { | ||||||
| 			defer tests.PrintCurrentTest(t)() | 			defer tests.PrintCurrentTest(t)() | ||||||
| 			testCtx := NewAPITestContext(t, username, "initial-unsigned") | 			testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeRepo) | ||||||
| 			t.Run("CreateCRUDFile-Never", crudActionCreateFile( | 			t.Run("CreateCRUDFile-Never", crudActionCreateFile( | ||||||
| 				t, testCtx, user, "parentsigned", "parentsigned-never", "unsigned-never2.txt", func(t *testing.T, response api.FileResponse) { | 				t, testCtx, user, "parentsigned", "parentsigned-never", "unsigned-never2.txt", func(t *testing.T, response api.FileResponse) { | ||||||
| 					assert.False(t, response.Verification.Verified) | 					assert.False(t, response.Verification.Verified) | ||||||
| @@ -123,7 +124,7 @@ func TestGPGGit(t *testing.T) { | |||||||
|  |  | ||||||
| 		t.Run("Unsigned-Initial-CRUD-Always", func(t *testing.T) { | 		t.Run("Unsigned-Initial-CRUD-Always", func(t *testing.T) { | ||||||
| 			defer tests.PrintCurrentTest(t)() | 			defer tests.PrintCurrentTest(t)() | ||||||
| 			testCtx := NewAPITestContext(t, username, "initial-unsigned") | 			testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeRepo) | ||||||
| 			t.Run("CreateCRUDFile-Always", crudActionCreateFile( | 			t.Run("CreateCRUDFile-Always", crudActionCreateFile( | ||||||
| 				t, testCtx, user, "master", "always", "signed-always.txt", func(t *testing.T, response api.FileResponse) { | 				t, testCtx, user, "master", "always", "signed-always.txt", func(t *testing.T, response api.FileResponse) { | ||||||
| 					assert.NotNil(t, response.Verification) | 					assert.NotNil(t, response.Verification) | ||||||
| @@ -160,7 +161,7 @@ func TestGPGGit(t *testing.T) { | |||||||
|  |  | ||||||
| 		t.Run("Unsigned-Initial-CRUD-ParentSigned", func(t *testing.T) { | 		t.Run("Unsigned-Initial-CRUD-ParentSigned", func(t *testing.T) { | ||||||
| 			defer tests.PrintCurrentTest(t)() | 			defer tests.PrintCurrentTest(t)() | ||||||
| 			testCtx := NewAPITestContext(t, username, "initial-unsigned") | 			testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeRepo) | ||||||
| 			t.Run("CreateCRUDFile-Always-ParentSigned", crudActionCreateFile( | 			t.Run("CreateCRUDFile-Always-ParentSigned", crudActionCreateFile( | ||||||
| 				t, testCtx, user, "always", "always-parentsigned", "signed-always-parentsigned.txt", func(t *testing.T, response api.FileResponse) { | 				t, testCtx, user, "always", "always-parentsigned", "signed-always-parentsigned.txt", func(t *testing.T, response api.FileResponse) { | ||||||
| 					assert.NotNil(t, response.Verification) | 					assert.NotNil(t, response.Verification) | ||||||
| @@ -183,7 +184,7 @@ func TestGPGGit(t *testing.T) { | |||||||
|  |  | ||||||
| 		t.Run("AlwaysSign-Initial", func(t *testing.T) { | 		t.Run("AlwaysSign-Initial", func(t *testing.T) { | ||||||
| 			defer tests.PrintCurrentTest(t)() | 			defer tests.PrintCurrentTest(t)() | ||||||
| 			testCtx := NewAPITestContext(t, username, "initial-always") | 			testCtx := NewAPITestContext(t, username, "initial-always", auth_model.AccessTokenScopeRepo) | ||||||
| 			t.Run("CreateRepository", doAPICreateRepository(testCtx, false)) | 			t.Run("CreateRepository", doAPICreateRepository(testCtx, false)) | ||||||
| 			t.Run("CheckMasterBranchSigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) { | 			t.Run("CheckMasterBranchSigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) { | ||||||
| 				assert.NotNil(t, branch.Commit) | 				assert.NotNil(t, branch.Commit) | ||||||
| @@ -211,7 +212,7 @@ func TestGPGGit(t *testing.T) { | |||||||
|  |  | ||||||
| 		t.Run("AlwaysSign-Initial-CRUD-Never", func(t *testing.T) { | 		t.Run("AlwaysSign-Initial-CRUD-Never", func(t *testing.T) { | ||||||
| 			defer tests.PrintCurrentTest(t)() | 			defer tests.PrintCurrentTest(t)() | ||||||
| 			testCtx := NewAPITestContext(t, username, "initial-always-never") | 			testCtx := NewAPITestContext(t, username, "initial-always-never", auth_model.AccessTokenScopeRepo) | ||||||
| 			t.Run("CreateRepository", doAPICreateRepository(testCtx, false)) | 			t.Run("CreateRepository", doAPICreateRepository(testCtx, false)) | ||||||
| 			t.Run("CreateCRUDFile-Never", crudActionCreateFile( | 			t.Run("CreateCRUDFile-Never", crudActionCreateFile( | ||||||
| 				t, testCtx, user, "master", "never", "unsigned-never.txt", func(t *testing.T, response api.FileResponse) { | 				t, testCtx, user, "master", "never", "unsigned-never.txt", func(t *testing.T, response api.FileResponse) { | ||||||
| @@ -224,7 +225,7 @@ func TestGPGGit(t *testing.T) { | |||||||
| 		u.Path = baseAPITestContext.GitPath() | 		u.Path = baseAPITestContext.GitPath() | ||||||
| 		t.Run("AlwaysSign-Initial-CRUD-ParentSigned-On-Always", func(t *testing.T) { | 		t.Run("AlwaysSign-Initial-CRUD-ParentSigned-On-Always", func(t *testing.T) { | ||||||
| 			defer tests.PrintCurrentTest(t)() | 			defer tests.PrintCurrentTest(t)() | ||||||
| 			testCtx := NewAPITestContext(t, username, "initial-always-parent") | 			testCtx := NewAPITestContext(t, username, "initial-always-parent", auth_model.AccessTokenScopeRepo) | ||||||
| 			t.Run("CreateRepository", doAPICreateRepository(testCtx, false)) | 			t.Run("CreateRepository", doAPICreateRepository(testCtx, false)) | ||||||
| 			t.Run("CreateCRUDFile-ParentSigned", crudActionCreateFile( | 			t.Run("CreateCRUDFile-ParentSigned", crudActionCreateFile( | ||||||
| 				t, testCtx, user, "master", "parentsigned", "signed-parent.txt", func(t *testing.T, response api.FileResponse) { | 				t, testCtx, user, "master", "parentsigned", "signed-parent.txt", func(t *testing.T, response api.FileResponse) { | ||||||
| @@ -243,7 +244,7 @@ func TestGPGGit(t *testing.T) { | |||||||
|  |  | ||||||
| 		t.Run("AlwaysSign-Initial-CRUD-Always", func(t *testing.T) { | 		t.Run("AlwaysSign-Initial-CRUD-Always", func(t *testing.T) { | ||||||
| 			defer tests.PrintCurrentTest(t)() | 			defer tests.PrintCurrentTest(t)() | ||||||
| 			testCtx := NewAPITestContext(t, username, "initial-always-always") | 			testCtx := NewAPITestContext(t, username, "initial-always-always", auth_model.AccessTokenScopeRepo) | ||||||
| 			t.Run("CreateRepository", doAPICreateRepository(testCtx, false)) | 			t.Run("CreateRepository", doAPICreateRepository(testCtx, false)) | ||||||
| 			t.Run("CreateCRUDFile-Always", crudActionCreateFile( | 			t.Run("CreateCRUDFile-Always", crudActionCreateFile( | ||||||
| 				t, testCtx, user, "master", "always", "signed-always.txt", func(t *testing.T, response api.FileResponse) { | 				t, testCtx, user, "master", "always", "signed-always.txt", func(t *testing.T, response api.FileResponse) { | ||||||
| @@ -263,7 +264,7 @@ func TestGPGGit(t *testing.T) { | |||||||
|  |  | ||||||
| 		t.Run("UnsignedMerging", func(t *testing.T) { | 		t.Run("UnsignedMerging", func(t *testing.T) { | ||||||
| 			defer tests.PrintCurrentTest(t)() | 			defer tests.PrintCurrentTest(t)() | ||||||
| 			testCtx := NewAPITestContext(t, username, "initial-unsigned") | 			testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeRepo) | ||||||
| 			var err error | 			var err error | ||||||
| 			t.Run("CreatePullRequest", func(t *testing.T) { | 			t.Run("CreatePullRequest", func(t *testing.T) { | ||||||
| 				pr, err = doAPICreatePullRequest(testCtx, testCtx.Username, testCtx.Reponame, "master", "never2")(t) | 				pr, err = doAPICreatePullRequest(testCtx, testCtx.Username, testCtx.Reponame, "master", "never2")(t) | ||||||
| @@ -284,7 +285,7 @@ func TestGPGGit(t *testing.T) { | |||||||
|  |  | ||||||
| 		t.Run("BaseSignedMerging", func(t *testing.T) { | 		t.Run("BaseSignedMerging", func(t *testing.T) { | ||||||
| 			defer tests.PrintCurrentTest(t)() | 			defer tests.PrintCurrentTest(t)() | ||||||
| 			testCtx := NewAPITestContext(t, username, "initial-unsigned") | 			testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeRepo) | ||||||
| 			var err error | 			var err error | ||||||
| 			t.Run("CreatePullRequest", func(t *testing.T) { | 			t.Run("CreatePullRequest", func(t *testing.T) { | ||||||
| 				pr, err = doAPICreatePullRequest(testCtx, testCtx.Username, testCtx.Reponame, "master", "parentsigned2")(t) | 				pr, err = doAPICreatePullRequest(testCtx, testCtx.Username, testCtx.Reponame, "master", "parentsigned2")(t) | ||||||
| @@ -305,7 +306,7 @@ func TestGPGGit(t *testing.T) { | |||||||
|  |  | ||||||
| 		t.Run("CommitsSignedMerging", func(t *testing.T) { | 		t.Run("CommitsSignedMerging", func(t *testing.T) { | ||||||
| 			defer tests.PrintCurrentTest(t)() | 			defer tests.PrintCurrentTest(t)() | ||||||
| 			testCtx := NewAPITestContext(t, username, "initial-unsigned") | 			testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeRepo) | ||||||
| 			var err error | 			var err error | ||||||
| 			t.Run("CreatePullRequest", func(t *testing.T) { | 			t.Run("CreatePullRequest", func(t *testing.T) { | ||||||
| 				pr, err = doAPICreatePullRequest(testCtx, testCtx.Username, testCtx.Reponame, "master", "always-parentsigned")(t) | 				pr, err = doAPICreatePullRequest(testCtx, testCtx.Username, testCtx.Reponame, "master", "always-parentsigned")(t) | ||||||
|   | |||||||
| @@ -21,6 +21,7 @@ import ( | |||||||
| 	"testing" | 	"testing" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
|  | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	"code.gitea.io/gitea/modules/graceful" | 	"code.gitea.io/gitea/modules/graceful" | ||||||
| 	"code.gitea.io/gitea/modules/json" | 	"code.gitea.io/gitea/modules/json" | ||||||
| @@ -217,8 +218,8 @@ func emptyTestSession(t testing.TB) *TestSession { | |||||||
| 	return &TestSession{jar: jar} | 	return &TestSession{jar: jar} | ||||||
| } | } | ||||||
|  |  | ||||||
| func getUserToken(t testing.TB, userName string) string { | func getUserToken(t testing.TB, userName string, scope ...auth.AccessTokenScope) string { | ||||||
| 	return getTokenForLoggedInUser(t, loginUser(t, userName)) | 	return getTokenForLoggedInUser(t, loginUser(t, userName), scope...) | ||||||
| } | } | ||||||
|  |  | ||||||
| func loginUser(t testing.TB, userName string) *TestSession { | func loginUser(t testing.TB, userName string) *TestSession { | ||||||
| @@ -256,7 +257,10 @@ func loginUserWithPassword(t testing.TB, userName, password string) *TestSession | |||||||
| // token has to be unique this counter take care of | // token has to be unique this counter take care of | ||||||
| var tokenCounter int64 | var tokenCounter int64 | ||||||
|  |  | ||||||
| func getTokenForLoggedInUser(t testing.TB, session *TestSession) string { | // getTokenForLoggedInUser returns a token for a logged in user. | ||||||
|  | // The scope is an optional list of snake_case strings like the frontend form fields, | ||||||
|  | // but without the "scope_" prefix. | ||||||
|  | func getTokenForLoggedInUser(t testing.TB, session *TestSession, scopes ...auth.AccessTokenScope) string { | ||||||
| 	t.Helper() | 	t.Helper() | ||||||
| 	var token string | 	var token string | ||||||
| 	req := NewRequest(t, "GET", "/user/settings/applications") | 	req := NewRequest(t, "GET", "/user/settings/applications") | ||||||
| @@ -274,10 +278,13 @@ func getTokenForLoggedInUser(t testing.TB, session *TestSession) string { | |||||||
| 		csrf = doc.GetCSRF() | 		csrf = doc.GetCSRF() | ||||||
| 	} | 	} | ||||||
| 	assert.NotEmpty(t, csrf) | 	assert.NotEmpty(t, csrf) | ||||||
| 	req = NewRequestWithValues(t, "POST", "/user/settings/applications", map[string]string{ | 	urlValues := url.Values{} | ||||||
| 		"_csrf": csrf, | 	urlValues.Add("_csrf", csrf) | ||||||
| 		"name":  fmt.Sprintf("api-testing-token-%d", atomic.AddInt64(&tokenCounter, 1)), | 	urlValues.Add("name", fmt.Sprintf("api-testing-token-%d", atomic.AddInt64(&tokenCounter, 1))) | ||||||
| 	}) | 	for _, scope := range scopes { | ||||||
|  | 		urlValues.Add("scope", string(scope)) | ||||||
|  | 	} | ||||||
|  | 	req = NewRequestWithURLValues(t, "POST", "/user/settings/applications", urlValues) | ||||||
| 	resp = session.MakeRequest(t, req, http.StatusSeeOther) | 	resp = session.MakeRequest(t, req, http.StatusSeeOther) | ||||||
|  |  | ||||||
| 	// Log the flash values on failure | 	// Log the flash values on failure | ||||||
| @@ -317,6 +324,11 @@ func NewRequestWithValues(t testing.TB, method, urlStr string, values map[string | |||||||
| 	for key, value := range values { | 	for key, value := range values { | ||||||
| 		urlValues[key] = []string{value} | 		urlValues[key] = []string{value} | ||||||
| 	} | 	} | ||||||
|  | 	return NewRequestWithURLValues(t, method, urlStr, urlValues) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewRequestWithURLValues(t testing.TB, method, urlStr string, urlValues url.Values) *http.Request { | ||||||
|  | 	t.Helper() | ||||||
| 	req := NewRequestWithBody(t, method, urlStr, bytes.NewBufferString(urlValues.Encode())) | 	req := NewRequestWithBody(t, method, urlStr, bytes.NewBufferString(urlValues.Encode())) | ||||||
| 	req.Header.Add("Content-Type", "application/x-www-form-urlencoded") | 	req.Header.Add("Content-Type", "application/x-www-form-urlencoded") | ||||||
| 	return req | 	return req | ||||||
|   | |||||||
| @@ -11,6 +11,7 @@ import ( | |||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| @@ -66,7 +67,7 @@ func TestMigrateGiteaForm(t *testing.T) { | |||||||
| 		repoName := "repo1" | 		repoName := "repo1" | ||||||
| 		repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: ownerName}) | 		repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: ownerName}) | ||||||
| 		session := loginUser(t, ownerName) | 		session := loginUser(t, ownerName) | ||||||
| 		token := getTokenForLoggedInUser(t, session) | 		token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 		// Step 0: verify the repo is available | 		// Step 0: verify the repo is available | ||||||
| 		req := NewRequestf(t, "GET", fmt.Sprintf("/%s/%s", ownerName, repoName)) | 		req := NewRequestf(t, "GET", fmt.Sprintf("/%s/%s", ownerName, repoName)) | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ import ( | |||||||
| 	"strings" | 	"strings" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/organization" | 	"code.gitea.io/gitea/models/organization" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| @@ -24,7 +25,7 @@ func testOrgCounts(t *testing.T, u *url.URL) { | |||||||
| 	orgOwner := "user2" | 	orgOwner := "user2" | ||||||
| 	orgName := "testOrg" | 	orgName := "testOrg" | ||||||
| 	orgCollaborator := "user4" | 	orgCollaborator := "user4" | ||||||
| 	ctx := NewAPITestContext(t, orgOwner, "repo1") | 	ctx := NewAPITestContext(t, orgOwner, "repo1", auth_model.AccessTokenScopeAdminOrg) | ||||||
|  |  | ||||||
| 	var ownerCountRepos map[string]int | 	var ownerCountRepos map[string]int | ||||||
| 	var collabCountRepos map[string]int | 	var collabCountRepos map[string]int | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ import ( | |||||||
| 	"strings" | 	"strings" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	api "code.gitea.io/gitea/modules/structs" | 	api "code.gitea.io/gitea/modules/structs" | ||||||
| @@ -158,7 +159,7 @@ func TestOrgRestrictedUser(t *testing.T) { | |||||||
|  |  | ||||||
| 	// Therefore create a read-only team | 	// Therefore create a read-only team | ||||||
| 	adminSession := loginUser(t, "user1") | 	adminSession := loginUser(t, "user1") | ||||||
| 	token := getTokenForLoggedInUser(t, adminSession) | 	token := getTokenForLoggedInUser(t, adminSession, auth_model.AccessTokenScopeAdminOrg) | ||||||
|  |  | ||||||
| 	teamToCreate := &api.CreateTeamOption{ | 	teamToCreate := &api.CreateTeamOption{ | ||||||
| 		Name:                    "codereader", | 		Name:                    "codereader", | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ import ( | |||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	activities_model "code.gitea.io/gitea/models/activities" | 	activities_model "code.gitea.io/gitea/models/activities" | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| @@ -33,7 +34,7 @@ func testPrivateActivityDoSomethingForActionEntries(t *testing.T) { | |||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repoBefore.OwnerID}) | 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repoBefore.OwnerID}) | ||||||
|  |  | ||||||
| 	session := loginUser(t, privateActivityTestUser) | 	session := loginUser(t, privateActivityTestUser) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
| 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues?state=all&token=%s", owner.Name, repoBefore.Name, token) | 	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{ | 	req := NewRequestWithJSON(t, "POST", urlStr, &api.CreateIssueOption{ | ||||||
| 		Body:  "test", | 		Body:  "test", | ||||||
|   | |||||||
| @@ -17,6 +17,7 @@ import ( | |||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	issues_model "code.gitea.io/gitea/models/issues" | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| @@ -217,7 +218,7 @@ func TestCantMergeConflict(t *testing.T) { | |||||||
| 		testEditFileToNewBranch(t, session, "user1", "repo1", "master", "base", "README.md", "Hello, World (Edited Twice)\n") | 		testEditFileToNewBranch(t, session, "user1", "repo1", "master", "base", "README.md", "Hello, World (Edited Twice)\n") | ||||||
|  |  | ||||||
| 		// Use API to create a conflicting pr | 		// Use API to create a conflicting pr | ||||||
| 		token := getTokenForLoggedInUser(t, session) | 		token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
| 		req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls?token=%s", "user1", "repo1", token), &api.CreatePullRequestOption{ | 		req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls?token=%s", "user1", "repo1", token), &api.CreatePullRequestOption{ | ||||||
| 			Head:  "conflict", | 			Head:  "conflict", | ||||||
| 			Base:  "base", | 			Base:  "base", | ||||||
| @@ -325,7 +326,7 @@ func TestCantMergeUnrelated(t *testing.T) { | |||||||
| 		testEditFileToNewBranch(t, session, "user1", "repo1", "master", "conflict", "README.md", "Hello, World (Edited Once)\n") | 		testEditFileToNewBranch(t, session, "user1", "repo1", "master", "conflict", "README.md", "Hello, World (Edited Once)\n") | ||||||
|  |  | ||||||
| 		// Use API to create a conflicting pr | 		// Use API to create a conflicting pr | ||||||
| 		token := getTokenForLoggedInUser(t, session) | 		token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
| 		req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls?token=%s", "user1", "repo1", token), &api.CreatePullRequestOption{ | 		req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls?token=%s", "user1", "repo1", token), &api.CreatePullRequestOption{ | ||||||
| 			Head:  "unrelated", | 			Head:  "unrelated", | ||||||
| 			Base:  "base", | 			Base:  "base", | ||||||
|   | |||||||
| @@ -11,6 +11,7 @@ import ( | |||||||
| 	"strings" | 	"strings" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	api "code.gitea.io/gitea/modules/structs" | 	api "code.gitea.io/gitea/modules/structs" | ||||||
|  |  | ||||||
| 	"github.com/stretchr/testify/assert" | 	"github.com/stretchr/testify/assert" | ||||||
| @@ -63,7 +64,7 @@ func TestPullCreate_CommitStatus(t *testing.T) { | |||||||
| 			api.CommitStatusWarning: "gitea-exclamation", | 			api.CommitStatusWarning: "gitea-exclamation", | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		testCtx := NewAPITestContext(t, "user1", "repo1") | 		testCtx := NewAPITestContext(t, "user1", "repo1", auth_model.AccessTokenScopeRepo) | ||||||
|  |  | ||||||
| 		// Update commit status, and check if icon is updated as well | 		// Update commit status, and check if icon is updated as well | ||||||
| 		for _, status := range statusList { | 		for _, status := range statusList { | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ import ( | |||||||
| 	"testing" | 	"testing" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	issues_model "code.gitea.io/gitea/models/issues" | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| @@ -38,7 +39,7 @@ func TestAPIPullUpdate(t *testing.T) { | |||||||
| 		assert.NoError(t, pr.LoadIssue(db.DefaultContext)) | 		assert.NoError(t, pr.LoadIssue(db.DefaultContext)) | ||||||
|  |  | ||||||
| 		session := loginUser(t, "user2") | 		session := loginUser(t, "user2") | ||||||
| 		token := getTokenForLoggedInUser(t, session) | 		token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
| 		req := NewRequestf(t, "POST", "/api/v1/repos/%s/%s/pulls/%d/update?token="+token, pr.BaseRepo.OwnerName, pr.BaseRepo.Name, pr.Issue.Index) | 		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) | 		session.MakeRequest(t, req, http.StatusOK) | ||||||
|  |  | ||||||
| @@ -66,7 +67,7 @@ func TestAPIPullUpdateByRebase(t *testing.T) { | |||||||
| 		assert.NoError(t, pr.LoadIssue(db.DefaultContext)) | 		assert.NoError(t, pr.LoadIssue(db.DefaultContext)) | ||||||
|  |  | ||||||
| 		session := loginUser(t, "user2") | 		session := loginUser(t, "user2") | ||||||
| 		token := getTokenForLoggedInUser(t, session) | 		token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) | ||||||
| 		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) | 		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) | 		session.MakeRequest(t, req, http.StatusOK) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -11,6 +11,7 @@ import ( | |||||||
| 	"sync" | 	"sync" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/modules/json" | 	"code.gitea.io/gitea/modules/json" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| 	api "code.gitea.io/gitea/modules/structs" | 	api "code.gitea.io/gitea/modules/structs" | ||||||
| @@ -50,7 +51,8 @@ func doTestRepoCommitWithStatus(t *testing.T, state string, classes ...string) { | |||||||
| 	assert.NotEmpty(t, commitURL) | 	assert.NotEmpty(t, commitURL) | ||||||
|  |  | ||||||
| 	// Call API to add status for commit | 	// Call API to add status for commit | ||||||
| 	t.Run("CreateStatus", doAPICreateCommitStatus(NewAPITestContext(t, "user2", "repo1"), path.Base(commitURL), api.CommitStatusState(state))) | 	ctx := NewAPITestContext(t, "user2", "repo1", auth_model.AccessTokenScopeRepo) | ||||||
|  | 	t.Run("CreateStatus", doAPICreateCommitStatus(ctx, path.Base(commitURL), api.CommitStatusState(state))) | ||||||
|  |  | ||||||
| 	req = NewRequest(t, "GET", "/user2/repo1/commits/branch/master") | 	req = NewRequest(t, "GET", "/user2/repo1/commits/branch/master") | ||||||
| 	resp = session.MakeRequest(t, req, http.StatusOK) | 	resp = session.MakeRequest(t, req, http.StatusOK) | ||||||
| @@ -142,7 +144,8 @@ func TestRepoCommitsStatusParallel(t *testing.T) { | |||||||
| 		wg.Add(1) | 		wg.Add(1) | ||||||
| 		go func(parentT *testing.T, i int) { | 		go func(parentT *testing.T, i int) { | ||||||
| 			parentT.Run(fmt.Sprintf("ParallelCreateStatus_%d", i), func(t *testing.T) { | 			parentT.Run(fmt.Sprintf("ParallelCreateStatus_%d", i), func(t *testing.T) { | ||||||
| 				runBody := doAPICreateCommitStatus(NewAPITestContext(t, "user2", "repo1"), path.Base(commitURL), api.CommitStatusState("pending")) | 				ctx := NewAPITestContext(t, "user2", "repo1", auth_model.AccessTokenScopeRepoStatus) | ||||||
|  | 				runBody := doAPICreateCommitStatus(ctx, path.Base(commitURL), api.CommitStatusState("pending")) | ||||||
| 				runBody(t) | 				runBody(t) | ||||||
| 				wg.Done() | 				wg.Done() | ||||||
| 			}) | 			}) | ||||||
|   | |||||||
| @@ -12,6 +12,7 @@ import ( | |||||||
| 	"testing" | 	"testing" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/modules/git" | 	"code.gitea.io/gitea/modules/git" | ||||||
| 	api "code.gitea.io/gitea/modules/structs" | 	api "code.gitea.io/gitea/modules/structs" | ||||||
|  |  | ||||||
| @@ -47,7 +48,9 @@ func TestPushDeployKeyOnEmptyRepo(t *testing.T) { | |||||||
|  |  | ||||||
| func testPushDeployKeyOnEmptyRepo(t *testing.T, u *url.URL) { | func testPushDeployKeyOnEmptyRepo(t *testing.T, u *url.URL) { | ||||||
| 	// OK login | 	// OK login | ||||||
| 	ctx := NewAPITestContext(t, "user2", "deploy-key-empty-repo-1") | 	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) | ||||||
|  |  | ||||||
| 	keyname := fmt.Sprintf("%s-push", ctx.Reponame) | 	keyname := fmt.Sprintf("%s-push", ctx.Reponame) | ||||||
| 	u.Path = ctx.GitPath() | 	u.Path = ctx.GitPath() | ||||||
|  |  | ||||||
| @@ -72,7 +75,7 @@ func testPushDeployKeyOnEmptyRepo(t *testing.T, u *url.URL) { | |||||||
|  |  | ||||||
| 		t.Run("CheckIsNotEmpty", doCheckRepositoryEmptyStatus(ctx, false)) | 		t.Run("CheckIsNotEmpty", doCheckRepositoryEmptyStatus(ctx, false)) | ||||||
|  |  | ||||||
| 		t.Run("DeleteRepository", doAPIDeleteRepository(ctx)) | 		t.Run("DeleteRepository", doAPIDeleteRepository(ctxWithDeleteRepo)) | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -89,10 +92,13 @@ func testKeyOnlyOneType(t *testing.T, u *url.URL) { | |||||||
| 	keyname := fmt.Sprintf("%s-push", reponame) | 	keyname := fmt.Sprintf("%s-push", reponame) | ||||||
|  |  | ||||||
| 	// OK login | 	// OK login | ||||||
| 	ctx := NewAPITestContext(t, username, reponame) | 	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) | ||||||
|  |  | ||||||
| 	otherCtx := ctx | 	otherCtx := ctx | ||||||
| 	otherCtx.Reponame = "ssh-key-test-repo-2" | 	otherCtx.Reponame = "ssh-key-test-repo-2" | ||||||
|  | 	otherCtxWithDeleteRepo := ctxWithDeleteRepo | ||||||
|  | 	otherCtxWithDeleteRepo.Reponame = otherCtx.Reponame | ||||||
|  |  | ||||||
| 	failCtx := ctx | 	failCtx := ctx | ||||||
| 	failCtx.ExpectedCode = http.StatusUnprocessableEntity | 	failCtx.ExpectedCode = http.StatusUnprocessableEntity | ||||||
| @@ -160,7 +166,7 @@ func testKeyOnlyOneType(t *testing.T, u *url.URL) { | |||||||
| 			otherSSHURL := createSSHUrl(otherCtx.GitPath(), u) | 			otherSSHURL := createSSHUrl(otherCtx.GitPath(), u) | ||||||
| 			dstOtherPath := t.TempDir() | 			dstOtherPath := t.TempDir() | ||||||
|  |  | ||||||
| 			t.Run("DeleteRepository", doAPIDeleteRepository(ctx)) | 			t.Run("DeleteRepository", doAPIDeleteRepository(ctxWithDeleteRepo)) | ||||||
|  |  | ||||||
| 			t.Run("FailToCreateUserKeyAsStillDeploy", doAPICreateUserKey(failCtx, keyname, keyFile)) | 			t.Run("FailToCreateUserKeyAsStillDeploy", doAPICreateUserKey(failCtx, keyname, keyFile)) | ||||||
|  |  | ||||||
| @@ -170,9 +176,9 @@ func testKeyOnlyOneType(t *testing.T, u *url.URL) { | |||||||
|  |  | ||||||
| 			t.Run("PushToOther", doGitPushTestRepository(dstOtherPath, "origin", "master")) | 			t.Run("PushToOther", doGitPushTestRepository(dstOtherPath, "origin", "master")) | ||||||
|  |  | ||||||
| 			t.Run("DeleteOtherRepository", doAPIDeleteRepository(otherCtx)) | 			t.Run("DeleteOtherRepository", doAPIDeleteRepository(otherCtxWithDeleteRepo)) | ||||||
|  |  | ||||||
| 			t.Run("RecreateRepository", doAPICreateRepository(ctx, false)) | 			t.Run("RecreateRepository", doAPICreateRepository(ctxWithDeleteRepo, false)) | ||||||
|  |  | ||||||
| 			t.Run("CreateUserKey", doAPICreateUserKey(ctx, keyname, keyFile, func(t *testing.T, publicKey api.PublicKey) { | 			t.Run("CreateUserKey", doAPICreateUserKey(ctx, keyname, keyFile, func(t *testing.T, publicKey api.PublicKey) { | ||||||
| 				userKeyPublicKeyID = publicKey.ID | 				userKeyPublicKeyID = publicKey.ID | ||||||
|   | |||||||
| @@ -7,6 +7,7 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	issues_model "code.gitea.io/gitea/models/issues" | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| @@ -165,7 +166,7 @@ Note: This user hasn't uploaded any GPG keys. | |||||||
| 	// Import key | 	// Import key | ||||||
| 	// User1 <user1@example.com> | 	// User1 <user1@example.com> | ||||||
| 	session := loginUser(t, "user1") | 	session := loginUser(t, "user1") | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteGPGKey) | ||||||
| 	testCreateGPGKey(t, session.MakeRequest, token, http.StatusCreated, `-----BEGIN PGP PUBLIC KEY BLOCK----- | 	testCreateGPGKey(t, session.MakeRequest, token, http.StatusCreated, `-----BEGIN PGP PUBLIC KEY BLOCK----- | ||||||
|  |  | ||||||
| mQENBFyy/VUBCADJ7zbM20Z1RWmFoVgp5WkQfI2rU1Vj9cQHes9i42wVLLtcbPeo | mQENBFyy/VUBCADJ7zbM20Z1RWmFoVgp5WkQfI2rU1Vj9cQHes9i42wVLLtcbPeo | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user