diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index b7bb74e558..d428294036 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -436,9 +436,16 @@ func reqSiteAdmin() func(ctx *context.APIContext) { } } -// reqOwner user should be the owner of the repo or site admin. -func reqOwner() func(ctx *context.APIContext) { +// reqOwner requires that the current user is either the owner of the repository or an administrator. If one or more +// unitTypes are given, it also requires that at least one the respective unitTypes is enabled. +func reqOwner(unitTypes ...unit.Type) func(ctx *context.APIContext) { return func(ctx *context.APIContext) { + if len(unitTypes) > 0 && !slices.ContainsFunc(unitTypes, func(unitType unit.Type) bool { + return ctx.Repo.Repository.UnitEnabled(ctx, unitType) + }) { + ctx.NotFound() + return + } if !ctx.Repo.IsOwner() && !ctx.IsUserSiteAdmin() { ctx.Error(http.StatusForbidden, "reqOwner", "user should be the owner of the repo") return @@ -466,7 +473,8 @@ func reqAdmin() func(ctx *context.APIContext) { } } -// reqRepoWriter user should have a permission to write to a repo, or be a site admin +// reqRepoWriter requires that the current user has permission to write to a repository or that it is an administrator. +// One or more unitTypes have to be specified, and at least one of them has to be enabled. func reqRepoWriter(unitTypes ...unit.Type) func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if !slices.ContainsFunc(unitTypes, func(unitType unit.Type) bool { @@ -1145,7 +1153,7 @@ func Routes() *web.Route { }, reqToken()) addActionsRoutes( m, - reqOwner(), + reqOwner(unit.TypeActions), repo.NewAction(), ) m.Group("/hooks/git", func() { diff --git a/tests/integration/api_repo_actions_test.go b/tests/integration/api_repo_actions_test.go index d12691b117..44d0cb2873 100644 --- a/tests/integration/api_repo_actions_test.go +++ b/tests/integration/api_repo_actions_test.go @@ -13,6 +13,7 @@ import ( actions_model "forgejo.org/models/actions" auth_model "forgejo.org/models/auth" + "forgejo.org/models/db" repo_model "forgejo.org/models/repo" unit_model "forgejo.org/models/unit" "forgejo.org/models/unittest" @@ -20,6 +21,7 @@ import ( api "forgejo.org/modules/structs" "forgejo.org/modules/webhook" "forgejo.org/routers/api/v1/shared" + repo_service "forgejo.org/services/repository" files_service "forgejo.org/services/repository/files" "forgejo.org/tests" @@ -545,4 +547,25 @@ func TestAPIRepoActionsRunnerOperations(t *testing.T) { assert.Equal(t, "token does not have at least one of required scope(s): [write:repository]", errorMessage.Message) }) + + t.Run("Endpoints disabled if Actions disabled", func(t *testing.T) { + repository, _, cleanUp := tests.CreateDeclarativeRepo(t, user2, "no-actions", + []unit_model.Type{unit_model.TypeCode, unit_model.TypeActions}, []unit_model.Type{}, nil) + defer cleanUp() + + requestURL := fmt.Sprintf("/api/v1/repos/%s/actions/runners", repository.FullName()) + + request := NewRequest(t, "GET", requestURL) + request.AddTokenAuth(readToken) + MakeRequest(t, request, http.StatusOK) + + enabledUnits := []repo_model.RepoUnit{{RepoID: repository.ID, Type: unit_model.TypeCode}} + disabledUnits := []unit_model.Type{unit_model.TypeActions} + err := repo_service.UpdateRepositoryUnits(db.DefaultContext, repository, enabledUnits, disabledUnits) + require.NoError(t, err) + + request = NewRequest(t, "GET", requestURL) + request.AddTokenAuth(readToken) + MakeRequest(t, request, http.StatusNotFound) + }) } diff --git a/tests/integration/api_repo_secrets_test.go b/tests/integration/api_repo_secrets_test.go index a2c9439f03..3da4412dde 100644 --- a/tests/integration/api_repo_secrets_test.go +++ b/tests/integration/api_repo_secrets_test.go @@ -9,11 +9,16 @@ import ( "testing" auth_model "forgejo.org/models/auth" + "forgejo.org/models/db" repo_model "forgejo.org/models/repo" + unit_model "forgejo.org/models/unit" "forgejo.org/models/unittest" user_model "forgejo.org/models/user" api "forgejo.org/modules/structs" + repo_service "forgejo.org/services/repository" "forgejo.org/tests" + + "github.com/stretchr/testify/require" ) func TestAPIRepoSecrets(t *testing.T) { @@ -109,4 +114,25 @@ func TestAPIRepoSecrets(t *testing.T) { AddTokenAuth(token) MakeRequest(t, req, http.StatusBadRequest) }) + + t.Run("Endpoints disabled if Actions disabled", func(t *testing.T) { + repository, _, cleanUp := tests.CreateDeclarativeRepo(t, user, "no-actions", + []unit_model.Type{unit_model.TypeCode, unit_model.TypeActions}, []unit_model.Type{}, nil) + defer cleanUp() + + getURL := fmt.Sprintf("/api/v1/repos/%s/actions/secrets", repository.FullName()) + + getRequest := NewRequest(t, "GET", getURL) + getRequest.AddTokenAuth(token) + MakeRequest(t, getRequest, http.StatusOK) + + enabledUnits := []repo_model.RepoUnit{{RepoID: repository.ID, Type: unit_model.TypeCode}} + disabledUnits := []unit_model.Type{unit_model.TypeActions} + err := repo_service.UpdateRepositoryUnits(db.DefaultContext, repository, enabledUnits, disabledUnits) + require.NoError(t, err) + + getRequest = NewRequest(t, "GET", getURL) + getRequest.AddTokenAuth(token) + MakeRequest(t, getRequest, http.StatusNotFound) + }) } diff --git a/tests/integration/api_repo_variables_test.go b/tests/integration/api_repo_variables_test.go index 27401dcdd3..9330ca6d7e 100644 --- a/tests/integration/api_repo_variables_test.go +++ b/tests/integration/api_repo_variables_test.go @@ -9,13 +9,17 @@ import ( "testing" auth_model "forgejo.org/models/auth" + "forgejo.org/models/db" repo_model "forgejo.org/models/repo" + unit_model "forgejo.org/models/unit" "forgejo.org/models/unittest" user_model "forgejo.org/models/user" api "forgejo.org/modules/structs" + repo_service "forgejo.org/services/repository" "forgejo.org/tests" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestAPIRepoVariablesTestCreateRepositoryVariable(t *testing.T) { @@ -234,3 +238,30 @@ func TestAPIRepoVariablesGetAllRepositoryVariables(t *testing.T) { assert.Equal(t, "SECOND", actionVariables[1].Name) assert.Equal(t, "Dolor sit amet", actionVariables[1].Data) } + +func TestAPIRepoVariablesEndpointsDisabledIfActionsDisabled(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + session := loginUser(t, user2.Name) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) + + repository, _, cleanUp := tests.CreateDeclarativeRepo(t, user2, "no-actions", + []unit_model.Type{unit_model.TypeCode, unit_model.TypeActions}, []unit_model.Type{}, nil) + defer cleanUp() + + getURL := fmt.Sprintf("/api/v1/repos/%s/actions/variables", repository.FullName()) + + getRequest := NewRequest(t, "GET", getURL) + getRequest.AddTokenAuth(token) + MakeRequest(t, getRequest, http.StatusOK) + + enabledUnits := []repo_model.RepoUnit{{RepoID: repository.ID, Type: unit_model.TypeCode}} + disabledUnits := []unit_model.Type{unit_model.TypeActions} + err := repo_service.UpdateRepositoryUnits(db.DefaultContext, repository, enabledUnits, disabledUnits) + require.NoError(t, err) + + getRequest = NewRequest(t, "GET", getURL) + getRequest.AddTokenAuth(token) + MakeRequest(t, getRequest, http.StatusNotFound) +}