feat: Add audit logging (#38189)

Co-authored-by: bircni <bircni@users.noreply.github.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
bircni
2026-09-12 08:15:23 +00:00
committed by GitHub
co-authored by bircni wxiaoguang
parent 4d43445532
commit da37b7916b
136 changed files with 3863 additions and 208 deletions
+25 -1
View File
@@ -15,6 +15,7 @@ import (
"gitea.dev/models/db"
"gitea.dev/models/perm"
access_model "gitea.dev/models/perm/access"
repo_model "gitea.dev/models/repo"
user_model "gitea.dev/models/user"
"gitea.dev/models/webhook"
"gitea.dev/modules/git"
@@ -25,6 +26,7 @@ import (
"gitea.dev/modules/util"
"gitea.dev/modules/web"
webhook_module "gitea.dev/modules/webhook"
"gitea.dev/services/audit"
"gitea.dev/services/context"
"gitea.dev/services/convert"
"gitea.dev/services/forms"
@@ -58,6 +60,8 @@ func Webhooks(ctx *context.Context) {
}
type ownerRepoCtx struct {
Owner *user_model.User
Repo *repo_model.Repository
OwnerID int64
RepoID int64
IsAdmin bool
@@ -71,6 +75,7 @@ type ownerRepoCtx struct {
func getOwnerRepoCtx(ctx *context.Context) (*ownerRepoCtx, error) {
if ctx.Data["PageIsRepoSettings"] == true {
return &ownerRepoCtx{
Repo: ctx.Repo.Repository,
RepoID: ctx.Repo.Repository.ID,
Link: path.Join(ctx.Repo.RepoLink, "settings/hooks"),
LinkNew: path.Join(ctx.Repo.RepoLink, "settings/hooks"),
@@ -80,6 +85,7 @@ func getOwnerRepoCtx(ctx *context.Context) (*ownerRepoCtx, error) {
if ctx.Data["PageIsOrgSettings"] == true {
return &ownerRepoCtx{
Owner: ctx.ContextUser,
OwnerID: ctx.ContextUser.ID,
Link: path.Join(ctx.Org.OrgLink, "settings/hooks"),
LinkNew: path.Join(ctx.Org.OrgLink, "settings/hooks"),
@@ -89,6 +95,7 @@ func getOwnerRepoCtx(ctx *context.Context) (*ownerRepoCtx, error) {
if ctx.Data["PageIsUserSettings"] == true {
return &ownerRepoCtx{
Owner: ctx.Doer,
OwnerID: ctx.Doer.ID,
Link: path.Join(setting.AppSubURL, "/user/settings/hooks"),
LinkNew: path.Join(setting.AppSubURL, "/user/settings/hooks"),
@@ -109,6 +116,14 @@ func getOwnerRepoCtx(ctx *context.Context) (*ownerRepoCtx, error) {
return nil, errors.New("unable to set OwnerRepo context")
}
// recordWebhookAudit emits a webhook audit event scoped to the repository,
// organization, user, or instance (admin/system) the webhook belongs to. The
// shared add/edit handlers run in any of these contexts, so the scope is derived
// from orCtx rather than assuming a repository.
func (orCtx *ownerRepoCtx) recordWebhookAudit(ctx *context.Context, actions audit.ScopedActions, url string) {
audit.RecordScoped(ctx, orCtx.Owner, orCtx.Repo, actions, "webhook", url)
}
func checkHookType(ctx *context.Context) string {
hookType := strings.ToLower(ctx.PathParam("type"))
if !util.SliceContainsString(setting.Webhook.Types, hookType, true) {
@@ -258,6 +273,8 @@ func createWebhook(ctx *context.Context, params webhookParams) {
return
}
orCtx.recordWebhookAudit(ctx, audit.WebhookAdd, w.URL)
ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
ctx.Redirect(orCtx.Link)
}
@@ -311,6 +328,8 @@ func editWebhook(ctx *context.Context, params webhookParams) {
return
}
orCtx.recordWebhookAudit(ctx, audit.WebhookUpdate, w.URL)
ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
}
@@ -736,9 +755,14 @@ func ReplayWebhook(ctx *context.Context) {
// DeleteWebhook delete a webhook
func DeleteWebhook(ctx *context.Context) {
if err := webhook.DeleteWebhookByRepoID(ctx, ctx.Repo.Repository.ID, ctx.FormInt64("id")); err != nil {
hook, err := webhook.GetWebhookByRepoID(ctx, ctx.Repo.Repository.ID, ctx.FormInt64("id"))
if err != nil {
ctx.Flash.Error("GetWebhookByRepoID: " + err.Error())
} else if err := webhook.DeleteWebhookByRepoID(ctx, ctx.Repo.Repository.ID, hook.ID); err != nil {
ctx.Flash.Error("DeleteWebhookByRepoID: " + err.Error())
} else {
audit.RecordScoped(ctx, nil, ctx.Repo.Repository, audit.WebhookRemove, "webhook", hook.URL)
ctx.Flash.Success(ctx.Tr("repo.settings.webhook_deletion_success"))
}