clonepack/internal/api/auth.go
GnomeZworc 87fe581353
add a simple auth function
Signed-off-by: GnomeZworc <nicolas.boufidjeline@g3e.fr>
2026-04-25 23:49:56 +02:00

91 lines
2.7 KiB
Go

package api
import (
"context"
"errors"
"net"
"net/http"
"github.com/go-chi/chi/v5/middleware"
)
// Action identifies the operation being performed.
// Format: "resource:verb"
type Action string
const (
ActionRepoList Action = "repos:list"
ActionRepoCreate Action = "repos:create"
ActionRepoRead Action = "repos:read"
ActionRepoUpdate Action = "repos:update"
ActionRepoDelete Action = "repos:delete"
ActionCloneStart Action = "clone:start"
ActionCloneRead Action = "clone:read"
ActionSyncTrigger Action = "sync:trigger"
ActionSyncRead Action = "sync:read"
ActionSyncApprove Action = "sync:approve"
ActionSyncReject Action = "sync:reject"
ActionSyncBlock Action = "sync:block"
ActionSnapshotCreate Action = "snapshots:create"
ActionSnapshotRead Action = "snapshots:read"
ActionSnapshotDelete Action = "snapshots:delete"
ActionSnapshotRollback Action = "snapshots:rollback"
)
var ErrForbidden = errors.New("forbidden")
// Caller holds the identity injected into the request context by LocalhostAuth.
type Caller struct {
IP string
RequestID string
LocalhostPrivileged bool
}
type contextKey string
const callerKey contextKey = "caller"
// CallerFromContext returns the Caller injected by LocalhostAuth, or nil.
func CallerFromContext(ctx context.Context) *Caller {
c, _ := ctx.Value(callerKey).(*Caller)
return c
}
// Authorize checks whether the caller in ctx is allowed to perform action on repoID.
// repoID == 0 means the action is not scoped to a specific repo (e.g. list, create).
//
// If localhost_privileged is enabled, 127.0.0.1/::1 are always allowed without a token.
// Other callers are currently allowed too; token enforcement will be added with the web UI.
func Authorize(ctx context.Context, action Action, repoID int64) error {
caller := CallerFromContext(ctx)
if caller == nil {
return ErrForbidden
}
if caller.LocalhostPrivileged && (caller.IP == "127.0.0.1" || caller.IP == "::1") {
return nil
}
// Future: look up token permissions for non-localhost callers.
return nil
}
// LocalhostAuth injects a Caller into the context for downstream Authorize calls.
// localhostPrivileged controls whether 127.0.0.1/::1 bypass future token enforcement.
func LocalhostAuth(localhostPrivileged bool) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ip, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
ip = r.RemoteAddr
}
caller := &Caller{
IP: ip,
RequestID: middleware.GetReqID(r.Context()),
LocalhostPrivileged: localhostPrivileged,
}
next.ServeHTTP(w, r.WithContext(context.WithValue(r.Context(), callerKey, caller)))
})
}
}