2026-05-07 21:37:25 -06:00
|
|
|
package httpauth
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
2026-05-18 14:15:12 -06:00
|
|
|
"encoding/json"
|
2026-05-07 21:37:25 -06:00
|
|
|
|
|
|
|
|
"code.nochebuena.dev/go/rbac"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type claimsPermissionProvider struct {
|
|
|
|
|
claimsKey string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewClaimsPermissionProvider returns an rbac.PermissionProvider that reads
|
|
|
|
|
// pre-computed permission masks from JWT claims stored in the request context
|
|
|
|
|
// by SetTokenData. Expects claims[claimsKey] to be a map[string]any where each
|
|
|
|
|
// key is a resource name and the value is the bitmask as int64 or float64
|
|
|
|
|
// (JSON unmarshaling decodes numbers as float64).
|
|
|
|
|
// Returns 0 without error if the claim is absent — callers treat 0 as no access.
|
|
|
|
|
func NewClaimsPermissionProvider(claimsKey string) rbac.PermissionProvider {
|
|
|
|
|
return &claimsPermissionProvider{claimsKey: claimsKey}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *claimsPermissionProvider) ResolveMask(ctx context.Context, _, resource string) (rbac.PermissionMask, error) {
|
|
|
|
|
claims, ok := getClaims(ctx)
|
|
|
|
|
if !ok {
|
|
|
|
|
return 0, nil
|
|
|
|
|
}
|
2026-05-18 14:15:12 -06:00
|
|
|
masks, ok := claims[p.claimsKey].(map[string]any)
|
2026-05-07 21:37:25 -06:00
|
|
|
if !ok {
|
|
|
|
|
return 0, nil
|
|
|
|
|
}
|
2026-05-18 14:15:12 -06:00
|
|
|
// Check specific resource first, then wildcard fallback.
|
|
|
|
|
for _, key := range []string{resource, "*"} {
|
|
|
|
|
switch v := masks[key].(type) {
|
|
|
|
|
case int64:
|
|
|
|
|
return rbac.PermissionMask(v), nil
|
|
|
|
|
case float64:
|
|
|
|
|
return rbac.PermissionMask(int64(v)), nil
|
|
|
|
|
case json.Number:
|
|
|
|
|
n, err := v.Int64()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return 0, nil
|
|
|
|
|
}
|
|
|
|
|
return rbac.PermissionMask(n), nil
|
|
|
|
|
}
|
2026-05-07 21:37:25 -06:00
|
|
|
}
|
|
|
|
|
return 0, nil
|
|
|
|
|
}
|