From 900306e65a5f04965aa1feab13d31bd141ecb120 Mon Sep 17 00:00:00 2001 From: Mathieu Fenniak Date: Sun, 26 Apr 2026 23:54:41 +0200 Subject: [PATCH] feat: add repo-specific & public-only authz reducers to authorized integrations (#12267) Built on #12266; one commit added. Adds the ability to reduce the authorization scope of an authorized integration to public-only resources and repo-specific resources. Backend only -- no frontend created yet. ## Checklist The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. All work and communication must conform to Forgejo's [AI Agreement](https://codeberg.org/forgejo/governance/src/branch/main/AIAgreement.md). There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org). ### Tests for Go changes - I added test coverage for Go changes... - [ ] in their respective `*_test.go` for unit tests. - [x] in the `tests/integration` directory if it involves interactions with a live Forgejo server. - I ran... - [x] `make pr-go` before pushing ### Documentation - [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change. - [x] I did not document these changes and I do not expect someone else to do it. ### Release notes - [ ] This change will be noticed by a Forgejo user or admin (feature, bug fix, performance, etc.). I suggest to include a release note for this change. - [x] This change is not visible to a Forgejo user or admin (refactor, dependency upgrade, etc.). I think there is no need to add a release note for this change. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/12267 Reviewed-by: Andreas Ahlenstorf --- .../authorized_integ_resource_repo.yml | 9 +++ .../authorized_integration.yml | 9 +++ models/auth/access_token_resource.go | 4 + .../authorized_integration_resource_repo.go | 17 +++++ ...thorized_integration_resource_repo_test.go | 40 ++++++++++ .../auth_result_authorized_integration.go | 10 ++- .../auth/method/authorized_integration.go | 12 ++- .../method/authorized_integration_test.go | 2 +- .../authorized_integ_resource_repo.yml | 5 ++ .../authorized_integration.yml | 35 +++++++++ services/authz/access_token.go | 7 +- services/authz/access_token_test.go | 2 +- services/authz/authorized_integration.go | 33 +++++++++ services/authz/authorized_integration_test.go | 46 ++++++++++++ services/authz/specific_repos_reducer.go | 11 ++- services/authz/specific_repos_reducer_test.go | 6 +- .../auth_authorized_integration_test.go | 73 ++++++++++++++++++- tests/integration/integration_test.go | 1 + 18 files changed, 305 insertions(+), 17 deletions(-) create mode 100644 models/auth/TestGetRepositoriesAccessibleWithIntegration/authorized_integ_resource_repo.yml create mode 100644 models/auth/TestGetRepositoriesAccessibleWithIntegration/authorized_integration.yml create mode 100644 models/auth/authorized_integration_resource_repo_test.go create mode 100644 services/authz/TestGetAuthorizationReducerForAuthorizedIntegration/authorized_integ_resource_repo.yml create mode 100644 services/authz/TestGetAuthorizationReducerForAuthorizedIntegration/authorized_integration.yml create mode 100644 services/authz/authorized_integration.go create mode 100644 services/authz/authorized_integration_test.go diff --git a/models/auth/TestGetRepositoriesAccessibleWithIntegration/authorized_integ_resource_repo.yml b/models/auth/TestGetRepositoriesAccessibleWithIntegration/authorized_integ_resource_repo.yml new file mode 100644 index 0000000000..5e0ccf1130 --- /dev/null +++ b/models/auth/TestGetRepositoriesAccessibleWithIntegration/authorized_integ_resource_repo.yml @@ -0,0 +1,9 @@ +- integ_id: 1 + repo_id: 1 + created_unix: 1772158384 +- integ_id: 1 + repo_id: 2 + created_unix: 1772158384 +- integ_id: 1 + repo_id: 3 + created_unix: 1772158384 diff --git a/models/auth/TestGetRepositoriesAccessibleWithIntegration/authorized_integration.yml b/models/auth/TestGetRepositoriesAccessibleWithIntegration/authorized_integration.yml new file mode 100644 index 0000000000..18e5e68054 --- /dev/null +++ b/models/auth/TestGetRepositoriesAccessibleWithIntegration/authorized_integration.yml @@ -0,0 +1,9 @@ +- id: 1 + user_id: 2 + scope: all + resource_all_repos: false + issuer: https://example.org/ + audience: https://forgejo.example.org/-/user/integration/abcdef123 + claim_rules: "{}" + created_unix: 1777153359 + updated_unix: 1777153359 diff --git a/models/auth/access_token_resource.go b/models/auth/access_token_resource.go index bf98c52b69..85978dd131 100644 --- a/models/auth/access_token_resource.go +++ b/models/auth/access_token_resource.go @@ -24,6 +24,10 @@ func init() { db.RegisterModel(new(AccessTokenResourceRepo)) } +func (atr *AccessTokenResourceRepo) GetTargetRepoID() int64 { + return atr.RepoID +} + func GetRepositoriesAccessibleWithToken(ctx context.Context, accessTokenID int64) ([]*AccessTokenResourceRepo, error) { var resources []*AccessTokenResourceRepo err := db.GetEngine(ctx). diff --git a/models/auth/authorized_integration_resource_repo.go b/models/auth/authorized_integration_resource_repo.go index 98960b5c8a..9c48d16f98 100644 --- a/models/auth/authorized_integration_resource_repo.go +++ b/models/auth/authorized_integration_resource_repo.go @@ -4,6 +4,8 @@ package auth import ( + "context" + "forgejo.org/models/db" "forgejo.org/modules/timeutil" ) @@ -25,3 +27,18 @@ type AuthorizedIntegResourceRepo struct { func init() { db.RegisterModel(new(AuthorizedIntegResourceRepo)) } + +func (air *AuthorizedIntegResourceRepo) GetTargetRepoID() int64 { + return air.RepoID +} + +func GetRepositoriesAccessibleWithIntegration(ctx context.Context, aiID int64) ([]*AuthorizedIntegResourceRepo, error) { + var resources []*AuthorizedIntegResourceRepo + err := db.GetEngine(ctx). + Where("integ_id = ?", aiID). + Find(&resources) + if err != nil { + return nil, err + } + return resources, nil +} diff --git a/models/auth/authorized_integration_resource_repo_test.go b/models/auth/authorized_integration_resource_repo_test.go new file mode 100644 index 0000000000..853cddb828 --- /dev/null +++ b/models/auth/authorized_integration_resource_repo_test.go @@ -0,0 +1,40 @@ +// Copyright 2026 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package auth_test + +import ( + "testing" + + auth_model "forgejo.org/models/auth" + "forgejo.org/models/unittest" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestGetRepositoriesAccessibleWithIntegration(t *testing.T) { + defer unittest.OverrideFixtures("models/auth/TestGetRepositoriesAccessibleWithIntegration")() + require.NoError(t, unittest.PrepareTestDatabase()) + + t.Run("No Resources", func(t *testing.T) { + resources, err := auth_model.GetRepositoriesAccessibleWithIntegration(t.Context(), 999) + require.NoError(t, err) + assert.Empty(t, resources) + }) + + t.Run("Has Resources", func(t *testing.T) { + resources, err := auth_model.GetRepositoriesAccessibleWithIntegration(t.Context(), 1) + require.NoError(t, err) + require.Len(t, resources, 3) + + // Verify all expected repo IDs are present + repoIDs := make([]int64, len(resources)) + for i, res := range resources { + repoIDs[i] = res.RepoID + } + assert.Contains(t, repoIDs, int64(1)) + assert.Contains(t, repoIDs, int64(2)) + assert.Contains(t, repoIDs, int64(3)) + }) +} diff --git a/services/auth/method/auth_result_authorized_integration.go b/services/auth/method/auth_result_authorized_integration.go index 6868b4848f..55efa95d18 100644 --- a/services/auth/method/auth_result_authorized_integration.go +++ b/services/auth/method/auth_result_authorized_integration.go @@ -8,14 +8,16 @@ import ( user_model "forgejo.org/models/user" "forgejo.org/modules/optional" "forgejo.org/services/auth" + "forgejo.org/services/authz" ) var _ auth.AuthenticationResult = &authorizedIntegrationAuthenticationResult{} type authorizedIntegrationAuthenticationResult struct { *auth.BaseAuthenticationResult - user *user_model.User - scope auth_model.AccessTokenScope + user *user_model.User + scope auth_model.AccessTokenScope + reducer authz.AuthorizationReducer } func (r *authorizedIntegrationAuthenticationResult) User() *user_model.User { @@ -25,3 +27,7 @@ func (r *authorizedIntegrationAuthenticationResult) User() *user_model.User { func (r *authorizedIntegrationAuthenticationResult) Scope() optional.Option[auth_model.AccessTokenScope] { return optional.Some(r.scope) } + +func (r *authorizedIntegrationAuthenticationResult) Reducer() authz.AuthorizationReducer { + return r.reducer +} diff --git a/services/auth/method/authorized_integration.go b/services/auth/method/authorized_integration.go index 60e210ee1c..000383d8c5 100644 --- a/services/auth/method/authorized_integration.go +++ b/services/auth/method/authorized_integration.go @@ -23,6 +23,7 @@ import ( "forgejo.org/modules/setting" "forgejo.org/modules/util" "forgejo.org/services/auth" + "forgejo.org/services/authz" "github.com/gobwas/glob" "github.com/golang-jwt/jwt/v5" @@ -204,11 +205,16 @@ func (a *AuthorizedIntegration) Verify(req *http.Request, w http.ResponseWriter, log.Error("UpdateLastUsed: %v", err) } + reducer, err := authz.GetAuthorizationReducerForAuthorizedIntegration(req.Context(), authorizedIntegration) + if err != nil { + return &auth.AuthenticationError{Error: fmt.Errorf("authorized integration GetAuthorizationReducerForAuthorizedIntegration: %w", err)} + } + return &auth.AuthenticationSuccess{ Result: &authorizedIntegrationAuthenticationResult{ - user: u, - scope: authorizedIntegration.Scope, - // TODO: add repo-specific access with an authz reducer + user: u, + scope: authorizedIntegration.Scope, + reducer: reducer, }, } } diff --git a/services/auth/method/authorized_integration_test.go b/services/auth/method/authorized_integration_test.go index 46048573ad..f2afbd3896 100644 --- a/services/auth/method/authorized_integration_test.go +++ b/services/auth/method/authorized_integration_test.go @@ -250,7 +250,7 @@ func TestAuthorizedIntegration(t *testing.T) { hasScope, scope := res.Scope().Get() assert.True(t, hasScope) assert.Equal(t, auth_model.AccessTokenScopeAll, scope) - assert.Nil(t, res.Reducer()) + assert.NotNil(t, res.Reducer()) }) t.Run("valid Basic JWT", func(t *testing.T) { diff --git a/services/authz/TestGetAuthorizationReducerForAuthorizedIntegration/authorized_integ_resource_repo.yml b/services/authz/TestGetAuthorizationReducerForAuthorizedIntegration/authorized_integ_resource_repo.yml new file mode 100644 index 0000000000..d5d55d1d67 --- /dev/null +++ b/services/authz/TestGetAuthorizationReducerForAuthorizedIntegration/authorized_integ_resource_repo.yml @@ -0,0 +1,5 @@ +- + id: 1 + integ_id: 7 + repo_id: 1 + created_unix: 1772158384 diff --git a/services/authz/TestGetAuthorizationReducerForAuthorizedIntegration/authorized_integration.yml b/services/authz/TestGetAuthorizationReducerForAuthorizedIntegration/authorized_integration.yml new file mode 100644 index 0000000000..7c173e5ea6 --- /dev/null +++ b/services/authz/TestGetAuthorizationReducerForAuthorizedIntegration/authorized_integration.yml @@ -0,0 +1,35 @@ +# all access +- + id: 5 + user_id: 2 + scope: all + resource_all_repos: true + issuer: https://example.org/ + audience: https://forgejo.example.org/-/user/integration/abcdef123 + claim_rules: "{}" + created_unix: 1777153359 + updated_unix: 1777153359 + +# public-only +- + id: 6 + user_id: 2 + scope: public-only,read:activitypub + resource_all_repos: true + issuer: https://example.org/ + audience: https://forgejo.example.org/-/user/integration/abcdef1234 + claim_rules: "{}" + created_unix: 1777153359 + updated_unix: 1777153359 + +# repo-specific +- + id: 7 + user_id: 2 + scope: all + resource_all_repos: false + issuer: https://example.org/ + audience: https://forgejo.example.org/-/user/integration/abcdef1235 + claim_rules: "{}" + created_unix: 1777153359 + updated_unix: 1777153359 diff --git a/services/authz/access_token.go b/services/authz/access_token.go index 207fdd6546..1660881dab 100644 --- a/services/authz/access_token.go +++ b/services/authz/access_token.go @@ -25,7 +25,12 @@ func GetAuthorizationReducerForAccessToken(ctx context.Context, token *auth_mode if err != nil { return nil, fmt.Errorf("GetRepositoriesAccessibleWithToken: %w", err) } - return &SpecificReposAuthorizationReducer{resourceRepos: repos}, nil + // Cast slice into []RepoGetter + iface := make([]RepoGetter, len(repos)) + for i, r := range repos { + iface[i] = r + } + return &SpecificReposAuthorizationReducer{resourceRepos: iface}, nil } // A locale lookup string for the error -- eg. `access_token.error.invalid_something` diff --git a/services/authz/access_token_test.go b/services/authz/access_token_test.go index 8219e2a057..2e4d9e4453 100644 --- a/services/authz/access_token_test.go +++ b/services/authz/access_token_test.go @@ -42,7 +42,7 @@ func TestGetAuthorizationReducerForAccessToken(t *testing.T) { require.NotNil(t, specific) require.Len(t, specific.resourceRepos, 1) - assert.EqualValues(t, 1, specific.resourceRepos[0].RepoID) + assert.EqualValues(t, 1, specific.resourceRepos[0].GetTargetRepoID()) }) } diff --git a/services/authz/authorized_integration.go b/services/authz/authorized_integration.go new file mode 100644 index 0000000000..c463c0aeca --- /dev/null +++ b/services/authz/authorized_integration.go @@ -0,0 +1,33 @@ +// Copyright 2026 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package authz + +import ( + "context" + "fmt" + + auth_model "forgejo.org/models/auth" +) + +func GetAuthorizationReducerForAuthorizedIntegration(ctx context.Context, ai *auth_model.AuthorizedIntegration) (AuthorizationReducer, error) { + if ai.ResourceAllRepos { + if publicOnly, err := ai.Scope.PublicOnly(); err != nil { + return nil, fmt.Errorf("PublicOnly: %w", err) + } else if publicOnly { + return &PublicReposAuthorizationReducer{}, nil + } + return &AllAccessAuthorizationReducer{}, nil + } + + repos, err := auth_model.GetRepositoriesAccessibleWithIntegration(ctx, ai.ID) + if err != nil { + return nil, fmt.Errorf("GetRepositoriesAccessibleWithIntegration: %w", err) + } + // Cast slice into []RepoGetter + iface := make([]RepoGetter, len(repos)) + for i, r := range repos { + iface[i] = r + } + return &SpecificReposAuthorizationReducer{resourceRepos: iface}, nil +} diff --git a/services/authz/authorized_integration_test.go b/services/authz/authorized_integration_test.go new file mode 100644 index 0000000000..fe3ec697a4 --- /dev/null +++ b/services/authz/authorized_integration_test.go @@ -0,0 +1,46 @@ +// Copyright 2026 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package authz + +import ( + "testing" + + "forgejo.org/models/auth" + "forgejo.org/models/unittest" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestGetAuthorizationReducerForAuthorizedIntegration(t *testing.T) { + defer unittest.OverrideFixtures("services/authz/TestGetAuthorizationReducerForAuthorizedIntegration")() + require.NoError(t, unittest.PrepareTestDatabase()) + + t.Run("all access", func(t *testing.T) { + token := unittest.AssertExistsAndLoadBean(t, &auth.AuthorizedIntegration{ID: 5}) + reducer, err := GetAuthorizationReducerForAuthorizedIntegration(t.Context(), token) + require.NoError(t, err) + assert.IsType(t, &AllAccessAuthorizationReducer{}, reducer) + }) + + t.Run("public resources only", func(t *testing.T) { + token := unittest.AssertExistsAndLoadBean(t, &auth.AuthorizedIntegration{ID: 6}) + reducer, err := GetAuthorizationReducerForAuthorizedIntegration(t.Context(), token) + require.NoError(t, err) + assert.IsType(t, &PublicReposAuthorizationReducer{}, reducer) + }) + + t.Run("specific repos only", func(t *testing.T) { + token := unittest.AssertExistsAndLoadBean(t, &auth.AuthorizedIntegration{ID: 7}) + reducer, err := GetAuthorizationReducerForAuthorizedIntegration(t.Context(), token) + require.NoError(t, err) + + specific, ok := reducer.(*SpecificReposAuthorizationReducer) + require.True(t, ok) + require.NotNil(t, specific) + + require.Len(t, specific.resourceRepos, 1) + assert.EqualValues(t, 1, specific.resourceRepos[0].GetTargetRepoID()) + }) +} diff --git a/services/authz/specific_repos_reducer.go b/services/authz/specific_repos_reducer.go index dd085189af..b4e97f2a6b 100644 --- a/services/authz/specific_repos_reducer.go +++ b/services/authz/specific_repos_reducer.go @@ -7,7 +7,6 @@ import ( "context" "fmt" - auth_model "forgejo.org/models/auth" "forgejo.org/models/perm" repo_model "forgejo.org/models/repo" "forgejo.org/modules/structs" @@ -19,12 +18,16 @@ import ( // repositories that aren't listed among the specific repos, read-only access is permitted. For all other repos, no // access is permitted. type SpecificReposAuthorizationReducer struct { - resourceRepos []*auth_model.AccessTokenResourceRepo + resourceRepos []RepoGetter +} + +type RepoGetter interface { + GetTargetRepoID() int64 } func (r *SpecificReposAuthorizationReducer) ReduceRepoAccess(ctx context.Context, repo *repo_model.Repository, accessMode perm.AccessMode) (perm.AccessMode, error) { for _, tokenRepo := range r.resourceRepos { - if tokenRepo.RepoID == repo.ID { + if tokenRepo.GetTargetRepoID() == repo.ID { // No restrictions as this repo is within the scope of the access token. return accessMode, nil } @@ -47,7 +50,7 @@ func (r *SpecificReposAuthorizationReducer) ReduceRepoAccess(ctx context.Context func (r *SpecificReposAuthorizationReducer) RepoReadAccessFilter() builder.Cond { repoIDs := make([]int64, len(r.resourceRepos)) for i, tokenRepo := range r.resourceRepos { - repoIDs[i] = tokenRepo.RepoID + repoIDs[i] = tokenRepo.GetTargetRepoID() } targetRepos := builder.In("repository.id", repoIDs) diff --git a/services/authz/specific_repos_reducer_test.go b/services/authz/specific_repos_reducer_test.go index 5db65eeca4..a7bd1545b0 100644 --- a/services/authz/specific_repos_reducer_test.go +++ b/services/authz/specific_repos_reducer_test.go @@ -20,11 +20,11 @@ func TestSpecificReposAuthorizationReducer(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) reducer := &SpecificReposAuthorizationReducer{ - resourceRepos: []*auth.AccessTokenResourceRepo{ - { + resourceRepos: []RepoGetter{ + &auth.AccessTokenResourceRepo{ RepoID: 1, }, - { + &auth.AccessTokenResourceRepo{ RepoID: 2, }, }, diff --git a/tests/integration/auth_authorized_integration_test.go b/tests/integration/auth_authorized_integration_test.go index 36fefe7cf9..668abc2630 100644 --- a/tests/integration/auth_authorized_integration_test.go +++ b/tests/integration/auth_authorized_integration_test.go @@ -4,18 +4,23 @@ package integration import ( + "fmt" "net/http" "testing" auth_model "forgejo.org/models/auth" + "forgejo.org/models/db" api "forgejo.org/modules/structs" "forgejo.org/tests" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestAPIAuthWithAuthorizedIntegration(t *testing.T) { - t.Run("all access token", func(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + t.Run("all access authorized integration", func(t *testing.T) { defer tests.PrintCurrentTest(t)() ait := newAITester(t) @@ -32,7 +37,7 @@ func TestAPIAuthWithAuthorizedIntegration(t *testing.T) { MakeRequest(t, req, http.StatusOK) }) - t.Run("scope-limited access token", func(t *testing.T) { + t.Run("scope-limited authorized integration", func(t *testing.T) { defer tests.PrintCurrentTest(t)() ait := newAITester(t, func(ai *auth_model.AuthorizedIntegration) { @@ -50,4 +55,68 @@ func TestAPIAuthWithAuthorizedIntegration(t *testing.T) { req = NewRequest(t, "GET", "/api/v1/repos/user2/repo1").AddTokenAuth(token) MakeRequest(t, req, http.StatusForbidden) }) + + // Clone of TestAPICompareCommitsAccessTokenResources, but using an authorized integration rather than an access + // token, to test its application of an authorization reducer for public-only and repo-specific access. Any API test + // that uses repo-specific tokens could serve as a test here; this one is just relatively simple and succinct for + // the number of things being tested. + t.Run("authorization reducer", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + // Using the compare API, will be testing that the base repo's security checks implement fine-grained access + // controls (and baselines with all and public-only). + testCase := func(t *testing.T, repo, token string, expectedStatus int) { + req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/compare/master...master", repo)).AddTokenAuth(token) + MakeRequest(t, req, expectedStatus) + } + + t.Run("all access token", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + ait := newAITester(t, func(ai *auth_model.AuthorizedIntegration) { + ai.Scope = auth_model.AccessTokenScopeReadRepository + }) + defer ait.close() + allToken := ait.signedJWT() + + testCase(t, "user2/repo1", allToken, http.StatusOK) // public user2/repo1 + testCase(t, "org3/repo3", allToken, http.StatusOK) // private org3/repo3 + testCase(t, "user2/repo20", allToken, http.StatusOK) // private user2/repo20 + }) + + t.Run("public-only access token", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + ait := newAITester(t, func(ai *auth_model.AuthorizedIntegration) { + ai.Scope = auth_model.AccessTokenScope(fmt.Sprintf("%s,%s", auth_model.AccessTokenScopePublicOnly, auth_model.AccessTokenScopeReadRepository)) + }) + defer ait.close() + publicOnlyToken := ait.signedJWT() + + testCase(t, "user2/repo1", publicOnlyToken, http.StatusOK) // public user2/repo1 + testCase(t, "org3/repo3", publicOnlyToken, http.StatusNotFound) // private org3/repo3 + testCase(t, "user2/repo20", publicOnlyToken, http.StatusNotFound) // private user2/repo20 + }) + + t.Run("specific repo access token", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + ait := newAITester(t, func(ai *auth_model.AuthorizedIntegration) { + ai.Scope = auth_model.AccessTokenScopeReadRepository + ai.ResourceAllRepos = false + }) + defer ait.close() + + _, err := db.GetEngine(t.Context()).Insert(&auth_model.AuthorizedIntegResourceRepo{ + IntegID: ait.authorizedIntegration.ID, + RepoID: 3, + }) + require.NoError(t, err) + repo3OnlyToken := ait.signedJWT() + + testCase(t, "user2/repo1", repo3OnlyToken, http.StatusOK) // public user2/repo1 + testCase(t, "org3/repo3", repo3OnlyToken, http.StatusOK) // private org3/repo3 + testCase(t, "user2/repo20", repo3OnlyToken, http.StatusNotFound) // private user2/repo20, outside of fine-grain + }) + }) } diff --git a/tests/integration/integration_test.go b/tests/integration/integration_test.go index ecd7d86073..c8bbaa9b2c 100644 --- a/tests/integration/integration_test.go +++ b/tests/integration/integration_test.go @@ -850,6 +850,7 @@ func newAITester(t *testing.T, setupAI ...func(*auth.AuthorizedIntegration)) *Au }, }, }, + ResourceAllRepos: true, } for _, setup := range setupAI { setup(ait.authorizedIntegration)