bab29a83fa
Sweep all .go imports + go.mod, plus module-path refs in .golangci.yml, Dockerfile, swagger templates, locale example, and repo/wiki URLs (MokoGitea-Fork -> MokoGIT). Part of the MokoGIT rebrand. Claude-Session: https://claude.ai/code/session_01Bqe7fAuHQeiLueYfeHFrHw
131 lines
4.4 KiB
Go
131 lines
4.4 KiB
Go
// Copyright 2026 Moko Consulting <hello@mokoconsulting.tech>
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package git
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"code.mokoconsulting.tech/MokoConsulting/MokoGIT/models/db"
|
|
repo_model "code.mokoconsulting.tech/MokoConsulting/MokoGIT/models/repo"
|
|
user_model "code.mokoconsulting.tech/MokoConsulting/MokoGIT/models/user"
|
|
"code.mokoconsulting.tech/MokoConsulting/MokoGIT/modules/glob"
|
|
"code.mokoconsulting.tech/MokoConsulting/MokoGIT/modules/log"
|
|
"code.mokoconsulting.tech/MokoConsulting/MokoGIT/modules/timeutil"
|
|
|
|
"xorm.io/builder"
|
|
)
|
|
|
|
// OrgPushPolicy is a single org-wide policy enforced in the pre-receive hook on
|
|
// every repository of the organization. Unlike the branch/tag rulesets there is at
|
|
// most one policy per org. Empty pattern / zero fields mean "no constraint". See #727.
|
|
type OrgPushPolicy struct {
|
|
ID int64 `xorm:"pk autoincr"`
|
|
OrgID int64 `xorm:"UNIQUE NOT NULL"`
|
|
BranchNamePattern string `xorm:"TEXT"`
|
|
TagNamePattern string `xorm:"TEXT"`
|
|
RequireSecretBlock bool `xorm:"NOT NULL DEFAULT false"`
|
|
MaxFileSize int64 `xorm:"NOT NULL DEFAULT 0"`
|
|
BlockedFilePatterns string `xorm:"TEXT"`
|
|
CreatedUnix timeutil.TimeStamp `xorm:"created"`
|
|
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
|
|
}
|
|
|
|
func init() {
|
|
db.RegisterModel(new(OrgPushPolicy))
|
|
}
|
|
|
|
// nameMatchesPattern reports whether name satisfies a glob pattern. An empty pattern
|
|
// imposes no constraint; an invalid pattern fails open (no constraint) so a
|
|
// misconfigured policy never blocks all pushes.
|
|
func nameMatchesPattern(pattern, name string) bool {
|
|
pattern = strings.TrimSpace(pattern)
|
|
if pattern == "" {
|
|
return true
|
|
}
|
|
g, err := glob.Compile(pattern, '/')
|
|
if err != nil {
|
|
log.Warn("Invalid org push policy name pattern %q: %v", pattern, err)
|
|
return true
|
|
}
|
|
return g.Match(name)
|
|
}
|
|
|
|
// BranchNameAllowed reports whether a branch name satisfies the naming policy.
|
|
func (p *OrgPushPolicy) BranchNameAllowed(name string) bool {
|
|
return nameMatchesPattern(p.BranchNamePattern, name)
|
|
}
|
|
|
|
// TagNameAllowed reports whether a tag name satisfies the naming policy.
|
|
func (p *OrgPushPolicy) TagNameAllowed(name string) bool {
|
|
return nameMatchesPattern(p.TagNamePattern, name)
|
|
}
|
|
|
|
// BlockedFileGlobs parses the ';'-separated blocked file pattern list.
|
|
func (p *OrgPushPolicy) BlockedFileGlobs() []glob.Glob {
|
|
var out []glob.Glob
|
|
for _, expr := range strings.Split(p.BlockedFilePatterns, ";") {
|
|
expr = strings.TrimSpace(strings.ToLower(expr))
|
|
if expr == "" {
|
|
continue
|
|
}
|
|
if g, err := glob.Compile(expr, '.', '/'); err == nil {
|
|
out = append(out, g)
|
|
} else {
|
|
log.Warn("Invalid org push policy blocked file pattern %q: %v", expr, err)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// GetOrgPushPolicy returns the org's push policy, or nil if none is configured.
|
|
func GetOrgPushPolicy(ctx context.Context, orgID int64) (*OrgPushPolicy, error) {
|
|
policy, exist, err := db.Get[OrgPushPolicy](ctx, builder.Eq{"org_id": orgID})
|
|
if err != nil {
|
|
return nil, err
|
|
} else if !exist {
|
|
return nil, nil //nolint:nilnil
|
|
}
|
|
return policy, nil
|
|
}
|
|
|
|
// GetOrgPushPolicyForRepo returns the push policy of the repo's owning organization,
|
|
// or nil if the owner is not an organization or has no policy.
|
|
func GetOrgPushPolicyForRepo(ctx context.Context, repo *repo_model.Repository) (*OrgPushPolicy, error) {
|
|
owner, err := user_model.GetUserByID(ctx, repo.OwnerID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !owner.IsOrganization() {
|
|
return nil, nil //nolint:nilnil
|
|
}
|
|
return GetOrgPushPolicy(ctx, owner.ID)
|
|
}
|
|
|
|
// UpsertOrgPushPolicy creates or updates the single push policy for an org.
|
|
func UpsertOrgPushPolicy(ctx context.Context, policy *OrgPushPolicy) error {
|
|
existing, err := GetOrgPushPolicy(ctx, policy.OrgID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if existing == nil {
|
|
if _, err := db.GetEngine(ctx).Insert(policy); err != nil {
|
|
return fmt.Errorf("Insert OrgPushPolicy: %v", err)
|
|
}
|
|
return nil
|
|
}
|
|
policy.ID = existing.ID
|
|
if _, err := db.GetEngine(ctx).ID(existing.ID).AllCols().Update(policy); err != nil {
|
|
return fmt.Errorf("Update OrgPushPolicy: %v", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// DeleteOrgPushPolicy removes an org's push policy.
|
|
func DeleteOrgPushPolicy(ctx context.Context, orgID int64) error {
|
|
_, err := db.GetEngine(ctx).Where("org_id = ?", orgID).Delete(new(OrgPushPolicy))
|
|
return err
|
|
}
|