From dd136858f1ea40ad3c94191d647487fa4f31926c Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 18 Oct 2024 20:33:49 +0200 Subject: Adding upstream version 9.0.0. Signed-off-by: Daniel Baumann --- services/context/quota.go | 200 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100644 services/context/quota.go (limited to 'services/context/quota.go') diff --git a/services/context/quota.go b/services/context/quota.go new file mode 100644 index 0000000..94e8847 --- /dev/null +++ b/services/context/quota.go @@ -0,0 +1,200 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package context + +import ( + "context" + "net/http" + "strings" + + quota_model "code.gitea.io/gitea/models/quota" + "code.gitea.io/gitea/modules/base" +) + +type QuotaTargetType int + +const ( + QuotaTargetUser QuotaTargetType = iota + QuotaTargetRepo + QuotaTargetOrg +) + +// QuotaExceeded +// swagger:response quotaExceeded +type APIQuotaExceeded struct { + Message string `json:"message"` + UserID int64 `json:"user_id"` + UserName string `json:"username,omitempty"` +} + +// QuotaGroupAssignmentAPI returns a middleware to handle context-quota-group assignment for api routes +func QuotaGroupAssignmentAPI() func(ctx *APIContext) { + return func(ctx *APIContext) { + groupName := ctx.Params("quotagroup") + group, err := quota_model.GetGroupByName(ctx, groupName) + if err != nil { + ctx.Error(http.StatusInternalServerError, "quota_model.GetGroupByName", err) + return + } + if group == nil { + ctx.NotFound() + return + } + ctx.QuotaGroup = group + } +} + +// QuotaRuleAssignmentAPI returns a middleware to handle context-quota-rule assignment for api routes +func QuotaRuleAssignmentAPI() func(ctx *APIContext) { + return func(ctx *APIContext) { + ruleName := ctx.Params("quotarule") + rule, err := quota_model.GetRuleByName(ctx, ruleName) + if err != nil { + ctx.Error(http.StatusInternalServerError, "quota_model.GetRuleByName", err) + return + } + if rule == nil { + ctx.NotFound() + return + } + ctx.QuotaRule = rule + } +} + +// ctx.CheckQuota checks whether the user in question is within quota limits (web context) +func (ctx *Context) CheckQuota(subject quota_model.LimitSubject, userID int64, username string) bool { + ok, err := checkQuota(ctx.Base.originCtx, subject, userID, username, func(userID int64, username string) { + showHTML := false + for _, part := range ctx.Req.Header["Accept"] { + if strings.Contains(part, "text/html") { + showHTML = true + break + } + } + if !showHTML { + ctx.plainTextInternal(3, http.StatusRequestEntityTooLarge, []byte("Quota exceeded.\n")) + return + } + + ctx.Data["IsRepo"] = ctx.Repo.Repository != nil + ctx.Data["Title"] = "Quota Exceeded" + ctx.HTML(http.StatusRequestEntityTooLarge, base.TplName("status/413")) + }, func(err error) { + ctx.Error(http.StatusInternalServerError, "quota_model.EvaluateForUser") + }) + if err != nil { + return false + } + return ok +} + +// ctx.CheckQuota checks whether the user in question is within quota limits (API context) +func (ctx *APIContext) CheckQuota(subject quota_model.LimitSubject, userID int64, username string) bool { + ok, err := checkQuota(ctx.Base.originCtx, subject, userID, username, func(userID int64, username string) { + ctx.JSON(http.StatusRequestEntityTooLarge, APIQuotaExceeded{ + Message: "quota exceeded", + UserID: userID, + UserName: username, + }) + }, func(err error) { + ctx.InternalServerError(err) + }) + if err != nil { + return false + } + return ok +} + +// EnforceQuotaWeb returns a middleware that enforces quota limits on the given web route. +func EnforceQuotaWeb(subject quota_model.LimitSubject, target QuotaTargetType) func(ctx *Context) { + return func(ctx *Context) { + ctx.CheckQuota(subject, target.UserID(ctx), target.UserName(ctx)) + } +} + +// EnforceQuotaWeb returns a middleware that enforces quota limits on the given API route. +func EnforceQuotaAPI(subject quota_model.LimitSubject, target QuotaTargetType) func(ctx *APIContext) { + return func(ctx *APIContext) { + ctx.CheckQuota(subject, target.UserID(ctx), target.UserName(ctx)) + } +} + +// checkQuota wraps quota checking into a single function +func checkQuota(ctx context.Context, subject quota_model.LimitSubject, userID int64, username string, quotaExceededHandler func(userID int64, username string), errorHandler func(err error)) (bool, error) { + ok, err := quota_model.EvaluateForUser(ctx, userID, subject) + if err != nil { + errorHandler(err) + return false, err + } + if !ok { + quotaExceededHandler(userID, username) + return false, nil + } + return true, nil +} + +type QuotaContext interface { + GetQuotaTargetUserID(target QuotaTargetType) int64 + GetQuotaTargetUserName(target QuotaTargetType) string +} + +func (ctx *Context) GetQuotaTargetUserID(target QuotaTargetType) int64 { + switch target { + case QuotaTargetUser: + return ctx.Doer.ID + case QuotaTargetRepo: + return ctx.Repo.Repository.OwnerID + case QuotaTargetOrg: + return ctx.Org.Organization.ID + default: + return 0 + } +} + +func (ctx *Context) GetQuotaTargetUserName(target QuotaTargetType) string { + switch target { + case QuotaTargetUser: + return ctx.Doer.Name + case QuotaTargetRepo: + return ctx.Repo.Repository.Owner.Name + case QuotaTargetOrg: + return ctx.Org.Organization.Name + default: + return "" + } +} + +func (ctx *APIContext) GetQuotaTargetUserID(target QuotaTargetType) int64 { + switch target { + case QuotaTargetUser: + return ctx.Doer.ID + case QuotaTargetRepo: + return ctx.Repo.Repository.OwnerID + case QuotaTargetOrg: + return ctx.Org.Organization.ID + default: + return 0 + } +} + +func (ctx *APIContext) GetQuotaTargetUserName(target QuotaTargetType) string { + switch target { + case QuotaTargetUser: + return ctx.Doer.Name + case QuotaTargetRepo: + return ctx.Repo.Repository.Owner.Name + case QuotaTargetOrg: + return ctx.Org.Organization.Name + default: + return "" + } +} + +func (target QuotaTargetType) UserID(ctx QuotaContext) int64 { + return ctx.GetQuotaTargetUserID(target) +} + +func (target QuotaTargetType) UserName(ctx QuotaContext) string { + return ctx.GetQuotaTargetUserName(target) +} -- cgit v1.2.3