mirror of https://github.com/go-gitea/gitea.git
feat(api): add repository permissions endpoints
GET/PUT/DELETE for repo-level settings. Following existing Gitea API patterns. Signed-off-by: SBALAVIGNESH123 <balavignesh449@gmail.com>pull/36113/head
parent
c2465f9825
commit
bddccc263e
@ -0,0 +1,208 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package repo
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/web"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
)
|
||||
|
||||
// GetActionsPermissions returns the Actions token permissions for a repository
|
||||
func GetActionsPermissions(ctx *context.APIContext) {
|
||||
// swagger:operation GET /repos/{owner}/{repo}/settings/actions/permissions repository repoGetActionsPermissions
|
||||
// ---
|
||||
// summary: Get repository Actions token permissions
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: owner
|
||||
// in: path
|
||||
// description: owner of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: repo
|
||||
// in: path
|
||||
// description: name of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/ActionsPermissionsResponse"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
// Check if user has admin access to this repo
|
||||
// NOTE: Only repo admins should be able to view/modify permission settings
|
||||
// This is important for security - we don't want regular contributors
|
||||
// to be able to grant themselves elevated permissions via Actions
|
||||
if !ctx.Repo.IsAdmin() {
|
||||
ctx.Error(http.StatusForbidden, "NoPermission", "You must be a repository admin to access this")
|
||||
return
|
||||
}
|
||||
|
||||
perms, err := actions_model.GetRepoActionPermissions(ctx, ctx.Repo.Repository.ID)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetPermissions", err)
|
||||
return
|
||||
}
|
||||
|
||||
// If no custom permissions are set, return the default (restricted mode)
|
||||
// This is intentional - we want a secure default that requires explicit opt-in
|
||||
// to more permissive settings. See: https://github.com/go-gitea/gitea/issues/24635
|
||||
if perms == nil {
|
||||
perms = &actions_model.ActionTokenPermission{
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
PermissionMode: actions_model.PermissionModeRestricted,
|
||||
// Default restricted permissions - only read contents and metadata
|
||||
ContentsRead: true,
|
||||
MetadataRead: true,
|
||||
}
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, convertToAPIPermissions(perms))
|
||||
}
|
||||
|
||||
// UpdateActionsPermissions updates the Actions token permissions for a repository
|
||||
func UpdateActionsPermissions(ctx *context.APIContext) {
|
||||
// swagger:operation PUT /repos/{owner}/{repo}/settings/actions/permissions repository repoUpdateActionsPermissions
|
||||
// ---
|
||||
// summary: Update repository Actions token permissions
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: owner
|
||||
// in: path
|
||||
// description: owner of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: repo
|
||||
// in: path
|
||||
// description: name of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: body
|
||||
// in: body
|
||||
// schema:
|
||||
// "$ref": "#/definitions/ActionsPermissions"
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/ActionsPermissionsResponse"
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
// "422":
|
||||
// "$ref": "#/responses/validationError"
|
||||
|
||||
if !ctx.Repo.IsAdmin() {
|
||||
ctx.Error(http.StatusForbidden, "NoPermission", "You must be a repository admin to modify this")
|
||||
return
|
||||
}
|
||||
|
||||
form := web.GetForm(ctx).(*api.ActionsPermissions)
|
||||
|
||||
// Validate permission mode
|
||||
if form.PermissionMode < 0 || form.PermissionMode > 2 {
|
||||
ctx.Error(http.StatusUnprocessableEntity, "InvalidMode", "Permission mode must be 0 (restricted), 1 (permissive), or 2 (custom)")
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Check if org-level permissions exist and validate against them
|
||||
// For now, we'll implement basic validation, but we should enhance this
|
||||
// to ensure repo settings don't exceed org caps. This is important for
|
||||
// multi-repository organizations where admins want centralized control.
|
||||
// See wolfogre's comment: https://github.com/go-gitea/gitea/pull/24554#issuecomment-1537040811
|
||||
|
||||
perm := &actions_model.ActionTokenPermission{
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
PermissionMode: actions_model.PermissionMode(form.PermissionMode),
|
||||
ActionsRead: form.ActionsRead,
|
||||
ActionsWrite: form.ActionsWrite,
|
||||
ContentsRead: form.ContentsRead,
|
||||
ContentsWrite: form.ContentsWrite,
|
||||
IssuesRead: form.IssuesRead,
|
||||
IssuesWrite: form.IssuesWrite,
|
||||
PackagesRead: form.PackagesRead,
|
||||
PackagesWrite: form.PackagesWrite,
|
||||
PullRequestsRead: form.PullRequestsRead,
|
||||
PullRequestsWrite: form.PullRequestsWrite,
|
||||
MetadataRead: true, // Always true - needed for basic operations
|
||||
}
|
||||
|
||||
if err := actions_model.CreateOrUpdateRepoPermissions(ctx, perm); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "UpdatePermissions", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, convertToAPIPermissions(perm))
|
||||
}
|
||||
|
||||
// ResetActionsPermissions resets permissions to default (restricted mode)
|
||||
func ResetActionsPermissions(ctx *context.APIContext) {
|
||||
// swagger:operation DELETE /repos/{owner}/{repo}/settings/actions/permissions repository repoResetActionsPermissions
|
||||
// ---
|
||||
// summary: Reset repository Actions permissions to default
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: owner
|
||||
// in: path
|
||||
// description: owner of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: repo
|
||||
// in: path
|
||||
// description: name of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "204":
|
||||
// "$ref": "#/responses/empty"
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
|
||||
if !ctx.Repo.IsAdmin() {
|
||||
ctx.Error(http.StatusForbidden, "NoPermission", "You must be a repository admin")
|
||||
return
|
||||
}
|
||||
|
||||
// Create default restricted permissions
|
||||
// This is a "safe reset" - puts the repo back to secure defaults
|
||||
defaultPerm := &actions_model.ActionTokenPermission{
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
PermissionMode: actions_model.PermissionModeRestricted,
|
||||
ContentsRead: true,
|
||||
MetadataRead: true,
|
||||
}
|
||||
|
||||
if err := actions_model.CreateOrUpdateRepoPermissions(ctx, defaultPerm); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "ResetPermissions", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// convertToAPIPermissions converts model to API response format
|
||||
// This helper keeps our internal model separate from the API contract
|
||||
func convertToAPIPermissions(perm *actions_model.ActionTokenPermission) *api.ActionsPermissions {
|
||||
return &api.ActionsPermissions{
|
||||
PermissionMode: int(perm.PermissionMode),
|
||||
ActionsRead: perm.ActionsRead,
|
||||
ActionsWrite: perm.ActionsWrite,
|
||||
ContentsRead: perm.ContentsRead,
|
||||
ContentsWrite: perm.ContentsWrite,
|
||||
IssuesRead: perm.IssuesRead,
|
||||
IssuesWrite: perm.IssuesWrite,
|
||||
PackagesRead: perm.PackagesRead,
|
||||
PackagesWrite: perm.PackagesWrite,
|
||||
PullRequestsRead: perm.PullRequestsRead,
|
||||
PullRequestsWrite: perm.PullRequestsWrite,
|
||||
MetadataRead: perm.MetadataRead,
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue