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