2017-04-29 07:33:25 +03:00
// Copyright 2017 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2017-04-29 07:33:25 +03:00
2022-09-02 22:18:23 +03:00
package integration
2017-04-29 07:33:25 +03:00
import (
2017-10-10 04:23:29 +03:00
"fmt"
2017-04-29 07:33:25 +03:00
"net/http"
2019-03-15 17:19:09 +03:00
"net/url"
2017-04-29 07:33:25 +03:00
"testing"
2017-07-10 14:07:39 +03:00
2023-01-18 00:46:03 +03:00
auth_model "code.gitea.io/gitea/models/auth"
2022-05-11 13:09:36 +03:00
"code.gitea.io/gitea/models/db"
access_model "code.gitea.io/gitea/models/perm/access"
2021-12-10 04:27:50 +03:00
repo_model "code.gitea.io/gitea/models/repo"
2021-11-16 11:53:21 +03:00
"code.gitea.io/gitea/models/unittest"
2021-11-24 12:49:20 +03:00
user_model "code.gitea.io/gitea/models/user"
2020-06-09 05:46:51 +03:00
"code.gitea.io/gitea/modules/setting"
2019-05-11 13:21:34 +03:00
api "code.gitea.io/gitea/modules/structs"
2023-09-08 07:51:15 +03:00
repo_service "code.gitea.io/gitea/services/repository"
2022-09-02 22:18:23 +03:00
"code.gitea.io/gitea/tests"
2017-07-10 14:07:39 +03:00
"github.com/stretchr/testify/assert"
2017-04-29 07:33:25 +03:00
)
func TestAPIUserReposNotLogin ( t * testing . T ) {
2022-09-02 22:18:23 +03:00
defer tests . PrepareTestEnv ( t ) ( )
2022-08-16 05:22:25 +03:00
user := unittest . AssertExistsAndLoadBean ( t , & user_model . User { ID : 2 } )
2017-07-10 14:07:39 +03:00
req := NewRequestf ( t , "GET" , "/api/v1/users/%s/repos" , user . Name )
resp := MakeRequest ( t , req , http . StatusOK )
2017-04-29 07:33:25 +03:00
2017-07-10 14:07:39 +03:00
var apiRepos [ ] api . Repository
DecodeJSON ( t , resp , & apiRepos )
2021-12-10 04:27:50 +03:00
expectedLen := unittest . GetCount ( t , repo_model . Repository { OwnerID : user . ID } ,
2021-11-16 11:53:21 +03:00
unittest . Cond ( "is_private = ?" , false ) )
2017-07-10 14:07:39 +03:00
assert . Len ( t , apiRepos , expectedLen )
for _ , repo := range apiRepos {
assert . EqualValues ( t , user . ID , repo . Owner . ID )
assert . False ( t , repo . Private )
}
}
2023-07-11 05:04:28 +03:00
func TestAPIUserReposWithWrongToken ( t * testing . T ) {
defer tests . PrepareTestEnv ( t ) ( )
user := unittest . AssertExistsAndLoadBean ( t , & user_model . User { ID : 2 } )
wrongToken := fmt . Sprintf ( "Bearer %s" , "wrong_token" )
2023-12-22 02:59:59 +03:00
req := NewRequestf ( t , "GET" , "/api/v1/users/%s/repos" , user . Name ) .
AddTokenAuth ( wrongToken )
2023-07-11 05:04:28 +03:00
resp := MakeRequest ( t , req , http . StatusUnauthorized )
assert . Contains ( t , resp . Body . String ( ) , "user does not exist" )
}
2017-10-10 04:23:29 +03:00
func TestAPISearchRepo ( t * testing . T ) {
2022-09-02 22:18:23 +03:00
defer tests . PrepareTestEnv ( t ) ( )
2017-07-10 14:07:39 +03:00
const keyword = "test"
req := NewRequestf ( t , "GET" , "/api/v1/repos/search?q=%s" , keyword )
resp := MakeRequest ( t , req , http . StatusOK )
2017-04-30 08:08:09 +03:00
2017-09-20 12:24:38 +03:00
var body api . SearchResults
2017-07-10 14:07:39 +03:00
DecodeJSON ( t , resp , & body )
2017-09-20 12:24:38 +03:00
assert . NotEmpty ( t , body . Data )
for _ , repo := range body . Data {
assert . Contains ( t , repo . Name , keyword )
assert . False ( t , repo . Private )
2017-07-10 14:07:39 +03:00
}
2017-10-10 04:23:29 +03:00
2022-08-16 05:22:25 +03:00
user := unittest . AssertExistsAndLoadBean ( t , & user_model . User { ID : 15 } )
user2 := unittest . AssertExistsAndLoadBean ( t , & user_model . User { ID : 16 } )
2023-09-14 05:59:53 +03:00
org3 := unittest . AssertExistsAndLoadBean ( t , & user_model . User { ID : 18 } )
2022-08-16 05:22:25 +03:00
user4 := unittest . AssertExistsAndLoadBean ( t , & user_model . User { ID : 20 } )
orgUser := unittest . AssertExistsAndLoadBean ( t , & user_model . User { ID : 17 } )
2017-10-10 04:23:29 +03:00
2020-06-09 05:46:51 +03:00
oldAPIDefaultNum := setting . API . DefaultPagingNum
defer func ( ) {
setting . API . DefaultPagingNum = oldAPIDefaultNum
} ( )
setting . API . DefaultPagingNum = 10
2017-10-10 04:23:29 +03:00
// Map of expected results, where key is user for login
2021-11-24 12:49:20 +03:00
type expectedResults map [ * user_model . User ] struct {
2017-10-10 04:23:29 +03:00
count int
repoOwnerID int64
repoName string
includesPrivate bool
}
testCases := [ ] struct {
name , requestURL string
expectedResults
} {
2022-01-20 20:46:10 +03:00
{
name : "RepositoriesMax50" , requestURL : "/api/v1/repos/search?limit=50&private=false" , expectedResults : expectedResults {
2023-08-18 12:21:24 +03:00
nil : { count : 34 } ,
user : { count : 34 } ,
user2 : { count : 34 } ,
2022-01-20 20:46:10 +03:00
} ,
2017-10-10 04:23:29 +03:00
} ,
2022-01-20 20:46:10 +03:00
{
name : "RepositoriesMax10" , requestURL : "/api/v1/repos/search?limit=10&private=false" , expectedResults : expectedResults {
nil : { count : 10 } ,
user : { count : 10 } ,
user2 : { count : 10 } ,
} ,
2017-10-10 04:23:29 +03:00
} ,
2022-01-20 20:46:10 +03:00
{
name : "RepositoriesDefault" , requestURL : "/api/v1/repos/search?default&private=false" , expectedResults : expectedResults {
nil : { count : 10 } ,
user : { count : 10 } ,
user2 : { count : 10 } ,
} ,
2017-10-10 04:23:29 +03:00
} ,
2022-01-20 20:46:10 +03:00
{
name : "RepositoriesByName" , requestURL : fmt . Sprintf ( "/api/v1/repos/search?q=%s&private=false" , "big_test_" ) , expectedResults : expectedResults {
nil : { count : 7 , repoName : "big_test_" } ,
user : { count : 7 , repoName : "big_test_" } ,
user2 : { count : 7 , repoName : "big_test_" } ,
} ,
2017-10-10 04:23:29 +03:00
} ,
2022-06-04 22:18:50 +03:00
{
name : "RepositoriesByName" , requestURL : fmt . Sprintf ( "/api/v1/repos/search?q=%s&private=false" , "user2/big_test_" ) , expectedResults : expectedResults {
user2 : { count : 2 , repoName : "big_test_" } ,
} ,
} ,
2022-01-20 20:46:10 +03:00
{
name : "RepositoriesAccessibleAndRelatedToUser" , requestURL : fmt . Sprintf ( "/api/v1/repos/search?uid=%d" , user . ID ) , expectedResults : expectedResults {
nil : { count : 5 } ,
user : { count : 9 , includesPrivate : true } ,
user2 : { count : 6 , includesPrivate : true } ,
} ,
2017-10-10 04:23:29 +03:00
} ,
2022-01-20 20:46:10 +03:00
{
name : "RepositoriesAccessibleAndRelatedToUser2" , requestURL : fmt . Sprintf ( "/api/v1/repos/search?uid=%d" , user2 . ID ) , expectedResults : expectedResults {
nil : { count : 1 } ,
user : { count : 2 , includesPrivate : true } ,
user2 : { count : 2 , includesPrivate : true } ,
user4 : { count : 1 } ,
} ,
2017-10-10 04:23:29 +03:00
} ,
2022-01-20 20:46:10 +03:00
{
2023-09-14 05:59:53 +03:00
name : "RepositoriesAccessibleAndRelatedToUser3" , requestURL : fmt . Sprintf ( "/api/v1/repos/search?uid=%d" , org3 . ID ) , expectedResults : expectedResults {
2022-01-20 20:46:10 +03:00
nil : { count : 1 } ,
user : { count : 4 , includesPrivate : true } ,
user2 : { count : 3 , includesPrivate : true } ,
2023-09-14 05:59:53 +03:00
org3 : { count : 4 , includesPrivate : true } ,
2022-01-20 20:46:10 +03:00
} ,
2017-10-17 18:20:22 +03:00
} ,
2022-01-20 20:46:10 +03:00
{
name : "RepositoriesOwnedByOrganization" , requestURL : fmt . Sprintf ( "/api/v1/repos/search?uid=%d" , orgUser . ID ) , expectedResults : expectedResults {
nil : { count : 1 , repoOwnerID : orgUser . ID } ,
user : { count : 2 , repoOwnerID : orgUser . ID , includesPrivate : true } ,
user2 : { count : 1 , repoOwnerID : orgUser . ID } ,
} ,
2017-10-10 04:23:29 +03:00
} ,
2017-10-27 00:16:13 +03:00
{ name : "RepositoriesAccessibleAndRelatedToUser4" , requestURL : fmt . Sprintf ( "/api/v1/repos/search?uid=%d" , user4 . ID ) , expectedResults : expectedResults {
nil : { count : 3 } ,
2019-05-15 18:24:39 +03:00
user : { count : 4 , includesPrivate : true } ,
2022-01-20 20:46:10 +03:00
user4 : { count : 7 , includesPrivate : true } ,
} } ,
2017-10-27 00:16:13 +03:00
{ name : "RepositoriesAccessibleAndRelatedToUser4/SearchModeSource" , requestURL : fmt . Sprintf ( "/api/v1/repos/search?uid=%d&mode=%s" , user4 . ID , "source" ) , expectedResults : expectedResults {
nil : { count : 0 } ,
2019-05-15 18:24:39 +03:00
user : { count : 1 , includesPrivate : true } ,
2022-01-20 20:46:10 +03:00
user4 : { count : 1 , includesPrivate : true } ,
} } ,
2017-10-27 00:16:13 +03:00
{ name : "RepositoriesAccessibleAndRelatedToUser4/SearchModeFork" , requestURL : fmt . Sprintf ( "/api/v1/repos/search?uid=%d&mode=%s" , user4 . ID , "fork" ) , expectedResults : expectedResults {
nil : { count : 1 } ,
user : { count : 1 } ,
2022-01-20 20:46:10 +03:00
user4 : { count : 2 , includesPrivate : true } ,
} } ,
2017-10-27 00:16:13 +03:00
{ name : "RepositoriesAccessibleAndRelatedToUser4/SearchModeFork/Exclusive" , requestURL : fmt . Sprintf ( "/api/v1/repos/search?uid=%d&mode=%s&exclusive=1" , user4 . ID , "fork" ) , expectedResults : expectedResults {
nil : { count : 1 } ,
user : { count : 1 } ,
2022-01-20 20:46:10 +03:00
user4 : { count : 2 , includesPrivate : true } ,
} } ,
2017-10-27 00:16:13 +03:00
{ name : "RepositoriesAccessibleAndRelatedToUser4/SearchModeMirror" , requestURL : fmt . Sprintf ( "/api/v1/repos/search?uid=%d&mode=%s" , user4 . ID , "mirror" ) , expectedResults : expectedResults {
nil : { count : 2 } ,
user : { count : 2 } ,
2022-01-20 20:46:10 +03:00
user4 : { count : 4 , includesPrivate : true } ,
} } ,
2017-10-27 00:16:13 +03:00
{ name : "RepositoriesAccessibleAndRelatedToUser4/SearchModeMirror/Exclusive" , requestURL : fmt . Sprintf ( "/api/v1/repos/search?uid=%d&mode=%s&exclusive=1" , user4 . ID , "mirror" ) , expectedResults : expectedResults {
nil : { count : 1 } ,
user : { count : 1 } ,
2022-01-20 20:46:10 +03:00
user4 : { count : 2 , includesPrivate : true } ,
} } ,
2017-10-27 00:16:13 +03:00
{ name : "RepositoriesAccessibleAndRelatedToUser4/SearchModeCollaborative" , requestURL : fmt . Sprintf ( "/api/v1/repos/search?uid=%d&mode=%s" , user4 . ID , "collaborative" ) , expectedResults : expectedResults {
nil : { count : 0 } ,
2019-05-15 18:24:39 +03:00
user : { count : 1 , includesPrivate : true } ,
2022-01-20 20:46:10 +03:00
user4 : { count : 1 , includesPrivate : true } ,
} } ,
2017-10-10 04:23:29 +03:00
}
for _ , testCase := range testCases {
t . Run ( testCase . name , func ( t * testing . T ) {
for userToLogin , expected := range testCase . expectedResults {
var testName string
2017-10-27 00:16:13 +03:00
var userID int64
2018-09-10 19:15:52 +03:00
var token string
2017-10-10 04:23:29 +03:00
if userToLogin != nil && userToLogin . ID > 0 {
testName = fmt . Sprintf ( "LoggedUser%d" , userToLogin . ID )
2023-04-22 21:53:00 +03:00
session := loginUser ( t , userToLogin . Name )
Redesign Scoped Access Tokens (#24767)
## Changes
- Adds the following high level access scopes, each with `read` and
`write` levels:
- `activitypub`
- `admin` (hidden if user is not a site admin)
- `misc`
- `notification`
- `organization`
- `package`
- `issue`
- `repository`
- `user`
- Adds new middleware function `tokenRequiresScopes()` in addition to
`reqToken()`
- `tokenRequiresScopes()` is used for each high-level api section
- _if_ a scoped token is present, checks that the required scope is
included based on the section and HTTP method
- `reqToken()` is used for individual routes
- checks that required authentication is present (but does not check
scope levels as this will already have been handled by
`tokenRequiresScopes()`
- Adds migration to convert old scoped access tokens to the new set of
scopes
- Updates the user interface for scope selection
### User interface example
<img width="903" alt="Screen Shot 2023-05-31 at 1 56 55 PM"
src="https://github.com/go-gitea/gitea/assets/23248839/654766ec-2143-4f59-9037-3b51600e32f3">
<img width="917" alt="Screen Shot 2023-05-31 at 1 56 43 PM"
src="https://github.com/go-gitea/gitea/assets/23248839/1ad64081-012c-4a73-b393-66b30352654c">
## tokenRequiresScopes Design Decision
- `tokenRequiresScopes()` was added to more reliably cover api routes.
For an incoming request, this function uses the given scope category
(say `AccessTokenScopeCategoryOrganization`) and the HTTP method (say
`DELETE`) and verifies that any scoped tokens in use include
`delete:organization`.
- `reqToken()` is used to enforce auth for individual routes that
require it. If a scoped token is not present for a request,
`tokenRequiresScopes()` will not return an error
## TODO
- [x] Alphabetize scope categories
- [x] Change 'public repos only' to a radio button (private vs public).
Also expand this to organizations
- [X] Disable token creation if no scopes selected. Alternatively, show
warning
- [x] `reqToken()` is missing from many `POST/DELETE` routes in the api.
`tokenRequiresScopes()` only checks that a given token has the correct
scope, `reqToken()` must be used to check that a token (or some other
auth) is present.
- _This should be addressed in this PR_
- [x] The migration should be reviewed very carefully in order to
minimize access changes to existing user tokens.
- _This should be addressed in this PR_
- [x] Link to api to swagger documentation, clarify what
read/write/delete levels correspond to
- [x] Review cases where more than one scope is needed as this directly
deviates from the api definition.
- _This should be addressed in this PR_
- For example:
```go
m.Group("/users/{username}/orgs", func() {
m.Get("", reqToken(), org.ListUserOrgs)
m.Get("/{org}/permissions", reqToken(), org.GetUserOrgsPermissions)
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser,
auth_model.AccessTokenScopeCategoryOrganization),
context_service.UserAssignmentAPI())
```
## Future improvements
- [ ] Add required scopes to swagger documentation
- [ ] Redesign `reqToken()` to be opt-out rather than opt-in
- [ ] Subdivide scopes like `repository`
- [ ] Once a token is created, if it has no scopes, we should display
text instead of an empty bullet point
- [ ] If the 'public repos only' option is selected, should read
categories be selected by default
Closes #24501
Closes #24799
Co-authored-by: Jonathan Tran <jon@allspice.io>
Co-authored-by: Kyle D <kdumontnu@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2023-06-04 21:57:16 +03:00
token = getTokenForLoggedInUser ( t , session , auth_model . AccessTokenScopeReadRepository )
2017-10-27 00:16:13 +03:00
userID = userToLogin . ID
2017-10-10 04:23:29 +03:00
} else {
testName = "AnonymousUser"
2023-04-22 21:53:00 +03:00
_ = emptyTestSession ( t )
2017-10-10 04:23:29 +03:00
}
t . Run ( testName , func ( t * testing . T ) {
2023-12-22 02:59:59 +03:00
request := NewRequest ( t , "GET" , testCase . requestURL ) .
AddTokenAuth ( token )
2022-12-02 06:39:42 +03:00
response := MakeRequest ( t , request , http . StatusOK )
2017-10-10 04:23:29 +03:00
var body api . SearchResults
DecodeJSON ( t , response , & body )
2019-05-15 18:24:39 +03:00
repoNames := make ( [ ] string , 0 , len ( body . Data ) )
for _ , repo := range body . Data {
repoNames = append ( repoNames , fmt . Sprintf ( "%d:%s:%t" , repo . ID , repo . FullName , repo . Private ) )
}
assert . Len ( t , repoNames , expected . count )
2017-10-10 04:23:29 +03:00
for _ , repo := range body . Data {
2017-10-27 00:16:13 +03:00
r := getRepo ( t , repo . ID )
2022-05-11 13:09:36 +03:00
hasAccess , err := access_model . HasAccess ( db . DefaultContext , userID , r )
2019-05-15 18:24:39 +03:00
assert . NoError ( t , err , "Error when checking if User: %d has access to %s: %v" , userID , repo . FullName , err )
assert . True ( t , hasAccess , "User: %d does not have access to %s" , userID , repo . FullName )
2017-10-27 00:16:13 +03:00
2017-10-10 04:23:29 +03:00
assert . NotEmpty ( t , repo . Name )
2019-05-15 18:24:39 +03:00
assert . Equal ( t , repo . Name , r . Name )
2017-10-10 04:23:29 +03:00
if len ( expected . repoName ) > 0 {
assert . Contains ( t , repo . Name , expected . repoName )
}
if expected . repoOwnerID > 0 {
assert . Equal ( t , expected . repoOwnerID , repo . Owner . ID )
}
if ! expected . includesPrivate {
2019-05-15 18:24:39 +03:00
assert . False ( t , repo . Private , "User: %d not expecting private repository: %s" , userID , repo . FullName )
2017-10-10 04:23:29 +03:00
}
}
} )
}
} )
}
2017-04-30 08:08:09 +03:00
}
2017-07-12 04:23:41 +03:00
2021-12-10 04:27:50 +03:00
var repoCache = make ( map [ int64 ] * repo_model . Repository )
2017-10-27 00:16:13 +03:00
2021-12-10 04:27:50 +03:00
func getRepo ( t * testing . T , repoID int64 ) * repo_model . Repository {
2017-10-27 00:16:13 +03:00
if _ , ok := repoCache [ repoID ] ; ! ok {
2022-08-16 05:22:25 +03:00
repoCache [ repoID ] = unittest . AssertExistsAndLoadBean ( t , & repo_model . Repository { ID : repoID } )
2017-10-27 00:16:13 +03:00
}
return repoCache [ repoID ]
}
2017-07-12 04:23:41 +03:00
func TestAPIViewRepo ( t * testing . T ) {
2022-09-02 22:18:23 +03:00
defer tests . PrepareTestEnv ( t ) ( )
2017-07-12 04:23:41 +03:00
2020-04-06 21:42:30 +03:00
var repo api . Repository
2017-07-12 04:23:41 +03:00
req := NewRequest ( t , "GET" , "/api/v1/repos/user2/repo1" )
resp := MakeRequest ( t , req , http . StatusOK )
DecodeJSON ( t , resp , & repo )
assert . EqualValues ( t , 1 , repo . ID )
assert . EqualValues ( t , "repo1" , repo . Name )
2021-06-18 02:24:55 +03:00
assert . EqualValues ( t , 2 , repo . Releases )
2020-04-06 21:42:30 +03:00
assert . EqualValues ( t , 1 , repo . OpenIssues )
assert . EqualValues ( t , 3 , repo . OpenPulls )
req = NewRequest ( t , "GET" , "/api/v1/repos/user12/repo10" )
resp = MakeRequest ( t , req , http . StatusOK )
DecodeJSON ( t , resp , & repo )
assert . EqualValues ( t , 10 , repo . ID )
assert . EqualValues ( t , "repo10" , repo . Name )
assert . EqualValues ( t , 1 , repo . OpenPulls )
assert . EqualValues ( t , 1 , repo . Forks )
req = NewRequest ( t , "GET" , "/api/v1/repos/user5/repo4" )
resp = MakeRequest ( t , req , http . StatusOK )
DecodeJSON ( t , resp , & repo )
assert . EqualValues ( t , 4 , repo . ID )
assert . EqualValues ( t , "repo4" , repo . Name )
assert . EqualValues ( t , 1 , repo . Stars )
2017-07-12 04:23:41 +03:00
}
2017-07-13 14:14:15 +03:00
func TestAPIOrgRepos ( t * testing . T ) {
2022-09-02 22:18:23 +03:00
defer tests . PrepareTestEnv ( t ) ( )
2022-08-16 05:22:25 +03:00
user := unittest . AssertExistsAndLoadBean ( t , & user_model . User { ID : 2 } )
user2 := unittest . AssertExistsAndLoadBean ( t , & user_model . User { ID : 1 } )
2023-09-14 05:59:53 +03:00
org3 := unittest . AssertExistsAndLoadBean ( t , & user_model . User { ID : 5 } )
// org3 is an Org. Check their repos.
2022-08-16 05:22:25 +03:00
sourceOrg := unittest . AssertExistsAndLoadBean ( t , & user_model . User { ID : 3 } )
2017-07-13 14:14:15 +03:00
2021-11-24 12:49:20 +03:00
expectedResults := map [ * user_model . User ] struct {
2018-11-24 00:23:27 +03:00
count int
includesPrivate bool
} {
2023-01-18 00:46:03 +03:00
user : { count : 1 } ,
2020-02-15 07:29:06 +03:00
user : { count : 3 , includesPrivate : true } ,
2018-11-24 00:23:27 +03:00
user2 : { count : 3 , includesPrivate : true } ,
2023-09-14 05:59:53 +03:00
org3 : { count : 1 } ,
2018-11-24 00:23:27 +03:00
}
for userToLogin , expected := range expectedResults {
2023-01-18 00:46:03 +03:00
testName := fmt . Sprintf ( "LoggedUser%d" , userToLogin . ID )
session := loginUser ( t , userToLogin . Name )
Redesign Scoped Access Tokens (#24767)
## Changes
- Adds the following high level access scopes, each with `read` and
`write` levels:
- `activitypub`
- `admin` (hidden if user is not a site admin)
- `misc`
- `notification`
- `organization`
- `package`
- `issue`
- `repository`
- `user`
- Adds new middleware function `tokenRequiresScopes()` in addition to
`reqToken()`
- `tokenRequiresScopes()` is used for each high-level api section
- _if_ a scoped token is present, checks that the required scope is
included based on the section and HTTP method
- `reqToken()` is used for individual routes
- checks that required authentication is present (but does not check
scope levels as this will already have been handled by
`tokenRequiresScopes()`
- Adds migration to convert old scoped access tokens to the new set of
scopes
- Updates the user interface for scope selection
### User interface example
<img width="903" alt="Screen Shot 2023-05-31 at 1 56 55 PM"
src="https://github.com/go-gitea/gitea/assets/23248839/654766ec-2143-4f59-9037-3b51600e32f3">
<img width="917" alt="Screen Shot 2023-05-31 at 1 56 43 PM"
src="https://github.com/go-gitea/gitea/assets/23248839/1ad64081-012c-4a73-b393-66b30352654c">
## tokenRequiresScopes Design Decision
- `tokenRequiresScopes()` was added to more reliably cover api routes.
For an incoming request, this function uses the given scope category
(say `AccessTokenScopeCategoryOrganization`) and the HTTP method (say
`DELETE`) and verifies that any scoped tokens in use include
`delete:organization`.
- `reqToken()` is used to enforce auth for individual routes that
require it. If a scoped token is not present for a request,
`tokenRequiresScopes()` will not return an error
## TODO
- [x] Alphabetize scope categories
- [x] Change 'public repos only' to a radio button (private vs public).
Also expand this to organizations
- [X] Disable token creation if no scopes selected. Alternatively, show
warning
- [x] `reqToken()` is missing from many `POST/DELETE` routes in the api.
`tokenRequiresScopes()` only checks that a given token has the correct
scope, `reqToken()` must be used to check that a token (or some other
auth) is present.
- _This should be addressed in this PR_
- [x] The migration should be reviewed very carefully in order to
minimize access changes to existing user tokens.
- _This should be addressed in this PR_
- [x] Link to api to swagger documentation, clarify what
read/write/delete levels correspond to
- [x] Review cases where more than one scope is needed as this directly
deviates from the api definition.
- _This should be addressed in this PR_
- For example:
```go
m.Group("/users/{username}/orgs", func() {
m.Get("", reqToken(), org.ListUserOrgs)
m.Get("/{org}/permissions", reqToken(), org.GetUserOrgsPermissions)
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser,
auth_model.AccessTokenScopeCategoryOrganization),
context_service.UserAssignmentAPI())
```
## Future improvements
- [ ] Add required scopes to swagger documentation
- [ ] Redesign `reqToken()` to be opt-out rather than opt-in
- [ ] Subdivide scopes like `repository`
- [ ] Once a token is created, if it has no scopes, we should display
text instead of an empty bullet point
- [ ] If the 'public repos only' option is selected, should read
categories be selected by default
Closes #24501
Closes #24799
Co-authored-by: Jonathan Tran <jon@allspice.io>
Co-authored-by: Kyle D <kdumontnu@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2023-06-04 21:57:16 +03:00
token := getTokenForLoggedInUser ( t , session , auth_model . AccessTokenScopeReadOrganization )
2023-01-18 00:46:03 +03:00
2018-11-24 00:23:27 +03:00
t . Run ( testName , func ( t * testing . T ) {
2023-12-22 02:59:59 +03:00
req := NewRequestf ( t , "GET" , "/api/v1/orgs/%s/repos" , sourceOrg . Name ) .
AddTokenAuth ( token )
2022-12-02 06:39:42 +03:00
resp := MakeRequest ( t , req , http . StatusOK )
2018-11-24 00:23:27 +03:00
var apiRepos [ ] * api . Repository
DecodeJSON ( t , resp , & apiRepos )
assert . Len ( t , apiRepos , expected . count )
for _ , repo := range apiRepos {
if ! expected . includesPrivate {
assert . False ( t , repo . Private )
}
}
} )
2017-07-13 14:14:15 +03:00
}
}
2017-07-30 04:13:33 +03:00
func TestAPIGetRepoByIDUnauthorized ( t * testing . T ) {
2022-09-02 22:18:23 +03:00
defer tests . PrepareTestEnv ( t ) ( )
2022-08-16 05:22:25 +03:00
user := unittest . AssertExistsAndLoadBean ( t , & user_model . User { ID : 4 } )
2018-09-10 19:15:52 +03:00
session := loginUser ( t , user . Name )
Redesign Scoped Access Tokens (#24767)
## Changes
- Adds the following high level access scopes, each with `read` and
`write` levels:
- `activitypub`
- `admin` (hidden if user is not a site admin)
- `misc`
- `notification`
- `organization`
- `package`
- `issue`
- `repository`
- `user`
- Adds new middleware function `tokenRequiresScopes()` in addition to
`reqToken()`
- `tokenRequiresScopes()` is used for each high-level api section
- _if_ a scoped token is present, checks that the required scope is
included based on the section and HTTP method
- `reqToken()` is used for individual routes
- checks that required authentication is present (but does not check
scope levels as this will already have been handled by
`tokenRequiresScopes()`
- Adds migration to convert old scoped access tokens to the new set of
scopes
- Updates the user interface for scope selection
### User interface example
<img width="903" alt="Screen Shot 2023-05-31 at 1 56 55 PM"
src="https://github.com/go-gitea/gitea/assets/23248839/654766ec-2143-4f59-9037-3b51600e32f3">
<img width="917" alt="Screen Shot 2023-05-31 at 1 56 43 PM"
src="https://github.com/go-gitea/gitea/assets/23248839/1ad64081-012c-4a73-b393-66b30352654c">
## tokenRequiresScopes Design Decision
- `tokenRequiresScopes()` was added to more reliably cover api routes.
For an incoming request, this function uses the given scope category
(say `AccessTokenScopeCategoryOrganization`) and the HTTP method (say
`DELETE`) and verifies that any scoped tokens in use include
`delete:organization`.
- `reqToken()` is used to enforce auth for individual routes that
require it. If a scoped token is not present for a request,
`tokenRequiresScopes()` will not return an error
## TODO
- [x] Alphabetize scope categories
- [x] Change 'public repos only' to a radio button (private vs public).
Also expand this to organizations
- [X] Disable token creation if no scopes selected. Alternatively, show
warning
- [x] `reqToken()` is missing from many `POST/DELETE` routes in the api.
`tokenRequiresScopes()` only checks that a given token has the correct
scope, `reqToken()` must be used to check that a token (or some other
auth) is present.
- _This should be addressed in this PR_
- [x] The migration should be reviewed very carefully in order to
minimize access changes to existing user tokens.
- _This should be addressed in this PR_
- [x] Link to api to swagger documentation, clarify what
read/write/delete levels correspond to
- [x] Review cases where more than one scope is needed as this directly
deviates from the api definition.
- _This should be addressed in this PR_
- For example:
```go
m.Group("/users/{username}/orgs", func() {
m.Get("", reqToken(), org.ListUserOrgs)
m.Get("/{org}/permissions", reqToken(), org.GetUserOrgsPermissions)
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser,
auth_model.AccessTokenScopeCategoryOrganization),
context_service.UserAssignmentAPI())
```
## Future improvements
- [ ] Add required scopes to swagger documentation
- [ ] Redesign `reqToken()` to be opt-out rather than opt-in
- [ ] Subdivide scopes like `repository`
- [ ] Once a token is created, if it has no scopes, we should display
text instead of an empty bullet point
- [ ] If the 'public repos only' option is selected, should read
categories be selected by default
Closes #24501
Closes #24799
Co-authored-by: Jonathan Tran <jon@allspice.io>
Co-authored-by: Kyle D <kdumontnu@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2023-06-04 21:57:16 +03:00
token := getTokenForLoggedInUser ( t , session , auth_model . AccessTokenScopeReadRepository )
2023-12-22 02:59:59 +03:00
req := NewRequest ( t , "GET" , "/api/v1/repositories/2" ) .
AddTokenAuth ( token )
2022-12-02 06:39:42 +03:00
MakeRequest ( t , req , http . StatusNotFound )
2017-07-30 04:13:33 +03:00
}
2018-07-05 01:45:15 +03:00
func TestAPIRepoMigrate ( t * testing . T ) {
testCases := [ ] struct {
ctxUserID , userID int64
cloneURL , repoName string
expectedStatus int
} {
2020-07-24 07:46:38 +03:00
{ ctxUserID : 1 , userID : 2 , cloneURL : "https://github.com/go-gitea/test_repo.git" , repoName : "git-admin" , expectedStatus : http . StatusCreated } ,
{ ctxUserID : 2 , userID : 2 , cloneURL : "https://github.com/go-gitea/test_repo.git" , repoName : "git-own" , expectedStatus : http . StatusCreated } ,
{ ctxUserID : 2 , userID : 1 , cloneURL : "https://github.com/go-gitea/test_repo.git" , repoName : "git-bad" , expectedStatus : http . StatusForbidden } ,
{ ctxUserID : 2 , userID : 3 , cloneURL : "https://github.com/go-gitea/test_repo.git" , repoName : "git-org" , expectedStatus : http . StatusCreated } ,
{ ctxUserID : 2 , userID : 6 , cloneURL : "https://github.com/go-gitea/test_repo.git" , repoName : "git-bad-org" , expectedStatus : http . StatusForbidden } ,
2021-03-16 00:52:11 +03:00
{ ctxUserID : 2 , userID : 3 , cloneURL : "https://localhost:3000/user/test_repo.git" , repoName : "private-ip" , expectedStatus : http . StatusUnprocessableEntity } ,
2020-11-29 03:37:58 +03:00
{ ctxUserID : 2 , userID : 3 , cloneURL : "https://10.0.0.1/user/test_repo.git" , repoName : "private-ip" , expectedStatus : http . StatusUnprocessableEntity } ,
2018-07-05 01:45:15 +03:00
}
2022-09-02 22:18:23 +03:00
defer tests . PrepareTestEnv ( t ) ( )
2018-07-05 01:45:15 +03:00
for _ , testCase := range testCases {
2022-08-16 05:22:25 +03:00
user := unittest . AssertExistsAndLoadBean ( t , & user_model . User { ID : testCase . ctxUserID } )
2018-07-05 01:45:15 +03:00
session := loginUser ( t , user . Name )
Redesign Scoped Access Tokens (#24767)
## Changes
- Adds the following high level access scopes, each with `read` and
`write` levels:
- `activitypub`
- `admin` (hidden if user is not a site admin)
- `misc`
- `notification`
- `organization`
- `package`
- `issue`
- `repository`
- `user`
- Adds new middleware function `tokenRequiresScopes()` in addition to
`reqToken()`
- `tokenRequiresScopes()` is used for each high-level api section
- _if_ a scoped token is present, checks that the required scope is
included based on the section and HTTP method
- `reqToken()` is used for individual routes
- checks that required authentication is present (but does not check
scope levels as this will already have been handled by
`tokenRequiresScopes()`
- Adds migration to convert old scoped access tokens to the new set of
scopes
- Updates the user interface for scope selection
### User interface example
<img width="903" alt="Screen Shot 2023-05-31 at 1 56 55 PM"
src="https://github.com/go-gitea/gitea/assets/23248839/654766ec-2143-4f59-9037-3b51600e32f3">
<img width="917" alt="Screen Shot 2023-05-31 at 1 56 43 PM"
src="https://github.com/go-gitea/gitea/assets/23248839/1ad64081-012c-4a73-b393-66b30352654c">
## tokenRequiresScopes Design Decision
- `tokenRequiresScopes()` was added to more reliably cover api routes.
For an incoming request, this function uses the given scope category
(say `AccessTokenScopeCategoryOrganization`) and the HTTP method (say
`DELETE`) and verifies that any scoped tokens in use include
`delete:organization`.
- `reqToken()` is used to enforce auth for individual routes that
require it. If a scoped token is not present for a request,
`tokenRequiresScopes()` will not return an error
## TODO
- [x] Alphabetize scope categories
- [x] Change 'public repos only' to a radio button (private vs public).
Also expand this to organizations
- [X] Disable token creation if no scopes selected. Alternatively, show
warning
- [x] `reqToken()` is missing from many `POST/DELETE` routes in the api.
`tokenRequiresScopes()` only checks that a given token has the correct
scope, `reqToken()` must be used to check that a token (or some other
auth) is present.
- _This should be addressed in this PR_
- [x] The migration should be reviewed very carefully in order to
minimize access changes to existing user tokens.
- _This should be addressed in this PR_
- [x] Link to api to swagger documentation, clarify what
read/write/delete levels correspond to
- [x] Review cases where more than one scope is needed as this directly
deviates from the api definition.
- _This should be addressed in this PR_
- For example:
```go
m.Group("/users/{username}/orgs", func() {
m.Get("", reqToken(), org.ListUserOrgs)
m.Get("/{org}/permissions", reqToken(), org.GetUserOrgsPermissions)
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser,
auth_model.AccessTokenScopeCategoryOrganization),
context_service.UserAssignmentAPI())
```
## Future improvements
- [ ] Add required scopes to swagger documentation
- [ ] Redesign `reqToken()` to be opt-out rather than opt-in
- [ ] Subdivide scopes like `repository`
- [ ] Once a token is created, if it has no scopes, we should display
text instead of an empty bullet point
- [ ] If the 'public repos only' option is selected, should read
categories be selected by default
Closes #24501
Closes #24799
Co-authored-by: Jonathan Tran <jon@allspice.io>
Co-authored-by: Kyle D <kdumontnu@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2023-06-04 21:57:16 +03:00
token := getTokenForLoggedInUser ( t , session , auth_model . AccessTokenScopeWriteRepository )
2023-12-22 02:59:59 +03:00
req := NewRequestWithJSON ( t , "POST" , "/api/v1/repos/migrate" , & api . MigrateRepoOptions {
2020-09-11 01:29:19 +03:00
CloneAddr : testCase . cloneURL ,
RepoOwnerID : testCase . userID ,
RepoName : testCase . repoName ,
2023-12-22 02:59:59 +03:00
} ) . AddTokenAuth ( token )
2020-09-04 20:28:36 +03:00
resp := MakeRequest ( t , req , NoExpectedStatus )
if resp . Code == http . StatusUnprocessableEntity {
respJSON := map [ string ] string { }
DecodeJSON ( t , resp , & respJSON )
2020-11-29 03:37:58 +03:00
switch respJSON [ "message" ] {
case "Remote visit addressed rate limitation." :
2020-09-04 20:28:36 +03:00
t . Log ( "test hit github rate limitation" )
2021-11-20 12:34:05 +03:00
case "You can not import from disallowed hosts." :
2020-11-29 03:37:58 +03:00
assert . EqualValues ( t , "private-ip" , testCase . repoName )
default :
2023-10-11 14:02:24 +03:00
assert . FailNow ( t , "unexpected error '%v' on url '%s'" , respJSON [ "message" ] , testCase . cloneURL )
2020-09-04 20:28:36 +03:00
}
} else {
assert . EqualValues ( t , testCase . expectedStatus , resp . Code )
}
2018-07-05 01:45:15 +03:00
}
}
2018-07-05 02:51:02 +03:00
2019-03-15 17:19:09 +03:00
func TestAPIRepoMigrateConflict ( t * testing . T ) {
onGiteaRun ( t , testAPIRepoMigrateConflict )
}
func testAPIRepoMigrateConflict ( t * testing . T , u * url . URL ) {
username := "user2"
Redesign Scoped Access Tokens (#24767)
## Changes
- Adds the following high level access scopes, each with `read` and
`write` levels:
- `activitypub`
- `admin` (hidden if user is not a site admin)
- `misc`
- `notification`
- `organization`
- `package`
- `issue`
- `repository`
- `user`
- Adds new middleware function `tokenRequiresScopes()` in addition to
`reqToken()`
- `tokenRequiresScopes()` is used for each high-level api section
- _if_ a scoped token is present, checks that the required scope is
included based on the section and HTTP method
- `reqToken()` is used for individual routes
- checks that required authentication is present (but does not check
scope levels as this will already have been handled by
`tokenRequiresScopes()`
- Adds migration to convert old scoped access tokens to the new set of
scopes
- Updates the user interface for scope selection
### User interface example
<img width="903" alt="Screen Shot 2023-05-31 at 1 56 55 PM"
src="https://github.com/go-gitea/gitea/assets/23248839/654766ec-2143-4f59-9037-3b51600e32f3">
<img width="917" alt="Screen Shot 2023-05-31 at 1 56 43 PM"
src="https://github.com/go-gitea/gitea/assets/23248839/1ad64081-012c-4a73-b393-66b30352654c">
## tokenRequiresScopes Design Decision
- `tokenRequiresScopes()` was added to more reliably cover api routes.
For an incoming request, this function uses the given scope category
(say `AccessTokenScopeCategoryOrganization`) and the HTTP method (say
`DELETE`) and verifies that any scoped tokens in use include
`delete:organization`.
- `reqToken()` is used to enforce auth for individual routes that
require it. If a scoped token is not present for a request,
`tokenRequiresScopes()` will not return an error
## TODO
- [x] Alphabetize scope categories
- [x] Change 'public repos only' to a radio button (private vs public).
Also expand this to organizations
- [X] Disable token creation if no scopes selected. Alternatively, show
warning
- [x] `reqToken()` is missing from many `POST/DELETE` routes in the api.
`tokenRequiresScopes()` only checks that a given token has the correct
scope, `reqToken()` must be used to check that a token (or some other
auth) is present.
- _This should be addressed in this PR_
- [x] The migration should be reviewed very carefully in order to
minimize access changes to existing user tokens.
- _This should be addressed in this PR_
- [x] Link to api to swagger documentation, clarify what
read/write/delete levels correspond to
- [x] Review cases where more than one scope is needed as this directly
deviates from the api definition.
- _This should be addressed in this PR_
- For example:
```go
m.Group("/users/{username}/orgs", func() {
m.Get("", reqToken(), org.ListUserOrgs)
m.Get("/{org}/permissions", reqToken(), org.GetUserOrgsPermissions)
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser,
auth_model.AccessTokenScopeCategoryOrganization),
context_service.UserAssignmentAPI())
```
## Future improvements
- [ ] Add required scopes to swagger documentation
- [ ] Redesign `reqToken()` to be opt-out rather than opt-in
- [ ] Subdivide scopes like `repository`
- [ ] Once a token is created, if it has no scopes, we should display
text instead of an empty bullet point
- [ ] If the 'public repos only' option is selected, should read
categories be selected by default
Closes #24501
Closes #24799
Co-authored-by: Jonathan Tran <jon@allspice.io>
Co-authored-by: Kyle D <kdumontnu@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2023-06-04 21:57:16 +03:00
baseAPITestContext := NewAPITestContext ( t , username , "repo1" , auth_model . AccessTokenScopeWriteRepository , auth_model . AccessTokenScopeWriteUser )
2019-03-15 17:19:09 +03:00
u . Path = baseAPITestContext . GitPath ( )
t . Run ( "Existing" , func ( t * testing . T ) {
httpContext := baseAPITestContext
httpContext . Reponame = "repo-tmp-17"
t . Run ( "CreateRepo" , doAPICreateRepository ( httpContext , false ) )
2022-05-20 17:08:52 +03:00
user , err := user_model . GetUserByName ( db . DefaultContext , httpContext . Username )
2019-03-15 17:19:09 +03:00
assert . NoError ( t , err )
userID := user . ID
2020-07-24 07:46:38 +03:00
cloneURL := "https://github.com/go-gitea/test_repo.git"
2019-03-15 17:19:09 +03:00
2023-12-22 02:59:59 +03:00
req := NewRequestWithJSON ( t , "POST" , "/api/v1/repos/migrate" ,
2020-09-11 01:29:19 +03:00
& api . MigrateRepoOptions {
CloneAddr : cloneURL ,
RepoOwnerID : userID ,
RepoName : httpContext . Reponame ,
2023-12-22 02:59:59 +03:00
} ) .
AddTokenAuth ( httpContext . Token )
2019-03-15 17:19:09 +03:00
resp := httpContext . Session . MakeRequest ( t , req , http . StatusConflict )
respJSON := map [ string ] string { }
DecodeJSON ( t , resp , & respJSON )
2019-11-09 01:21:00 +03:00
assert . Equal ( t , "The repository with the same name already exists." , respJSON [ "message" ] )
2019-03-15 17:19:09 +03:00
} )
}
2022-04-01 21:29:57 +03:00
// mirror-sync must fail with "400 (Bad Request)" when an attempt is made to
// sync a non-mirror repository.
func TestAPIMirrorSyncNonMirrorRepo ( t * testing . T ) {
2022-09-02 22:18:23 +03:00
defer tests . PrepareTestEnv ( t ) ( )
2022-04-01 21:29:57 +03:00
session := loginUser ( t , "user2" )
Redesign Scoped Access Tokens (#24767)
## Changes
- Adds the following high level access scopes, each with `read` and
`write` levels:
- `activitypub`
- `admin` (hidden if user is not a site admin)
- `misc`
- `notification`
- `organization`
- `package`
- `issue`
- `repository`
- `user`
- Adds new middleware function `tokenRequiresScopes()` in addition to
`reqToken()`
- `tokenRequiresScopes()` is used for each high-level api section
- _if_ a scoped token is present, checks that the required scope is
included based on the section and HTTP method
- `reqToken()` is used for individual routes
- checks that required authentication is present (but does not check
scope levels as this will already have been handled by
`tokenRequiresScopes()`
- Adds migration to convert old scoped access tokens to the new set of
scopes
- Updates the user interface for scope selection
### User interface example
<img width="903" alt="Screen Shot 2023-05-31 at 1 56 55 PM"
src="https://github.com/go-gitea/gitea/assets/23248839/654766ec-2143-4f59-9037-3b51600e32f3">
<img width="917" alt="Screen Shot 2023-05-31 at 1 56 43 PM"
src="https://github.com/go-gitea/gitea/assets/23248839/1ad64081-012c-4a73-b393-66b30352654c">
## tokenRequiresScopes Design Decision
- `tokenRequiresScopes()` was added to more reliably cover api routes.
For an incoming request, this function uses the given scope category
(say `AccessTokenScopeCategoryOrganization`) and the HTTP method (say
`DELETE`) and verifies that any scoped tokens in use include
`delete:organization`.
- `reqToken()` is used to enforce auth for individual routes that
require it. If a scoped token is not present for a request,
`tokenRequiresScopes()` will not return an error
## TODO
- [x] Alphabetize scope categories
- [x] Change 'public repos only' to a radio button (private vs public).
Also expand this to organizations
- [X] Disable token creation if no scopes selected. Alternatively, show
warning
- [x] `reqToken()` is missing from many `POST/DELETE` routes in the api.
`tokenRequiresScopes()` only checks that a given token has the correct
scope, `reqToken()` must be used to check that a token (or some other
auth) is present.
- _This should be addressed in this PR_
- [x] The migration should be reviewed very carefully in order to
minimize access changes to existing user tokens.
- _This should be addressed in this PR_
- [x] Link to api to swagger documentation, clarify what
read/write/delete levels correspond to
- [x] Review cases where more than one scope is needed as this directly
deviates from the api definition.
- _This should be addressed in this PR_
- For example:
```go
m.Group("/users/{username}/orgs", func() {
m.Get("", reqToken(), org.ListUserOrgs)
m.Get("/{org}/permissions", reqToken(), org.GetUserOrgsPermissions)
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser,
auth_model.AccessTokenScopeCategoryOrganization),
context_service.UserAssignmentAPI())
```
## Future improvements
- [ ] Add required scopes to swagger documentation
- [ ] Redesign `reqToken()` to be opt-out rather than opt-in
- [ ] Subdivide scopes like `repository`
- [ ] Once a token is created, if it has no scopes, we should display
text instead of an empty bullet point
- [ ] If the 'public repos only' option is selected, should read
categories be selected by default
Closes #24501
Closes #24799
Co-authored-by: Jonathan Tran <jon@allspice.io>
Co-authored-by: Kyle D <kdumontnu@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2023-06-04 21:57:16 +03:00
token := getTokenForLoggedInUser ( t , session , auth_model . AccessTokenScopeWriteRepository )
2022-04-01 21:29:57 +03:00
var repo api . Repository
req := NewRequest ( t , "GET" , "/api/v1/repos/user2/repo1" )
resp := MakeRequest ( t , req , http . StatusOK )
DecodeJSON ( t , resp , & repo )
2023-04-23 00:56:27 +03:00
assert . False ( t , repo . Mirror )
2022-04-01 21:29:57 +03:00
2023-12-22 02:59:59 +03:00
req = NewRequestf ( t , "POST" , "/api/v1/repos/user2/repo1/mirror-sync" ) .
AddTokenAuth ( token )
2022-12-02 06:39:42 +03:00
resp = MakeRequest ( t , req , http . StatusBadRequest )
2022-04-01 21:29:57 +03:00
errRespJSON := map [ string ] string { }
DecodeJSON ( t , resp , & errRespJSON )
assert . Equal ( t , "Repository is not a mirror" , errRespJSON [ "message" ] )
}
2018-07-05 02:51:02 +03:00
func TestAPIOrgRepoCreate ( t * testing . T ) {
testCases := [ ] struct {
ctxUserID int64
orgName , repoName string
expectedStatus int
} {
2023-09-14 05:59:53 +03:00
{ ctxUserID : 1 , orgName : "org3" , repoName : "repo-admin" , expectedStatus : http . StatusCreated } ,
{ ctxUserID : 2 , orgName : "org3" , repoName : "repo-own" , expectedStatus : http . StatusCreated } ,
{ ctxUserID : 2 , orgName : "org6" , repoName : "repo-bad-org" , expectedStatus : http . StatusForbidden } ,
{ ctxUserID : 28 , orgName : "org3" , repoName : "repo-creator" , expectedStatus : http . StatusCreated } ,
{ ctxUserID : 28 , orgName : "org6" , repoName : "repo-not-creator" , expectedStatus : http . StatusForbidden } ,
2018-07-05 02:51:02 +03:00
}
2022-09-02 22:18:23 +03:00
defer tests . PrepareTestEnv ( t ) ( )
2018-07-05 02:51:02 +03:00
for _ , testCase := range testCases {
2022-08-16 05:22:25 +03:00
user := unittest . AssertExistsAndLoadBean ( t , & user_model . User { ID : testCase . ctxUserID } )
2018-07-05 02:51:02 +03:00
session := loginUser ( t , user . Name )
Redesign Scoped Access Tokens (#24767)
## Changes
- Adds the following high level access scopes, each with `read` and
`write` levels:
- `activitypub`
- `admin` (hidden if user is not a site admin)
- `misc`
- `notification`
- `organization`
- `package`
- `issue`
- `repository`
- `user`
- Adds new middleware function `tokenRequiresScopes()` in addition to
`reqToken()`
- `tokenRequiresScopes()` is used for each high-level api section
- _if_ a scoped token is present, checks that the required scope is
included based on the section and HTTP method
- `reqToken()` is used for individual routes
- checks that required authentication is present (but does not check
scope levels as this will already have been handled by
`tokenRequiresScopes()`
- Adds migration to convert old scoped access tokens to the new set of
scopes
- Updates the user interface for scope selection
### User interface example
<img width="903" alt="Screen Shot 2023-05-31 at 1 56 55 PM"
src="https://github.com/go-gitea/gitea/assets/23248839/654766ec-2143-4f59-9037-3b51600e32f3">
<img width="917" alt="Screen Shot 2023-05-31 at 1 56 43 PM"
src="https://github.com/go-gitea/gitea/assets/23248839/1ad64081-012c-4a73-b393-66b30352654c">
## tokenRequiresScopes Design Decision
- `tokenRequiresScopes()` was added to more reliably cover api routes.
For an incoming request, this function uses the given scope category
(say `AccessTokenScopeCategoryOrganization`) and the HTTP method (say
`DELETE`) and verifies that any scoped tokens in use include
`delete:organization`.
- `reqToken()` is used to enforce auth for individual routes that
require it. If a scoped token is not present for a request,
`tokenRequiresScopes()` will not return an error
## TODO
- [x] Alphabetize scope categories
- [x] Change 'public repos only' to a radio button (private vs public).
Also expand this to organizations
- [X] Disable token creation if no scopes selected. Alternatively, show
warning
- [x] `reqToken()` is missing from many `POST/DELETE` routes in the api.
`tokenRequiresScopes()` only checks that a given token has the correct
scope, `reqToken()` must be used to check that a token (or some other
auth) is present.
- _This should be addressed in this PR_
- [x] The migration should be reviewed very carefully in order to
minimize access changes to existing user tokens.
- _This should be addressed in this PR_
- [x] Link to api to swagger documentation, clarify what
read/write/delete levels correspond to
- [x] Review cases where more than one scope is needed as this directly
deviates from the api definition.
- _This should be addressed in this PR_
- For example:
```go
m.Group("/users/{username}/orgs", func() {
m.Get("", reqToken(), org.ListUserOrgs)
m.Get("/{org}/permissions", reqToken(), org.GetUserOrgsPermissions)
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser,
auth_model.AccessTokenScopeCategoryOrganization),
context_service.UserAssignmentAPI())
```
## Future improvements
- [ ] Add required scopes to swagger documentation
- [ ] Redesign `reqToken()` to be opt-out rather than opt-in
- [ ] Subdivide scopes like `repository`
- [ ] Once a token is created, if it has no scopes, we should display
text instead of an empty bullet point
- [ ] If the 'public repos only' option is selected, should read
categories be selected by default
Closes #24501
Closes #24799
Co-authored-by: Jonathan Tran <jon@allspice.io>
Co-authored-by: Kyle D <kdumontnu@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2023-06-04 21:57:16 +03:00
token := getTokenForLoggedInUser ( t , session , auth_model . AccessTokenScopeWriteOrganization , auth_model . AccessTokenScopeWriteRepository )
2023-12-22 02:59:59 +03:00
req := NewRequestWithJSON ( t , "POST" , fmt . Sprintf ( "/api/v1/org/%s/repos" , testCase . orgName ) , & api . CreateRepoOption {
2018-07-05 02:51:02 +03:00
Name : testCase . repoName ,
2023-12-22 02:59:59 +03:00
} ) . AddTokenAuth ( token )
2022-12-02 06:39:42 +03:00
MakeRequest ( t , req , testCase . expectedStatus )
2018-07-05 02:51:02 +03:00
}
}
2019-03-15 17:19:09 +03:00
func TestAPIRepoCreateConflict ( t * testing . T ) {
onGiteaRun ( t , testAPIRepoCreateConflict )
}
func testAPIRepoCreateConflict ( t * testing . T , u * url . URL ) {
username := "user2"
Redesign Scoped Access Tokens (#24767)
## Changes
- Adds the following high level access scopes, each with `read` and
`write` levels:
- `activitypub`
- `admin` (hidden if user is not a site admin)
- `misc`
- `notification`
- `organization`
- `package`
- `issue`
- `repository`
- `user`
- Adds new middleware function `tokenRequiresScopes()` in addition to
`reqToken()`
- `tokenRequiresScopes()` is used for each high-level api section
- _if_ a scoped token is present, checks that the required scope is
included based on the section and HTTP method
- `reqToken()` is used for individual routes
- checks that required authentication is present (but does not check
scope levels as this will already have been handled by
`tokenRequiresScopes()`
- Adds migration to convert old scoped access tokens to the new set of
scopes
- Updates the user interface for scope selection
### User interface example
<img width="903" alt="Screen Shot 2023-05-31 at 1 56 55 PM"
src="https://github.com/go-gitea/gitea/assets/23248839/654766ec-2143-4f59-9037-3b51600e32f3">
<img width="917" alt="Screen Shot 2023-05-31 at 1 56 43 PM"
src="https://github.com/go-gitea/gitea/assets/23248839/1ad64081-012c-4a73-b393-66b30352654c">
## tokenRequiresScopes Design Decision
- `tokenRequiresScopes()` was added to more reliably cover api routes.
For an incoming request, this function uses the given scope category
(say `AccessTokenScopeCategoryOrganization`) and the HTTP method (say
`DELETE`) and verifies that any scoped tokens in use include
`delete:organization`.
- `reqToken()` is used to enforce auth for individual routes that
require it. If a scoped token is not present for a request,
`tokenRequiresScopes()` will not return an error
## TODO
- [x] Alphabetize scope categories
- [x] Change 'public repos only' to a radio button (private vs public).
Also expand this to organizations
- [X] Disable token creation if no scopes selected. Alternatively, show
warning
- [x] `reqToken()` is missing from many `POST/DELETE` routes in the api.
`tokenRequiresScopes()` only checks that a given token has the correct
scope, `reqToken()` must be used to check that a token (or some other
auth) is present.
- _This should be addressed in this PR_
- [x] The migration should be reviewed very carefully in order to
minimize access changes to existing user tokens.
- _This should be addressed in this PR_
- [x] Link to api to swagger documentation, clarify what
read/write/delete levels correspond to
- [x] Review cases where more than one scope is needed as this directly
deviates from the api definition.
- _This should be addressed in this PR_
- For example:
```go
m.Group("/users/{username}/orgs", func() {
m.Get("", reqToken(), org.ListUserOrgs)
m.Get("/{org}/permissions", reqToken(), org.GetUserOrgsPermissions)
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser,
auth_model.AccessTokenScopeCategoryOrganization),
context_service.UserAssignmentAPI())
```
## Future improvements
- [ ] Add required scopes to swagger documentation
- [ ] Redesign `reqToken()` to be opt-out rather than opt-in
- [ ] Subdivide scopes like `repository`
- [ ] Once a token is created, if it has no scopes, we should display
text instead of an empty bullet point
- [ ] If the 'public repos only' option is selected, should read
categories be selected by default
Closes #24501
Closes #24799
Co-authored-by: Jonathan Tran <jon@allspice.io>
Co-authored-by: Kyle D <kdumontnu@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2023-06-04 21:57:16 +03:00
baseAPITestContext := NewAPITestContext ( t , username , "repo1" , auth_model . AccessTokenScopeWriteRepository , auth_model . AccessTokenScopeWriteUser )
2019-03-15 17:19:09 +03:00
u . Path = baseAPITestContext . GitPath ( )
t . Run ( "Existing" , func ( t * testing . T ) {
httpContext := baseAPITestContext
httpContext . Reponame = "repo-tmp-17"
t . Run ( "CreateRepo" , doAPICreateRepository ( httpContext , false ) )
2023-12-22 02:59:59 +03:00
req := NewRequestWithJSON ( t , "POST" , "/api/v1/user/repos" ,
2019-03-15 17:19:09 +03:00
& api . CreateRepoOption {
Name : httpContext . Reponame ,
2023-12-22 02:59:59 +03:00
} ) .
AddTokenAuth ( httpContext . Token )
2019-03-15 17:19:09 +03:00
resp := httpContext . Session . MakeRequest ( t , req , http . StatusConflict )
respJSON := map [ string ] string { }
DecodeJSON ( t , resp , & respJSON )
2023-04-23 00:56:27 +03:00
assert . Equal ( t , "The repository with the same name already exists." , respJSON [ "message" ] )
2019-03-15 17:19:09 +03:00
} )
}
2020-01-31 18:49:04 +03:00
func TestAPIRepoTransfer ( t * testing . T ) {
testCases := [ ] struct {
ctxUserID int64
newOwner string
teams * [ ] int64
expectedStatus int
} {
2023-09-14 05:59:53 +03:00
// Disclaimer for test story: "user1" is an admin, "user2" is normal user and part of in owner team of org "org3"
2021-03-01 03:47:30 +03:00
// Transfer to a user with teams in another org should fail
2023-09-14 05:59:53 +03:00
{ ctxUserID : 1 , newOwner : "org3" , teams : & [ ] int64 { 5 } , expectedStatus : http . StatusForbidden } ,
2021-03-01 03:47:30 +03:00
// Transfer to a user with non-existent team IDs should fail
{ ctxUserID : 1 , newOwner : "user2" , teams : & [ ] int64 { 2 } , expectedStatus : http . StatusUnprocessableEntity } ,
// Transfer should go through
2023-09-14 05:59:53 +03:00
{ ctxUserID : 1 , newOwner : "org3" , teams : & [ ] int64 { 2 } , expectedStatus : http . StatusAccepted } ,
2021-03-01 03:47:30 +03:00
// Let user transfer it back to himself
{ ctxUserID : 2 , newOwner : "user2" , expectedStatus : http . StatusAccepted } ,
// And revert transfer
2023-09-14 05:59:53 +03:00
{ ctxUserID : 2 , newOwner : "org3" , teams : & [ ] int64 { 2 } , expectedStatus : http . StatusAccepted } ,
2021-03-01 03:47:30 +03:00
// Cannot start transfer to an existing repo
2023-09-14 05:59:53 +03:00
{ ctxUserID : 2 , newOwner : "org3" , teams : nil , expectedStatus : http . StatusUnprocessableEntity } ,
2021-03-01 03:47:30 +03:00
// Start transfer, repo is now in pending transfer mode
2023-09-14 05:59:53 +03:00
{ ctxUserID : 2 , newOwner : "org6" , teams : nil , expectedStatus : http . StatusCreated } ,
2020-01-31 18:49:04 +03:00
}
2022-09-02 22:18:23 +03:00
defer tests . PrepareTestEnv ( t ) ( )
2020-01-31 18:49:04 +03:00
2022-01-20 20:46:10 +03:00
// create repo to move
2022-08-16 05:22:25 +03:00
user := unittest . AssertExistsAndLoadBean ( t , & user_model . User { ID : 1 } )
2020-01-31 18:49:04 +03:00
session := loginUser ( t , user . Name )
Redesign Scoped Access Tokens (#24767)
## Changes
- Adds the following high level access scopes, each with `read` and
`write` levels:
- `activitypub`
- `admin` (hidden if user is not a site admin)
- `misc`
- `notification`
- `organization`
- `package`
- `issue`
- `repository`
- `user`
- Adds new middleware function `tokenRequiresScopes()` in addition to
`reqToken()`
- `tokenRequiresScopes()` is used for each high-level api section
- _if_ a scoped token is present, checks that the required scope is
included based on the section and HTTP method
- `reqToken()` is used for individual routes
- checks that required authentication is present (but does not check
scope levels as this will already have been handled by
`tokenRequiresScopes()`
- Adds migration to convert old scoped access tokens to the new set of
scopes
- Updates the user interface for scope selection
### User interface example
<img width="903" alt="Screen Shot 2023-05-31 at 1 56 55 PM"
src="https://github.com/go-gitea/gitea/assets/23248839/654766ec-2143-4f59-9037-3b51600e32f3">
<img width="917" alt="Screen Shot 2023-05-31 at 1 56 43 PM"
src="https://github.com/go-gitea/gitea/assets/23248839/1ad64081-012c-4a73-b393-66b30352654c">
## tokenRequiresScopes Design Decision
- `tokenRequiresScopes()` was added to more reliably cover api routes.
For an incoming request, this function uses the given scope category
(say `AccessTokenScopeCategoryOrganization`) and the HTTP method (say
`DELETE`) and verifies that any scoped tokens in use include
`delete:organization`.
- `reqToken()` is used to enforce auth for individual routes that
require it. If a scoped token is not present for a request,
`tokenRequiresScopes()` will not return an error
## TODO
- [x] Alphabetize scope categories
- [x] Change 'public repos only' to a radio button (private vs public).
Also expand this to organizations
- [X] Disable token creation if no scopes selected. Alternatively, show
warning
- [x] `reqToken()` is missing from many `POST/DELETE` routes in the api.
`tokenRequiresScopes()` only checks that a given token has the correct
scope, `reqToken()` must be used to check that a token (or some other
auth) is present.
- _This should be addressed in this PR_
- [x] The migration should be reviewed very carefully in order to
minimize access changes to existing user tokens.
- _This should be addressed in this PR_
- [x] Link to api to swagger documentation, clarify what
read/write/delete levels correspond to
- [x] Review cases where more than one scope is needed as this directly
deviates from the api definition.
- _This should be addressed in this PR_
- For example:
```go
m.Group("/users/{username}/orgs", func() {
m.Get("", reqToken(), org.ListUserOrgs)
m.Get("/{org}/permissions", reqToken(), org.GetUserOrgsPermissions)
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser,
auth_model.AccessTokenScopeCategoryOrganization),
context_service.UserAssignmentAPI())
```
## Future improvements
- [ ] Add required scopes to swagger documentation
- [ ] Redesign `reqToken()` to be opt-out rather than opt-in
- [ ] Subdivide scopes like `repository`
- [ ] Once a token is created, if it has no scopes, we should display
text instead of an empty bullet point
- [ ] If the 'public repos only' option is selected, should read
categories be selected by default
Closes #24501
Closes #24799
Co-authored-by: Jonathan Tran <jon@allspice.io>
Co-authored-by: Kyle D <kdumontnu@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2023-06-04 21:57:16 +03:00
token := getTokenForLoggedInUser ( t , session , auth_model . AccessTokenScopeWriteRepository , auth_model . AccessTokenScopeWriteUser )
2020-01-31 18:49:04 +03:00
repoName := "moveME"
2021-06-18 17:46:22 +03:00
apiRepo := new ( api . Repository )
2023-12-22 02:59:59 +03:00
req := NewRequestWithJSON ( t , "POST" , "/api/v1/user/repos" , & api . CreateRepoOption {
2020-01-31 18:49:04 +03:00
Name : repoName ,
Description : "repo move around" ,
Private : false ,
Readme : "Default" ,
AutoInit : true ,
2023-12-22 02:59:59 +03:00
} ) . AddTokenAuth ( token )
2022-12-02 06:39:42 +03:00
resp := MakeRequest ( t , req , http . StatusCreated )
2021-06-18 17:46:22 +03:00
DecodeJSON ( t , resp , apiRepo )
2020-01-31 18:49:04 +03:00
2022-01-20 20:46:10 +03:00
// start testing
2020-01-31 18:49:04 +03:00
for _ , testCase := range testCases {
2022-08-16 05:22:25 +03:00
user = unittest . AssertExistsAndLoadBean ( t , & user_model . User { ID : testCase . ctxUserID } )
repo := unittest . AssertExistsAndLoadBean ( t , & repo_model . Repository { ID : apiRepo . ID } )
2020-01-31 18:49:04 +03:00
session = loginUser ( t , user . Name )
Redesign Scoped Access Tokens (#24767)
## Changes
- Adds the following high level access scopes, each with `read` and
`write` levels:
- `activitypub`
- `admin` (hidden if user is not a site admin)
- `misc`
- `notification`
- `organization`
- `package`
- `issue`
- `repository`
- `user`
- Adds new middleware function `tokenRequiresScopes()` in addition to
`reqToken()`
- `tokenRequiresScopes()` is used for each high-level api section
- _if_ a scoped token is present, checks that the required scope is
included based on the section and HTTP method
- `reqToken()` is used for individual routes
- checks that required authentication is present (but does not check
scope levels as this will already have been handled by
`tokenRequiresScopes()`
- Adds migration to convert old scoped access tokens to the new set of
scopes
- Updates the user interface for scope selection
### User interface example
<img width="903" alt="Screen Shot 2023-05-31 at 1 56 55 PM"
src="https://github.com/go-gitea/gitea/assets/23248839/654766ec-2143-4f59-9037-3b51600e32f3">
<img width="917" alt="Screen Shot 2023-05-31 at 1 56 43 PM"
src="https://github.com/go-gitea/gitea/assets/23248839/1ad64081-012c-4a73-b393-66b30352654c">
## tokenRequiresScopes Design Decision
- `tokenRequiresScopes()` was added to more reliably cover api routes.
For an incoming request, this function uses the given scope category
(say `AccessTokenScopeCategoryOrganization`) and the HTTP method (say
`DELETE`) and verifies that any scoped tokens in use include
`delete:organization`.
- `reqToken()` is used to enforce auth for individual routes that
require it. If a scoped token is not present for a request,
`tokenRequiresScopes()` will not return an error
## TODO
- [x] Alphabetize scope categories
- [x] Change 'public repos only' to a radio button (private vs public).
Also expand this to organizations
- [X] Disable token creation if no scopes selected. Alternatively, show
warning
- [x] `reqToken()` is missing from many `POST/DELETE` routes in the api.
`tokenRequiresScopes()` only checks that a given token has the correct
scope, `reqToken()` must be used to check that a token (or some other
auth) is present.
- _This should be addressed in this PR_
- [x] The migration should be reviewed very carefully in order to
minimize access changes to existing user tokens.
- _This should be addressed in this PR_
- [x] Link to api to swagger documentation, clarify what
read/write/delete levels correspond to
- [x] Review cases where more than one scope is needed as this directly
deviates from the api definition.
- _This should be addressed in this PR_
- For example:
```go
m.Group("/users/{username}/orgs", func() {
m.Get("", reqToken(), org.ListUserOrgs)
m.Get("/{org}/permissions", reqToken(), org.GetUserOrgsPermissions)
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser,
auth_model.AccessTokenScopeCategoryOrganization),
context_service.UserAssignmentAPI())
```
## Future improvements
- [ ] Add required scopes to swagger documentation
- [ ] Redesign `reqToken()` to be opt-out rather than opt-in
- [ ] Subdivide scopes like `repository`
- [ ] Once a token is created, if it has no scopes, we should display
text instead of an empty bullet point
- [ ] If the 'public repos only' option is selected, should read
categories be selected by default
Closes #24501
Closes #24799
Co-authored-by: Jonathan Tran <jon@allspice.io>
Co-authored-by: Kyle D <kdumontnu@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2023-06-04 21:57:16 +03:00
token = getTokenForLoggedInUser ( t , session , auth_model . AccessTokenScopeWriteRepository )
2023-12-22 02:59:59 +03:00
req = NewRequestWithJSON ( t , "POST" , fmt . Sprintf ( "/api/v1/repos/%s/%s/transfer" , repo . OwnerName , repo . Name ) , & api . TransferRepoOption {
2020-01-31 18:49:04 +03:00
NewOwner : testCase . newOwner ,
TeamIDs : testCase . teams ,
2023-12-22 02:59:59 +03:00
} ) . AddTokenAuth ( token )
2022-12-02 06:39:42 +03:00
MakeRequest ( t , req , testCase . expectedStatus )
2020-01-31 18:49:04 +03:00
}
2022-01-20 20:46:10 +03:00
// cleanup
2022-08-16 05:22:25 +03:00
repo := unittest . AssertExistsAndLoadBean ( t , & repo_model . Repository { ID : apiRepo . ID } )
2023-10-18 18:03:10 +03:00
_ = repo_service . DeleteRepositoryDirectly ( db . DefaultContext , user , repo . ID )
2020-01-31 18:49:04 +03:00
}
2021-06-17 17:02:34 +03:00
2021-12-24 07:26:52 +03:00
func transfer ( t * testing . T ) * repo_model . Repository {
2022-01-20 20:46:10 +03:00
// create repo to move
2022-08-16 05:22:25 +03:00
user := unittest . AssertExistsAndLoadBean ( t , & user_model . User { ID : 2 } )
2021-12-24 07:26:52 +03:00
session := loginUser ( t , user . Name )
Redesign Scoped Access Tokens (#24767)
## Changes
- Adds the following high level access scopes, each with `read` and
`write` levels:
- `activitypub`
- `admin` (hidden if user is not a site admin)
- `misc`
- `notification`
- `organization`
- `package`
- `issue`
- `repository`
- `user`
- Adds new middleware function `tokenRequiresScopes()` in addition to
`reqToken()`
- `tokenRequiresScopes()` is used for each high-level api section
- _if_ a scoped token is present, checks that the required scope is
included based on the section and HTTP method
- `reqToken()` is used for individual routes
- checks that required authentication is present (but does not check
scope levels as this will already have been handled by
`tokenRequiresScopes()`
- Adds migration to convert old scoped access tokens to the new set of
scopes
- Updates the user interface for scope selection
### User interface example
<img width="903" alt="Screen Shot 2023-05-31 at 1 56 55 PM"
src="https://github.com/go-gitea/gitea/assets/23248839/654766ec-2143-4f59-9037-3b51600e32f3">
<img width="917" alt="Screen Shot 2023-05-31 at 1 56 43 PM"
src="https://github.com/go-gitea/gitea/assets/23248839/1ad64081-012c-4a73-b393-66b30352654c">
## tokenRequiresScopes Design Decision
- `tokenRequiresScopes()` was added to more reliably cover api routes.
For an incoming request, this function uses the given scope category
(say `AccessTokenScopeCategoryOrganization`) and the HTTP method (say
`DELETE`) and verifies that any scoped tokens in use include
`delete:organization`.
- `reqToken()` is used to enforce auth for individual routes that
require it. If a scoped token is not present for a request,
`tokenRequiresScopes()` will not return an error
## TODO
- [x] Alphabetize scope categories
- [x] Change 'public repos only' to a radio button (private vs public).
Also expand this to organizations
- [X] Disable token creation if no scopes selected. Alternatively, show
warning
- [x] `reqToken()` is missing from many `POST/DELETE` routes in the api.
`tokenRequiresScopes()` only checks that a given token has the correct
scope, `reqToken()` must be used to check that a token (or some other
auth) is present.
- _This should be addressed in this PR_
- [x] The migration should be reviewed very carefully in order to
minimize access changes to existing user tokens.
- _This should be addressed in this PR_
- [x] Link to api to swagger documentation, clarify what
read/write/delete levels correspond to
- [x] Review cases where more than one scope is needed as this directly
deviates from the api definition.
- _This should be addressed in this PR_
- For example:
```go
m.Group("/users/{username}/orgs", func() {
m.Get("", reqToken(), org.ListUserOrgs)
m.Get("/{org}/permissions", reqToken(), org.GetUserOrgsPermissions)
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser,
auth_model.AccessTokenScopeCategoryOrganization),
context_service.UserAssignmentAPI())
```
## Future improvements
- [ ] Add required scopes to swagger documentation
- [ ] Redesign `reqToken()` to be opt-out rather than opt-in
- [ ] Subdivide scopes like `repository`
- [ ] Once a token is created, if it has no scopes, we should display
text instead of an empty bullet point
- [ ] If the 'public repos only' option is selected, should read
categories be selected by default
Closes #24501
Closes #24799
Co-authored-by: Jonathan Tran <jon@allspice.io>
Co-authored-by: Kyle D <kdumontnu@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2023-06-04 21:57:16 +03:00
token := getTokenForLoggedInUser ( t , session , auth_model . AccessTokenScopeWriteRepository , auth_model . AccessTokenScopeWriteUser )
2021-12-24 07:26:52 +03:00
repoName := "moveME"
apiRepo := new ( api . Repository )
2023-12-22 02:59:59 +03:00
req := NewRequestWithJSON ( t , "POST" , "/api/v1/user/repos" , & api . CreateRepoOption {
2021-12-24 07:26:52 +03:00
Name : repoName ,
Description : "repo move around" ,
Private : false ,
Readme : "Default" ,
AutoInit : true ,
2023-12-22 02:59:59 +03:00
} ) . AddTokenAuth ( token )
2021-12-24 07:26:52 +03:00
2022-12-02 06:39:42 +03:00
resp := MakeRequest ( t , req , http . StatusCreated )
2021-12-24 07:26:52 +03:00
DecodeJSON ( t , resp , apiRepo )
2022-08-16 05:22:25 +03:00
repo := unittest . AssertExistsAndLoadBean ( t , & repo_model . Repository { ID : apiRepo . ID } )
2023-12-22 02:59:59 +03:00
req = NewRequestWithJSON ( t , "POST" , fmt . Sprintf ( "/api/v1/repos/%s/%s/transfer" , repo . OwnerName , repo . Name ) , & api . TransferRepoOption {
2021-12-24 07:26:52 +03:00
NewOwner : "user4" ,
2023-12-22 02:59:59 +03:00
} ) . AddTokenAuth ( token )
2022-12-02 06:39:42 +03:00
MakeRequest ( t , req , http . StatusCreated )
2021-12-24 07:26:52 +03:00
return repo
}
func TestAPIAcceptTransfer ( t * testing . T ) {
2022-09-02 22:18:23 +03:00
defer tests . PrepareTestEnv ( t ) ( )
2021-12-24 07:26:52 +03:00
repo := transfer ( t )
// try to accept with not authorized user
session := loginUser ( t , "user2" )
Redesign Scoped Access Tokens (#24767)
## Changes
- Adds the following high level access scopes, each with `read` and
`write` levels:
- `activitypub`
- `admin` (hidden if user is not a site admin)
- `misc`
- `notification`
- `organization`
- `package`
- `issue`
- `repository`
- `user`
- Adds new middleware function `tokenRequiresScopes()` in addition to
`reqToken()`
- `tokenRequiresScopes()` is used for each high-level api section
- _if_ a scoped token is present, checks that the required scope is
included based on the section and HTTP method
- `reqToken()` is used for individual routes
- checks that required authentication is present (but does not check
scope levels as this will already have been handled by
`tokenRequiresScopes()`
- Adds migration to convert old scoped access tokens to the new set of
scopes
- Updates the user interface for scope selection
### User interface example
<img width="903" alt="Screen Shot 2023-05-31 at 1 56 55 PM"
src="https://github.com/go-gitea/gitea/assets/23248839/654766ec-2143-4f59-9037-3b51600e32f3">
<img width="917" alt="Screen Shot 2023-05-31 at 1 56 43 PM"
src="https://github.com/go-gitea/gitea/assets/23248839/1ad64081-012c-4a73-b393-66b30352654c">
## tokenRequiresScopes Design Decision
- `tokenRequiresScopes()` was added to more reliably cover api routes.
For an incoming request, this function uses the given scope category
(say `AccessTokenScopeCategoryOrganization`) and the HTTP method (say
`DELETE`) and verifies that any scoped tokens in use include
`delete:organization`.
- `reqToken()` is used to enforce auth for individual routes that
require it. If a scoped token is not present for a request,
`tokenRequiresScopes()` will not return an error
## TODO
- [x] Alphabetize scope categories
- [x] Change 'public repos only' to a radio button (private vs public).
Also expand this to organizations
- [X] Disable token creation if no scopes selected. Alternatively, show
warning
- [x] `reqToken()` is missing from many `POST/DELETE` routes in the api.
`tokenRequiresScopes()` only checks that a given token has the correct
scope, `reqToken()` must be used to check that a token (or some other
auth) is present.
- _This should be addressed in this PR_
- [x] The migration should be reviewed very carefully in order to
minimize access changes to existing user tokens.
- _This should be addressed in this PR_
- [x] Link to api to swagger documentation, clarify what
read/write/delete levels correspond to
- [x] Review cases where more than one scope is needed as this directly
deviates from the api definition.
- _This should be addressed in this PR_
- For example:
```go
m.Group("/users/{username}/orgs", func() {
m.Get("", reqToken(), org.ListUserOrgs)
m.Get("/{org}/permissions", reqToken(), org.GetUserOrgsPermissions)
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser,
auth_model.AccessTokenScopeCategoryOrganization),
context_service.UserAssignmentAPI())
```
## Future improvements
- [ ] Add required scopes to swagger documentation
- [ ] Redesign `reqToken()` to be opt-out rather than opt-in
- [ ] Subdivide scopes like `repository`
- [ ] Once a token is created, if it has no scopes, we should display
text instead of an empty bullet point
- [ ] If the 'public repos only' option is selected, should read
categories be selected by default
Closes #24501
Closes #24799
Co-authored-by: Jonathan Tran <jon@allspice.io>
Co-authored-by: Kyle D <kdumontnu@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2023-06-04 21:57:16 +03:00
token := getTokenForLoggedInUser ( t , session , auth_model . AccessTokenScopeWriteRepository , auth_model . AccessTokenScopeWriteUser )
2023-12-22 02:59:59 +03:00
req := NewRequest ( t , "POST" , fmt . Sprintf ( "/api/v1/repos/%s/%s/transfer/reject" , repo . OwnerName , repo . Name ) ) .
AddTokenAuth ( token )
2022-12-02 06:39:42 +03:00
MakeRequest ( t , req , http . StatusForbidden )
2021-12-24 07:26:52 +03:00
// try to accept repo that's not marked as transferred
2023-12-22 02:59:59 +03:00
req = NewRequest ( t , "POST" , fmt . Sprintf ( "/api/v1/repos/%s/%s/transfer/accept" , "user2" , "repo1" ) ) .
AddTokenAuth ( token )
2022-12-02 06:39:42 +03:00
MakeRequest ( t , req , http . StatusNotFound )
2021-12-24 07:26:52 +03:00
// accept transfer
session = loginUser ( t , "user4" )
Redesign Scoped Access Tokens (#24767)
## Changes
- Adds the following high level access scopes, each with `read` and
`write` levels:
- `activitypub`
- `admin` (hidden if user is not a site admin)
- `misc`
- `notification`
- `organization`
- `package`
- `issue`
- `repository`
- `user`
- Adds new middleware function `tokenRequiresScopes()` in addition to
`reqToken()`
- `tokenRequiresScopes()` is used for each high-level api section
- _if_ a scoped token is present, checks that the required scope is
included based on the section and HTTP method
- `reqToken()` is used for individual routes
- checks that required authentication is present (but does not check
scope levels as this will already have been handled by
`tokenRequiresScopes()`
- Adds migration to convert old scoped access tokens to the new set of
scopes
- Updates the user interface for scope selection
### User interface example
<img width="903" alt="Screen Shot 2023-05-31 at 1 56 55 PM"
src="https://github.com/go-gitea/gitea/assets/23248839/654766ec-2143-4f59-9037-3b51600e32f3">
<img width="917" alt="Screen Shot 2023-05-31 at 1 56 43 PM"
src="https://github.com/go-gitea/gitea/assets/23248839/1ad64081-012c-4a73-b393-66b30352654c">
## tokenRequiresScopes Design Decision
- `tokenRequiresScopes()` was added to more reliably cover api routes.
For an incoming request, this function uses the given scope category
(say `AccessTokenScopeCategoryOrganization`) and the HTTP method (say
`DELETE`) and verifies that any scoped tokens in use include
`delete:organization`.
- `reqToken()` is used to enforce auth for individual routes that
require it. If a scoped token is not present for a request,
`tokenRequiresScopes()` will not return an error
## TODO
- [x] Alphabetize scope categories
- [x] Change 'public repos only' to a radio button (private vs public).
Also expand this to organizations
- [X] Disable token creation if no scopes selected. Alternatively, show
warning
- [x] `reqToken()` is missing from many `POST/DELETE` routes in the api.
`tokenRequiresScopes()` only checks that a given token has the correct
scope, `reqToken()` must be used to check that a token (or some other
auth) is present.
- _This should be addressed in this PR_
- [x] The migration should be reviewed very carefully in order to
minimize access changes to existing user tokens.
- _This should be addressed in this PR_
- [x] Link to api to swagger documentation, clarify what
read/write/delete levels correspond to
- [x] Review cases where more than one scope is needed as this directly
deviates from the api definition.
- _This should be addressed in this PR_
- For example:
```go
m.Group("/users/{username}/orgs", func() {
m.Get("", reqToken(), org.ListUserOrgs)
m.Get("/{org}/permissions", reqToken(), org.GetUserOrgsPermissions)
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser,
auth_model.AccessTokenScopeCategoryOrganization),
context_service.UserAssignmentAPI())
```
## Future improvements
- [ ] Add required scopes to swagger documentation
- [ ] Redesign `reqToken()` to be opt-out rather than opt-in
- [ ] Subdivide scopes like `repository`
- [ ] Once a token is created, if it has no scopes, we should display
text instead of an empty bullet point
- [ ] If the 'public repos only' option is selected, should read
categories be selected by default
Closes #24501
Closes #24799
Co-authored-by: Jonathan Tran <jon@allspice.io>
Co-authored-by: Kyle D <kdumontnu@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2023-06-04 21:57:16 +03:00
token = getTokenForLoggedInUser ( t , session , auth_model . AccessTokenScopeWriteRepository , auth_model . AccessTokenScopeWriteUser )
2021-12-24 07:26:52 +03:00
2023-12-22 02:59:59 +03:00
req = NewRequest ( t , "POST" , fmt . Sprintf ( "/api/v1/repos/%s/%s/transfer/accept" , repo . OwnerName , repo . Name ) ) .
AddTokenAuth ( token )
2022-12-02 06:39:42 +03:00
resp := MakeRequest ( t , req , http . StatusAccepted )
2021-12-24 07:26:52 +03:00
apiRepo := new ( api . Repository )
DecodeJSON ( t , resp , apiRepo )
assert . Equal ( t , "user4" , apiRepo . Owner . UserName )
}
func TestAPIRejectTransfer ( t * testing . T ) {
2022-09-02 22:18:23 +03:00
defer tests . PrepareTestEnv ( t ) ( )
2021-12-24 07:26:52 +03:00
repo := transfer ( t )
// try to reject with not authorized user
session := loginUser ( t , "user2" )
Redesign Scoped Access Tokens (#24767)
## Changes
- Adds the following high level access scopes, each with `read` and
`write` levels:
- `activitypub`
- `admin` (hidden if user is not a site admin)
- `misc`
- `notification`
- `organization`
- `package`
- `issue`
- `repository`
- `user`
- Adds new middleware function `tokenRequiresScopes()` in addition to
`reqToken()`
- `tokenRequiresScopes()` is used for each high-level api section
- _if_ a scoped token is present, checks that the required scope is
included based on the section and HTTP method
- `reqToken()` is used for individual routes
- checks that required authentication is present (but does not check
scope levels as this will already have been handled by
`tokenRequiresScopes()`
- Adds migration to convert old scoped access tokens to the new set of
scopes
- Updates the user interface for scope selection
### User interface example
<img width="903" alt="Screen Shot 2023-05-31 at 1 56 55 PM"
src="https://github.com/go-gitea/gitea/assets/23248839/654766ec-2143-4f59-9037-3b51600e32f3">
<img width="917" alt="Screen Shot 2023-05-31 at 1 56 43 PM"
src="https://github.com/go-gitea/gitea/assets/23248839/1ad64081-012c-4a73-b393-66b30352654c">
## tokenRequiresScopes Design Decision
- `tokenRequiresScopes()` was added to more reliably cover api routes.
For an incoming request, this function uses the given scope category
(say `AccessTokenScopeCategoryOrganization`) and the HTTP method (say
`DELETE`) and verifies that any scoped tokens in use include
`delete:organization`.
- `reqToken()` is used to enforce auth for individual routes that
require it. If a scoped token is not present for a request,
`tokenRequiresScopes()` will not return an error
## TODO
- [x] Alphabetize scope categories
- [x] Change 'public repos only' to a radio button (private vs public).
Also expand this to organizations
- [X] Disable token creation if no scopes selected. Alternatively, show
warning
- [x] `reqToken()` is missing from many `POST/DELETE` routes in the api.
`tokenRequiresScopes()` only checks that a given token has the correct
scope, `reqToken()` must be used to check that a token (or some other
auth) is present.
- _This should be addressed in this PR_
- [x] The migration should be reviewed very carefully in order to
minimize access changes to existing user tokens.
- _This should be addressed in this PR_
- [x] Link to api to swagger documentation, clarify what
read/write/delete levels correspond to
- [x] Review cases where more than one scope is needed as this directly
deviates from the api definition.
- _This should be addressed in this PR_
- For example:
```go
m.Group("/users/{username}/orgs", func() {
m.Get("", reqToken(), org.ListUserOrgs)
m.Get("/{org}/permissions", reqToken(), org.GetUserOrgsPermissions)
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser,
auth_model.AccessTokenScopeCategoryOrganization),
context_service.UserAssignmentAPI())
```
## Future improvements
- [ ] Add required scopes to swagger documentation
- [ ] Redesign `reqToken()` to be opt-out rather than opt-in
- [ ] Subdivide scopes like `repository`
- [ ] Once a token is created, if it has no scopes, we should display
text instead of an empty bullet point
- [ ] If the 'public repos only' option is selected, should read
categories be selected by default
Closes #24501
Closes #24799
Co-authored-by: Jonathan Tran <jon@allspice.io>
Co-authored-by: Kyle D <kdumontnu@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2023-06-04 21:57:16 +03:00
token := getTokenForLoggedInUser ( t , session , auth_model . AccessTokenScopeWriteRepository )
2023-12-22 02:59:59 +03:00
req := NewRequest ( t , "POST" , fmt . Sprintf ( "/api/v1/repos/%s/%s/transfer/reject" , repo . OwnerName , repo . Name ) ) .
AddTokenAuth ( token )
2022-12-02 06:39:42 +03:00
MakeRequest ( t , req , http . StatusForbidden )
2021-12-24 07:26:52 +03:00
// try to reject repo that's not marked as transferred
2023-12-22 02:59:59 +03:00
req = NewRequest ( t , "POST" , fmt . Sprintf ( "/api/v1/repos/%s/%s/transfer/reject" , "user2" , "repo1" ) ) .
AddTokenAuth ( token )
2022-12-02 06:39:42 +03:00
MakeRequest ( t , req , http . StatusNotFound )
2021-12-24 07:26:52 +03:00
// reject transfer
session = loginUser ( t , "user4" )
Redesign Scoped Access Tokens (#24767)
## Changes
- Adds the following high level access scopes, each with `read` and
`write` levels:
- `activitypub`
- `admin` (hidden if user is not a site admin)
- `misc`
- `notification`
- `organization`
- `package`
- `issue`
- `repository`
- `user`
- Adds new middleware function `tokenRequiresScopes()` in addition to
`reqToken()`
- `tokenRequiresScopes()` is used for each high-level api section
- _if_ a scoped token is present, checks that the required scope is
included based on the section and HTTP method
- `reqToken()` is used for individual routes
- checks that required authentication is present (but does not check
scope levels as this will already have been handled by
`tokenRequiresScopes()`
- Adds migration to convert old scoped access tokens to the new set of
scopes
- Updates the user interface for scope selection
### User interface example
<img width="903" alt="Screen Shot 2023-05-31 at 1 56 55 PM"
src="https://github.com/go-gitea/gitea/assets/23248839/654766ec-2143-4f59-9037-3b51600e32f3">
<img width="917" alt="Screen Shot 2023-05-31 at 1 56 43 PM"
src="https://github.com/go-gitea/gitea/assets/23248839/1ad64081-012c-4a73-b393-66b30352654c">
## tokenRequiresScopes Design Decision
- `tokenRequiresScopes()` was added to more reliably cover api routes.
For an incoming request, this function uses the given scope category
(say `AccessTokenScopeCategoryOrganization`) and the HTTP method (say
`DELETE`) and verifies that any scoped tokens in use include
`delete:organization`.
- `reqToken()` is used to enforce auth for individual routes that
require it. If a scoped token is not present for a request,
`tokenRequiresScopes()` will not return an error
## TODO
- [x] Alphabetize scope categories
- [x] Change 'public repos only' to a radio button (private vs public).
Also expand this to organizations
- [X] Disable token creation if no scopes selected. Alternatively, show
warning
- [x] `reqToken()` is missing from many `POST/DELETE` routes in the api.
`tokenRequiresScopes()` only checks that a given token has the correct
scope, `reqToken()` must be used to check that a token (or some other
auth) is present.
- _This should be addressed in this PR_
- [x] The migration should be reviewed very carefully in order to
minimize access changes to existing user tokens.
- _This should be addressed in this PR_
- [x] Link to api to swagger documentation, clarify what
read/write/delete levels correspond to
- [x] Review cases where more than one scope is needed as this directly
deviates from the api definition.
- _This should be addressed in this PR_
- For example:
```go
m.Group("/users/{username}/orgs", func() {
m.Get("", reqToken(), org.ListUserOrgs)
m.Get("/{org}/permissions", reqToken(), org.GetUserOrgsPermissions)
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser,
auth_model.AccessTokenScopeCategoryOrganization),
context_service.UserAssignmentAPI())
```
## Future improvements
- [ ] Add required scopes to swagger documentation
- [ ] Redesign `reqToken()` to be opt-out rather than opt-in
- [ ] Subdivide scopes like `repository`
- [ ] Once a token is created, if it has no scopes, we should display
text instead of an empty bullet point
- [ ] If the 'public repos only' option is selected, should read
categories be selected by default
Closes #24501
Closes #24799
Co-authored-by: Jonathan Tran <jon@allspice.io>
Co-authored-by: Kyle D <kdumontnu@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2023-06-04 21:57:16 +03:00
token = getTokenForLoggedInUser ( t , session , auth_model . AccessTokenScopeWriteRepository )
2021-12-24 07:26:52 +03:00
2023-12-22 02:59:59 +03:00
req = NewRequest ( t , "POST" , fmt . Sprintf ( "/api/v1/repos/%s/%s/transfer/reject" , repo . OwnerName , repo . Name ) ) .
AddTokenAuth ( token )
2022-12-02 06:39:42 +03:00
resp := MakeRequest ( t , req , http . StatusOK )
2021-12-24 07:26:52 +03:00
apiRepo := new ( api . Repository )
DecodeJSON ( t , resp , apiRepo )
assert . Equal ( t , "user2" , apiRepo . Owner . UserName )
}
2021-07-05 18:29:08 +03:00
func TestAPIGenerateRepo ( t * testing . T ) {
2022-09-02 22:18:23 +03:00
defer tests . PrepareTestEnv ( t ) ( )
2021-07-05 18:29:08 +03:00
2022-08-16 05:22:25 +03:00
user := unittest . AssertExistsAndLoadBean ( t , & user_model . User { ID : 1 } )
2021-07-05 18:29:08 +03:00
session := loginUser ( t , user . Name )
Redesign Scoped Access Tokens (#24767)
## Changes
- Adds the following high level access scopes, each with `read` and
`write` levels:
- `activitypub`
- `admin` (hidden if user is not a site admin)
- `misc`
- `notification`
- `organization`
- `package`
- `issue`
- `repository`
- `user`
- Adds new middleware function `tokenRequiresScopes()` in addition to
`reqToken()`
- `tokenRequiresScopes()` is used for each high-level api section
- _if_ a scoped token is present, checks that the required scope is
included based on the section and HTTP method
- `reqToken()` is used for individual routes
- checks that required authentication is present (but does not check
scope levels as this will already have been handled by
`tokenRequiresScopes()`
- Adds migration to convert old scoped access tokens to the new set of
scopes
- Updates the user interface for scope selection
### User interface example
<img width="903" alt="Screen Shot 2023-05-31 at 1 56 55 PM"
src="https://github.com/go-gitea/gitea/assets/23248839/654766ec-2143-4f59-9037-3b51600e32f3">
<img width="917" alt="Screen Shot 2023-05-31 at 1 56 43 PM"
src="https://github.com/go-gitea/gitea/assets/23248839/1ad64081-012c-4a73-b393-66b30352654c">
## tokenRequiresScopes Design Decision
- `tokenRequiresScopes()` was added to more reliably cover api routes.
For an incoming request, this function uses the given scope category
(say `AccessTokenScopeCategoryOrganization`) and the HTTP method (say
`DELETE`) and verifies that any scoped tokens in use include
`delete:organization`.
- `reqToken()` is used to enforce auth for individual routes that
require it. If a scoped token is not present for a request,
`tokenRequiresScopes()` will not return an error
## TODO
- [x] Alphabetize scope categories
- [x] Change 'public repos only' to a radio button (private vs public).
Also expand this to organizations
- [X] Disable token creation if no scopes selected. Alternatively, show
warning
- [x] `reqToken()` is missing from many `POST/DELETE` routes in the api.
`tokenRequiresScopes()` only checks that a given token has the correct
scope, `reqToken()` must be used to check that a token (or some other
auth) is present.
- _This should be addressed in this PR_
- [x] The migration should be reviewed very carefully in order to
minimize access changes to existing user tokens.
- _This should be addressed in this PR_
- [x] Link to api to swagger documentation, clarify what
read/write/delete levels correspond to
- [x] Review cases where more than one scope is needed as this directly
deviates from the api definition.
- _This should be addressed in this PR_
- For example:
```go
m.Group("/users/{username}/orgs", func() {
m.Get("", reqToken(), org.ListUserOrgs)
m.Get("/{org}/permissions", reqToken(), org.GetUserOrgsPermissions)
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser,
auth_model.AccessTokenScopeCategoryOrganization),
context_service.UserAssignmentAPI())
```
## Future improvements
- [ ] Add required scopes to swagger documentation
- [ ] Redesign `reqToken()` to be opt-out rather than opt-in
- [ ] Subdivide scopes like `repository`
- [ ] Once a token is created, if it has no scopes, we should display
text instead of an empty bullet point
- [ ] If the 'public repos only' option is selected, should read
categories be selected by default
Closes #24501
Closes #24799
Co-authored-by: Jonathan Tran <jon@allspice.io>
Co-authored-by: Kyle D <kdumontnu@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2023-06-04 21:57:16 +03:00
token := getTokenForLoggedInUser ( t , session , auth_model . AccessTokenScopeWriteRepository )
2021-07-05 18:29:08 +03:00
2022-08-16 05:22:25 +03:00
templateRepo := unittest . AssertExistsAndLoadBean ( t , & repo_model . Repository { ID : 44 } )
2021-07-05 18:29:08 +03:00
// user
repo := new ( api . Repository )
2023-12-22 02:59:59 +03:00
req := NewRequestWithJSON ( t , "POST" , fmt . Sprintf ( "/api/v1/repos/%s/%s/generate" , templateRepo . OwnerName , templateRepo . Name ) , & api . GenerateRepoOption {
2021-07-05 18:29:08 +03:00
Owner : user . Name ,
Name : "new-repo" ,
Description : "test generate repo" ,
Private : false ,
GitContent : true ,
2023-12-22 02:59:59 +03:00
} ) . AddTokenAuth ( token )
2022-12-02 06:39:42 +03:00
resp := MakeRequest ( t , req , http . StatusCreated )
2021-07-05 18:29:08 +03:00
DecodeJSON ( t , resp , repo )
assert . Equal ( t , "new-repo" , repo . Name )
// org
2023-12-22 02:59:59 +03:00
req = NewRequestWithJSON ( t , "POST" , fmt . Sprintf ( "/api/v1/repos/%s/%s/generate" , templateRepo . OwnerName , templateRepo . Name ) , & api . GenerateRepoOption {
2023-09-14 05:59:53 +03:00
Owner : "org3" ,
2021-07-05 18:29:08 +03:00
Name : "new-repo" ,
Description : "test generate repo" ,
Private : false ,
GitContent : true ,
2023-12-22 02:59:59 +03:00
} ) . AddTokenAuth ( token )
2022-12-02 06:39:42 +03:00
resp = MakeRequest ( t , req , http . StatusCreated )
2021-07-05 18:29:08 +03:00
DecodeJSON ( t , resp , repo )
assert . Equal ( t , "new-repo" , repo . Name )
}
2021-06-17 17:02:34 +03:00
func TestAPIRepoGetReviewers ( t * testing . T ) {
2022-09-02 22:18:23 +03:00
defer tests . PrepareTestEnv ( t ) ( )
2022-08-16 05:22:25 +03:00
user := unittest . AssertExistsAndLoadBean ( t , & user_model . User { ID : 2 } )
2021-06-17 17:02:34 +03:00
session := loginUser ( t , user . Name )
Redesign Scoped Access Tokens (#24767)
## Changes
- Adds the following high level access scopes, each with `read` and
`write` levels:
- `activitypub`
- `admin` (hidden if user is not a site admin)
- `misc`
- `notification`
- `organization`
- `package`
- `issue`
- `repository`
- `user`
- Adds new middleware function `tokenRequiresScopes()` in addition to
`reqToken()`
- `tokenRequiresScopes()` is used for each high-level api section
- _if_ a scoped token is present, checks that the required scope is
included based on the section and HTTP method
- `reqToken()` is used for individual routes
- checks that required authentication is present (but does not check
scope levels as this will already have been handled by
`tokenRequiresScopes()`
- Adds migration to convert old scoped access tokens to the new set of
scopes
- Updates the user interface for scope selection
### User interface example
<img width="903" alt="Screen Shot 2023-05-31 at 1 56 55 PM"
src="https://github.com/go-gitea/gitea/assets/23248839/654766ec-2143-4f59-9037-3b51600e32f3">
<img width="917" alt="Screen Shot 2023-05-31 at 1 56 43 PM"
src="https://github.com/go-gitea/gitea/assets/23248839/1ad64081-012c-4a73-b393-66b30352654c">
## tokenRequiresScopes Design Decision
- `tokenRequiresScopes()` was added to more reliably cover api routes.
For an incoming request, this function uses the given scope category
(say `AccessTokenScopeCategoryOrganization`) and the HTTP method (say
`DELETE`) and verifies that any scoped tokens in use include
`delete:organization`.
- `reqToken()` is used to enforce auth for individual routes that
require it. If a scoped token is not present for a request,
`tokenRequiresScopes()` will not return an error
## TODO
- [x] Alphabetize scope categories
- [x] Change 'public repos only' to a radio button (private vs public).
Also expand this to organizations
- [X] Disable token creation if no scopes selected. Alternatively, show
warning
- [x] `reqToken()` is missing from many `POST/DELETE` routes in the api.
`tokenRequiresScopes()` only checks that a given token has the correct
scope, `reqToken()` must be used to check that a token (or some other
auth) is present.
- _This should be addressed in this PR_
- [x] The migration should be reviewed very carefully in order to
minimize access changes to existing user tokens.
- _This should be addressed in this PR_
- [x] Link to api to swagger documentation, clarify what
read/write/delete levels correspond to
- [x] Review cases where more than one scope is needed as this directly
deviates from the api definition.
- _This should be addressed in this PR_
- For example:
```go
m.Group("/users/{username}/orgs", func() {
m.Get("", reqToken(), org.ListUserOrgs)
m.Get("/{org}/permissions", reqToken(), org.GetUserOrgsPermissions)
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser,
auth_model.AccessTokenScopeCategoryOrganization),
context_service.UserAssignmentAPI())
```
## Future improvements
- [ ] Add required scopes to swagger documentation
- [ ] Redesign `reqToken()` to be opt-out rather than opt-in
- [ ] Subdivide scopes like `repository`
- [ ] Once a token is created, if it has no scopes, we should display
text instead of an empty bullet point
- [ ] If the 'public repos only' option is selected, should read
categories be selected by default
Closes #24501
Closes #24799
Co-authored-by: Jonathan Tran <jon@allspice.io>
Co-authored-by: Kyle D <kdumontnu@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2023-06-04 21:57:16 +03:00
token := getTokenForLoggedInUser ( t , session , auth_model . AccessTokenScopeReadRepository )
2022-08-16 05:22:25 +03:00
repo := unittest . AssertExistsAndLoadBean ( t , & repo_model . Repository { ID : 1 } )
2021-06-17 17:02:34 +03:00
2023-12-22 02:59:59 +03:00
req := NewRequestf ( t , "GET" , "/api/v1/repos/%s/%s/reviewers" , user . Name , repo . Name ) .
AddTokenAuth ( token )
2022-12-02 06:39:42 +03:00
resp := MakeRequest ( t , req , http . StatusOK )
2021-06-17 17:02:34 +03:00
var reviewers [ ] * api . User
DecodeJSON ( t , resp , & reviewers )
assert . Len ( t , reviewers , 4 )
}
func TestAPIRepoGetAssignees ( t * testing . T ) {
2022-09-02 22:18:23 +03:00
defer tests . PrepareTestEnv ( t ) ( )
2022-08-16 05:22:25 +03:00
user := unittest . AssertExistsAndLoadBean ( t , & user_model . User { ID : 2 } )
2021-06-17 17:02:34 +03:00
session := loginUser ( t , user . Name )
Redesign Scoped Access Tokens (#24767)
## Changes
- Adds the following high level access scopes, each with `read` and
`write` levels:
- `activitypub`
- `admin` (hidden if user is not a site admin)
- `misc`
- `notification`
- `organization`
- `package`
- `issue`
- `repository`
- `user`
- Adds new middleware function `tokenRequiresScopes()` in addition to
`reqToken()`
- `tokenRequiresScopes()` is used for each high-level api section
- _if_ a scoped token is present, checks that the required scope is
included based on the section and HTTP method
- `reqToken()` is used for individual routes
- checks that required authentication is present (but does not check
scope levels as this will already have been handled by
`tokenRequiresScopes()`
- Adds migration to convert old scoped access tokens to the new set of
scopes
- Updates the user interface for scope selection
### User interface example
<img width="903" alt="Screen Shot 2023-05-31 at 1 56 55 PM"
src="https://github.com/go-gitea/gitea/assets/23248839/654766ec-2143-4f59-9037-3b51600e32f3">
<img width="917" alt="Screen Shot 2023-05-31 at 1 56 43 PM"
src="https://github.com/go-gitea/gitea/assets/23248839/1ad64081-012c-4a73-b393-66b30352654c">
## tokenRequiresScopes Design Decision
- `tokenRequiresScopes()` was added to more reliably cover api routes.
For an incoming request, this function uses the given scope category
(say `AccessTokenScopeCategoryOrganization`) and the HTTP method (say
`DELETE`) and verifies that any scoped tokens in use include
`delete:organization`.
- `reqToken()` is used to enforce auth for individual routes that
require it. If a scoped token is not present for a request,
`tokenRequiresScopes()` will not return an error
## TODO
- [x] Alphabetize scope categories
- [x] Change 'public repos only' to a radio button (private vs public).
Also expand this to organizations
- [X] Disable token creation if no scopes selected. Alternatively, show
warning
- [x] `reqToken()` is missing from many `POST/DELETE` routes in the api.
`tokenRequiresScopes()` only checks that a given token has the correct
scope, `reqToken()` must be used to check that a token (or some other
auth) is present.
- _This should be addressed in this PR_
- [x] The migration should be reviewed very carefully in order to
minimize access changes to existing user tokens.
- _This should be addressed in this PR_
- [x] Link to api to swagger documentation, clarify what
read/write/delete levels correspond to
- [x] Review cases where more than one scope is needed as this directly
deviates from the api definition.
- _This should be addressed in this PR_
- For example:
```go
m.Group("/users/{username}/orgs", func() {
m.Get("", reqToken(), org.ListUserOrgs)
m.Get("/{org}/permissions", reqToken(), org.GetUserOrgsPermissions)
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser,
auth_model.AccessTokenScopeCategoryOrganization),
context_service.UserAssignmentAPI())
```
## Future improvements
- [ ] Add required scopes to swagger documentation
- [ ] Redesign `reqToken()` to be opt-out rather than opt-in
- [ ] Subdivide scopes like `repository`
- [ ] Once a token is created, if it has no scopes, we should display
text instead of an empty bullet point
- [ ] If the 'public repos only' option is selected, should read
categories be selected by default
Closes #24501
Closes #24799
Co-authored-by: Jonathan Tran <jon@allspice.io>
Co-authored-by: Kyle D <kdumontnu@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2023-06-04 21:57:16 +03:00
token := getTokenForLoggedInUser ( t , session , auth_model . AccessTokenScopeReadRepository )
2022-08-16 05:22:25 +03:00
repo := unittest . AssertExistsAndLoadBean ( t , & repo_model . Repository { ID : 1 } )
2021-06-17 17:02:34 +03:00
2023-12-22 02:59:59 +03:00
req := NewRequestf ( t , "GET" , "/api/v1/repos/%s/%s/assignees" , user . Name , repo . Name ) .
AddTokenAuth ( token )
2022-12-02 06:39:42 +03:00
resp := MakeRequest ( t , req , http . StatusOK )
2021-06-17 17:02:34 +03:00
var assignees [ ] * api . User
DecodeJSON ( t , resp , & assignees )
assert . Len ( t , assignees , 1 )
}