137 lines
3.5 KiB
Go
137 lines
3.5 KiB
Go
package rbac
|
|
|
|
import (
|
|
"context"
|
|
|
|
"git.haelnorr.com/h/golib/hws"
|
|
"git.haelnorr.com/h/oslstats/internal/contexts"
|
|
"git.haelnorr.com/h/oslstats/internal/db"
|
|
"git.haelnorr.com/h/oslstats/internal/permissions"
|
|
"git.haelnorr.com/h/oslstats/internal/roles"
|
|
"github.com/pkg/errors"
|
|
"github.com/uptrace/bun"
|
|
)
|
|
|
|
type Checker struct {
|
|
conn *bun.DB
|
|
s *hws.Server
|
|
}
|
|
|
|
func NewChecker(conn *bun.DB, s *hws.Server) (*Checker, error) {
|
|
if conn == nil {
|
|
return nil, errors.New("conn cannot be nil")
|
|
}
|
|
if s == nil {
|
|
return nil, errors.New("server cannot be nil")
|
|
}
|
|
return &Checker{conn: conn, s: s}, nil
|
|
}
|
|
|
|
// UserHasPermission checks if user has a specific permission (uses cache)
|
|
func (c *Checker) UserHasPermission(ctx context.Context, user *db.User, permission permissions.Permission) (bool, error) {
|
|
if user == nil {
|
|
return false, nil
|
|
}
|
|
|
|
// Check if we're in preview mode
|
|
previewRole := contexts.GetPreviewRole(ctx)
|
|
|
|
// Try cache first
|
|
cache := contexts.Permissions(ctx)
|
|
if cache != nil {
|
|
if cache.HasWildcard {
|
|
return true, nil
|
|
}
|
|
if has, exists := cache.Permissions[permission]; exists {
|
|
return has, nil
|
|
}
|
|
}
|
|
|
|
// If in preview mode, DO NOT fallback to database - use ONLY preview role permissions
|
|
// This ensures admins cannot bypass preview mode restrictions
|
|
if previewRole != nil {
|
|
// Not in cache and in preview mode = permission denied
|
|
return false, nil
|
|
}
|
|
|
|
// Not in preview mode: fallback to database for actual user permissions
|
|
var has bool
|
|
if err := db.WithTxFailSilently(ctx, c.conn, func(ctx context.Context, tx bun.Tx) error {
|
|
var err error
|
|
has, err = user.HasPermission(ctx, tx, permission)
|
|
if err != nil {
|
|
return errors.Wrap(err, "user.HasPermission")
|
|
}
|
|
return nil
|
|
}); err != nil {
|
|
return false, err
|
|
}
|
|
return has, nil
|
|
}
|
|
|
|
// UserHasRole checks if user has a specific role (uses cache)
|
|
func (c *Checker) UserHasRole(ctx context.Context, user *db.User, role roles.Role) (bool, error) {
|
|
if user == nil {
|
|
return false, nil
|
|
}
|
|
|
|
// Check if we're in preview mode
|
|
previewRole := contexts.GetPreviewRole(ctx)
|
|
|
|
cache := contexts.Permissions(ctx)
|
|
if cache != nil {
|
|
if has, exists := cache.Roles[role]; exists {
|
|
return has, nil
|
|
}
|
|
}
|
|
|
|
// If in preview mode, DO NOT fallback to database - use ONLY preview role
|
|
// This ensures admins cannot bypass preview mode restrictions
|
|
if previewRole != nil {
|
|
// Not in cache and in preview mode = role not assigned
|
|
return false, nil
|
|
}
|
|
|
|
// Not in preview mode: fallback to database for actual user roles
|
|
var has bool
|
|
if err := db.WithTxFailSilently(ctx, c.conn, func(ctx context.Context, tx bun.Tx) error {
|
|
var err error
|
|
has, err = user.HasRole(ctx, tx, role)
|
|
if err != nil {
|
|
return errors.Wrap(err, "user.HasRole")
|
|
}
|
|
return nil
|
|
}); err != nil {
|
|
return false, err
|
|
}
|
|
return has, nil
|
|
}
|
|
|
|
// UserHasAnyPermission checks if user has ANY of the given permissions
|
|
func (c *Checker) UserHasAnyPermission(ctx context.Context, user *db.User, permissions ...permissions.Permission) (bool, error) {
|
|
for _, perm := range permissions {
|
|
has, err := c.UserHasPermission(ctx, user, perm)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if has {
|
|
return true, nil
|
|
}
|
|
}
|
|
return false, nil
|
|
}
|
|
|
|
// UserHasAllPermissions checks if user has ALL of the given permissions
|
|
func (c *Checker) UserHasAllPermissions(ctx context.Context, user *db.User, permissions ...permissions.Permission) (bool, error) {
|
|
for _, perm := range permissions {
|
|
has, err := c.UserHasPermission(ctx, user, perm)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if !has {
|
|
return false, nil
|
|
}
|
|
}
|
|
return true, nil
|
|
}
|