diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index be72565a16..90ba816b65 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -811,6 +811,28 @@ func checkDeprecatedAuthMethods(ctx *context.APIContext) { } } +func reqUnitAccess(unitType unit.Type, accessMode perm.AccessMode, ignoreGlobal bool) func(ctx *context.APIContext) { + return func(ctx *context.APIContext) { + // only check global disabled units when ignoreGlobal is false + if !ignoreGlobal && unitType.UnitGlobalDisabled() { + ctx.NotFound("Repo unit is is disabled: "+unitType.LogString(), nil) + return + } + + if ctx.ContextUser == nil { + ctx.NotFound("ContextUser is nil", nil) + return + } + + if ctx.ContextUser.IsOrganization() { + if ctx.Org.Organization.UnitPermission(ctx, ctx.Doer, unitType) < accessMode { + ctx.NotFound("ContextUser is org but doer has no access to unit", nil) + return + } + } + } +} + // Routes registers all v1 APIs routes to web application. func Routes() *web.Router { m := web.NewRouter() @@ -960,8 +982,16 @@ func Routes() *web.Router { m.Group("/projects", func() { m.Post("", bind(api.CreateProjectOption{}), org.CreateProject) + m.Group("/{id}", func() { + m.Post("/{action:open|close}", org.ChangeProjectStatus) + }) }) - }, context.UserAssignmentAPI()) + }, context.UserAssignmentAPI(), reqUnitAccess(unit.TypeProjects, perm.AccessModeWrite, true), func(ctx *context.APIContext) { + if ctx.ContextUser.IsIndividual() && ctx.ContextUser.ID != ctx.Doer.ID { + ctx.NotFound("NewProject", nil) + return + } + }) }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser), reqToken()) // Users (requires user scope) diff --git a/routers/api/v1/org/project.go b/routers/api/v1/org/project.go index a7344cfa4d..9415727b28 100644 --- a/routers/api/v1/org/project.go +++ b/routers/api/v1/org/project.go @@ -10,6 +10,7 @@ import ( "code.gitea.io/gitea/services/context" ) +// CreateProject creates a new project func CreateProject(ctx *context.APIContext) { form := web.GetForm(ctx).(*api.CreateProjectOption) @@ -37,3 +38,24 @@ func CreateProject(ctx *context.APIContext) { ctx.JSON(http.StatusCreated, map[string]int64{"id": project.ID}) } + +// ChangeProjectStatus updates the status of a project between "open" and "close" +func ChangeProjectStatus(ctx *context.APIContext) { + var toClose bool + switch ctx.PathParam(":action") { + case "open": + toClose = false + case "close": + toClose = true + default: + ctx.NotFound("ChangeProjectStatus", nil) + return + } + id := ctx.PathParamInt64(":id") + + if err := project_model.ChangeProjectStatusByRepoIDAndID(ctx, 0, id, toClose); err != nil { + ctx.NotFoundOrServerError("ChangeProjectStatusByRepoIDAndID", project_model.IsErrProjectNotExist, err) + return + } + ctx.JSON(http.StatusOK, map[string]any{"message": "project status updated successfully"}) +}