mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2026-05-13 06:20:24 +00:00
feat: implement fine-grained access tokens on /users/{username}/repos & /orgs/{org}/repos
**Breaking**: when using a public-only access tokens, private
repositories were not filtered out by the `/users/{username}/repos` or
`/orgs/{org}/repos` APIs. This access has been removed in this change.
This commit is contained in:
parent
a309db27f2
commit
cac675bc21
2 changed files with 81 additions and 6 deletions
|
|
@ -20,10 +20,11 @@ func listUserRepos(ctx *context.APIContext, u *user_model.User, private bool) {
|
|||
opts := utils.GetListOptions(ctx)
|
||||
|
||||
repos, count, err := repo_model.GetUserRepositories(ctx, &repo_model.SearchRepoOptions{
|
||||
Actor: u,
|
||||
Private: private,
|
||||
ListOptions: opts,
|
||||
OrderBy: "id ASC",
|
||||
Actor: u,
|
||||
Private: private,
|
||||
ListOptions: opts,
|
||||
OrderBy: "id ASC",
|
||||
AuthorizationReducer: ctx.Reducer,
|
||||
})
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetUserRepositories", err)
|
||||
|
|
@ -37,9 +38,9 @@ func listUserRepos(ctx *context.APIContext, u *user_model.User, private bool) {
|
|||
|
||||
apiRepos := make([]*api.Repository, 0, len(repos))
|
||||
for i := range repos {
|
||||
permission, err := access_model.GetUserRepoPermission(ctx, repos[i], ctx.Doer)
|
||||
permission, err := access_model.GetUserRepoPermissionWithReducer(ctx, repos[i], ctx.Doer, ctx.Reducer)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err)
|
||||
ctx.Error(http.StatusInternalServerError, "GetUserRepoPermissionWithReducer", err)
|
||||
return
|
||||
}
|
||||
if ctx.IsSigned && ctx.Doer.IsAdmin || permission.HasAccess() {
|
||||
|
|
|
|||
|
|
@ -55,6 +55,80 @@ func TestAPIUserReposWithWrongToken(t *testing.T) {
|
|||
assert.Contains(t, resp.Body.String(), "access token does not exist")
|
||||
}
|
||||
|
||||
func TestAPIUserReposAccessTokenResources(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
|
||||
var repos []api.Repository
|
||||
|
||||
// Test cases repo1 (public), repo2 (private), repo16 (private).
|
||||
session := loginUser(t, "user2")
|
||||
|
||||
find := func() (bool, bool, bool) {
|
||||
foundRepo1 := false // public user2/repo1
|
||||
foundRepo2 := false // private user2/repo2
|
||||
foundRepo16 := false // second private repo user2/repo16 used in fine-grain testing, included as baseline
|
||||
for _, repo := range repos {
|
||||
switch repo.Name {
|
||||
case "repo1":
|
||||
foundRepo1 = true
|
||||
case "repo2":
|
||||
foundRepo2 = true
|
||||
case "repo16":
|
||||
foundRepo16 = true
|
||||
}
|
||||
}
|
||||
return foundRepo1, foundRepo2, foundRepo16
|
||||
}
|
||||
|
||||
t.Run("all access token", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
allToken := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadUser, auth_model.AccessTokenScopeReadRepository)
|
||||
|
||||
req := NewRequest(t, "GET", "/api/v1/users/user2/repos").AddTokenAuth(allToken)
|
||||
resp := MakeRequest(t, req, http.StatusOK)
|
||||
DecodeJSON(t, resp, &repos)
|
||||
foundRepo1, foundRepo2, foundRepo16 := find()
|
||||
|
||||
assert.True(t, foundRepo1) // public user2/repo1
|
||||
assert.True(t, foundRepo2) // private user2/repo2
|
||||
assert.True(t, foundRepo16) // private user2/repo16, used in fine-grain testing, included as baseline
|
||||
})
|
||||
|
||||
t.Run("public-only access token", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
publicOnlyToken := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopePublicOnly, auth_model.AccessTokenScopeReadUser, auth_model.AccessTokenScopeReadRepository)
|
||||
|
||||
req := NewRequest(t, "GET", "/api/v1/users/user2/repos").AddTokenAuth(publicOnlyToken)
|
||||
resp := MakeRequest(t, req, http.StatusOK)
|
||||
DecodeJSON(t, resp, &repos)
|
||||
foundRepo1, foundRepo2, foundRepo16 := find()
|
||||
|
||||
assert.True(t, foundRepo1) // public user2/repo1
|
||||
assert.False(t, foundRepo2) // private user2/repo2
|
||||
assert.False(t, foundRepo16) // private user2/repo16
|
||||
})
|
||||
|
||||
t.Run("specific repo access token", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
repo2OnlyToken := createFineGrainedRepoAccessToken(t, "user2",
|
||||
[]auth_model.AccessTokenScope{auth_model.AccessTokenScopeReadUser, auth_model.AccessTokenScopeReadRepository},
|
||||
[]int64{2},
|
||||
)
|
||||
|
||||
req := NewRequest(t, "GET", "/api/v1/users/user2/repos").AddTokenAuth(repo2OnlyToken)
|
||||
resp := MakeRequest(t, req, http.StatusOK)
|
||||
DecodeJSON(t, resp, &repos)
|
||||
foundRepo1, foundRepo2, foundRepo16 := find()
|
||||
|
||||
assert.True(t, foundRepo1) // public user2/repo1, allowed as it's public and read-access only
|
||||
assert.True(t, foundRepo2) // private user2/repo2, allowed inside fine-grain
|
||||
assert.False(t, foundRepo16) // private user2/repo16, denied outside fine-grain
|
||||
})
|
||||
}
|
||||
|
||||
func TestAPISearchRepo(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
const keyword = "test"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue