All import paths updated to git.wntrmute.dev/mc/. Bumps mcdsl to v1.2.0. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
58 lines
1.9 KiB
Go
58 lines
1.9 KiB
Go
package server
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
|
|
"git.wntrmute.dev/mc/mcr/internal/auth"
|
|
"git.wntrmute.dev/mc/mcr/internal/policy"
|
|
)
|
|
|
|
// PolicyEvaluator abstracts the policy engine for testability.
|
|
type PolicyEvaluator interface {
|
|
Evaluate(input policy.PolicyInput) (policy.Effect, *policy.Rule)
|
|
}
|
|
|
|
// AuditFunc is an optional callback for recording policy deny audit events.
|
|
// It follows the same signature as db.WriteAuditEvent but without an error
|
|
// return — audit failures should not block request processing.
|
|
type AuditFunc func(eventType, actorID, repository, digest, ip string, details map[string]string)
|
|
|
|
// RequirePolicy returns middleware that checks the policy engine for the
|
|
// given action. Claims must already be in the context (set by RequireAuth).
|
|
// The repository name is extracted from the chi "name" URL parameter;
|
|
// global operations (catalog, version check) have an empty repository.
|
|
func RequirePolicy(evaluator PolicyEvaluator, action policy.Action, auditFn AuditFunc) func(http.Handler) http.Handler {
|
|
return func(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
claims := auth.ClaimsFromContext(r.Context())
|
|
if claims == nil {
|
|
writeOCIError(w, "UNAUTHORIZED", http.StatusUnauthorized, "authentication required")
|
|
return
|
|
}
|
|
|
|
input := policy.PolicyInput{
|
|
Subject: claims.Subject,
|
|
AccountType: claims.AccountType,
|
|
Roles: claims.Roles,
|
|
Action: action,
|
|
Repository: chi.URLParam(r, "name"),
|
|
}
|
|
|
|
effect, _ := evaluator.Evaluate(input)
|
|
if effect == policy.Deny {
|
|
if auditFn != nil {
|
|
auditFn("policy_deny", claims.Subject, input.Repository, "", r.RemoteAddr, map[string]string{
|
|
"action": string(action),
|
|
})
|
|
}
|
|
writeOCIError(w, "DENIED", http.StatusForbidden, "access denied by policy")
|
|
return
|
|
}
|
|
|
|
next.ServeHTTP(w, r)
|
|
})
|
|
}
|
|
}
|