4e594d3a7d
- Change IsAuthorized() to use deny-by-default when RBAC is enabled - If ANY authorization rules are configured, only notifiers with explicit allowed_roles are accessible - Notifiers without rules are denied access when RBAC is active - If NO rules are configured, maintain open access for backward compatibility - Add HasRules() helper method to check if RBAC is enabled This fixes the issue where notifiers WITHOUT allowed_roles were being returned instead of the notifiers WITH matching allowed_roles. Now when RBAC is configured: - Only notifiers with explicit rules that match the user's roles are returned - All other notifiers are hidden from the client Example: If only email has allowed_roles=['admin'] and user has role 'admin': - OLD: email ✓, stdout ✓ (WRONG - stdout should be hidden) - NEW: email ✓, stdout ✗ (CORRECT - only email is returned) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
84 lines
2.6 KiB
Go
84 lines
2.6 KiB
Go
package auth
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/igodwin/notifier/internal/domain"
|
|
)
|
|
|
|
// NotifierAuthz manages authorization rules for notifiers
|
|
type NotifierAuthz struct {
|
|
// Map of "type:account" -> allowed roles
|
|
rules map[string][]string
|
|
}
|
|
|
|
// NewNotifierAuthz creates a new notifier authorization manager
|
|
func NewNotifierAuthz() *NotifierAuthz {
|
|
return &NotifierAuthz{
|
|
rules: make(map[string][]string),
|
|
}
|
|
}
|
|
|
|
// RegisterRule registers authorization rule for a notifier type and account
|
|
func (a *NotifierAuthz) RegisterRule(notificationType domain.NotificationType, account string, allowedRoles []string) {
|
|
key := makeAuthzKey(notificationType, account)
|
|
a.rules[key] = allowedRoles
|
|
}
|
|
|
|
// IsAuthorized checks if an auth context is authorized to use a specific notifier
|
|
func (a *NotifierAuthz) IsAuthorized(auth *AuthContext, notificationType domain.NotificationType, account string) bool {
|
|
if auth == nil || len(auth.Roles) == 0 {
|
|
return false
|
|
}
|
|
|
|
key := makeAuthzKey(notificationType, account)
|
|
allowedRoles, exists := a.rules[key]
|
|
|
|
// If RBAC is enabled (at least one rule exists), restrict access:
|
|
// - Notifiers with explicit rules: check if user has allowed roles
|
|
// - Notifiers without rules: deny access (must be explicitly allowed)
|
|
if a.HasRules() {
|
|
if !exists {
|
|
// RBAC is enabled but this notifier has no rule - deny access
|
|
return false
|
|
}
|
|
// Check if any of the user's roles is in the allowed roles
|
|
for _, userRole := range auth.Roles {
|
|
for _, allowedRole := range allowedRoles {
|
|
if userRole == allowedRole {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// If no rules are registered at all, allow all authenticated users (open access)
|
|
return true
|
|
}
|
|
|
|
// HasRules returns true if any authorization rules have been registered
|
|
func (a *NotifierAuthz) HasRules() bool {
|
|
return len(a.rules) > 0
|
|
}
|
|
|
|
// GetAllowedRoles returns the allowed roles for a notifier
|
|
func (a *NotifierAuthz) GetAllowedRoles(notificationType domain.NotificationType, account string) []string {
|
|
key := makeAuthzKey(notificationType, account)
|
|
return a.rules[key]
|
|
}
|
|
|
|
// SetAllowedRoles sets the allowed roles for a notifier
|
|
func (a *NotifierAuthz) SetAllowedRoles(notificationType domain.NotificationType, account string, allowedRoles []string) {
|
|
key := makeAuthzKey(notificationType, account)
|
|
a.rules[key] = allowedRoles
|
|
}
|
|
|
|
// makeAuthzKey creates a compound key from notification type and account
|
|
func makeAuthzKey(notificationType domain.NotificationType, account string) string {
|
|
if account == "" {
|
|
return string(notificationType)
|
|
}
|
|
return fmt.Sprintf("%s:%s", notificationType, account)
|
|
}
|