Files
pulse-triage[bot] 7dd5982ace fix(auth): synchronise local admin permissions during setup
First-run setup saved the configured administrator but left the router authorizer on its startup identity. This split Settings capability checks: administrator-only panels remained visible while API Access and Pulse Intelligence were denied until restart. Keep the captured authorizer aligned when setup commits the identity, and clear its prior bypass on successful development reset.

Cover the exact split with real file-backed RBAC policy and session setup, preserve outsider denial and identity replacement, and synchronise concurrent policy reads. Extension implementations still require independent compatibility review; this is not installation or release acceptance.

Change-source: pulse-maintainer
2026-09-10 06:52:38 +01:00

92 lines
2.8 KiB
Go

package auth
import "context"
// Authorizer defines the interface for making access control decisions.
type Authorizer interface {
// Authorize checks if a subject (from context) can perform an action on a resource.
// Returns true if allowed, false if denied, and an error if the check failed due to a system issue.
Authorize(ctx context.Context, action string, resource string) (bool, error)
}
type contextKey string
const (
contextKeyUser contextKey = "user"
contextKeyAPIToken contextKey = "apiToken"
)
// APITokenInfo is a minimal interface for checking token scopes without circular dependencies.
type APITokenInfo interface {
HasScope(scope string) bool
}
// WithUser adds a username to the context
func WithUser(ctx context.Context, username string) context.Context {
return context.WithValue(ctx, contextKeyUser, username)
}
// GetUser extracts the username from the context
func GetUser(ctx context.Context) string {
if user, ok := ctx.Value(contextKeyUser).(string); ok {
return user
}
return ""
}
// WithAPIToken adds an API token record to the context
func WithAPIToken(ctx context.Context, token APITokenInfo) context.Context {
return context.WithValue(ctx, contextKeyAPIToken, token)
}
// GetAPIToken extracts the API token record from the context
func GetAPIToken(ctx context.Context) APITokenInfo {
if token, ok := ctx.Value(contextKeyAPIToken).(APITokenInfo); ok {
return token
}
return nil
}
// GetAPITokenContextKey returns the context key used for API tokens (for testing purposes)
func GetAPITokenContextKey() contextKey {
return contextKeyAPIToken
}
// DefaultAuthorizer is a pass-through implementation that allows everything.
// Used in OSS version and when enterprise features are disabled.
type DefaultAuthorizer struct{}
func (d *DefaultAuthorizer) Authorize(ctx context.Context, action string, resource string) (bool, error) {
return true, nil
}
var globalAuthorizer Authorizer = &DefaultAuthorizer{}
// SetAuthorizer sets the global authorizer instance.
// This is used by pulse-enterprise to register the real RBAC implementation.
func SetAuthorizer(auth Authorizer) {
globalAuthorizer = auth
}
// AdminConfigurable is an optional interface for authorizers that can have an admin user set.
// SetAdminUser must be safe alongside Authorize calls; an empty username clears
// the previously configured identity.
type AdminConfigurable interface {
SetAdminUser(username string)
}
// SetAdminUser sets the admin user on the global authorizer if it supports it.
func SetAdminUser(username string) {
if username == "" {
return
}
if configurable, ok := globalAuthorizer.(AdminConfigurable); ok {
configurable.SetAdminUser(username)
}
}
// GetAuthorizer returns the global authorizer instance.
func GetAuthorizer() Authorizer {
return globalAuthorizer
}