Alerting: Receiver resource permissions service (#93552)

This commit is contained in:
Matthew Jacobson
2024-09-20 18:31:42 -04:00
committed by GitHub
parent 7398fe3fcb
commit 1ede1e32b8
11 changed files with 295 additions and 57 deletions
+55 -12
View File
@@ -17,6 +17,15 @@ var (
ScopeReceiversAll = ScopeReceiversProvider.GetResourceAllScope()
)
// ReceiverPermission is a type for representing a receiver permission.
type ReceiverPermission string
const (
ReceiverPermissionView ReceiverPermission = "View"
ReceiverPermissionEdit ReceiverPermission = "Edit"
ReceiverPermissionAdmin ReceiverPermission = "Admin"
)
var (
// Asserts pre-conditions for read access to redacted receivers. If this evaluates to false, the user cannot read any redacted receivers.
readRedactedReceiversPreConditionsEval = ac.EvalAny(
@@ -125,6 +134,28 @@ var (
ac.EvalPermission(ac.ActionAlertingReceiversDelete, ScopeReceiversProvider.GetResourceScopeUID(uid)),
)
}
// Admin
// Asserts pre-conditions for resource permissions access to receivers. If this evaluates to false, the user cannot modify permissions for any receivers.
permissionsReceiversPreConditionsEval = ac.EvalAll(
ac.EvalPermission(ac.ActionAlertingReceiversPermissionsRead), // Action for receivers. UID scope.
ac.EvalPermission(ac.ActionAlertingReceiversPermissionsWrite), // Action for receivers. UID scope.
)
// Asserts resource permissions access to all receivers.
permissionsAllReceiversEval = ac.EvalAll(
ac.EvalPermission(ac.ActionAlertingReceiversPermissionsRead, ScopeReceiversAll),
ac.EvalPermission(ac.ActionAlertingReceiversPermissionsWrite, ScopeReceiversAll),
)
// Asserts resource permissions access to a specific receiver.
permissionsReceiverEval = func(uid string) ac.Evaluator {
return ac.EvalAll(
ac.EvalPermission(ac.ActionAlertingReceiversPermissionsRead, ScopeReceiversProvider.GetResourceScopeUID(uid)),
ac.EvalPermission(ac.ActionAlertingReceiversPermissionsWrite, ScopeReceiversProvider.GetResourceScopeUID(uid)),
)
}
)
type ReceiverAccess[T models.Identified] struct {
@@ -133,6 +164,7 @@ type ReceiverAccess[T models.Identified] struct {
create actionAccess[T]
update actionAccess[T]
delete actionAccess[T]
permissions actionAccess[T]
}
// NewReceiverAccess creates a new ReceiverAccess service. If includeProvisioningActions is true, the service will include
@@ -199,6 +231,18 @@ func NewReceiverAccess[T models.Identified](a ac.AccessControl, includeProvision
},
authorizeAll: deleteAllReceiversEval,
},
permissions: actionAccess[T]{
genericService: genericService{
ac: a,
},
resource: "receiver",
action: "admin", // Essentially read+write receiver resource permissions.
authorizeSome: permissionsReceiversPreConditionsEval,
authorizeOne: func(receiver models.Identified) ac.Evaluator {
return permissionsReceiverEval(receiver.GetUID())
},
authorizeAll: permissionsAllReceiversEval,
},
}
// If this service is meant for the provisioning API, we include the provisioning actions as possible permissions.
@@ -219,9 +263,10 @@ func NewReceiverAccess[T models.Identified](a ac.AccessControl, includeProvision
})
}
// Write and delete permissions should require read permissions.
// Write, delete, and permissions management should require read permissions.
extendAccessControl(&rcvAccess.update, ac.EvalAll, rcvAccess.read)
extendAccessControl(&rcvAccess.delete, ac.EvalAll, rcvAccess.read)
extendAccessControl(&rcvAccess.permissions, ac.EvalAll, rcvAccess.read)
return rcvAccess
}
@@ -335,12 +380,11 @@ func (s ReceiverAccess[T]) Access(ctx context.Context, user identity.Requester,
basePerms.Set(models.ReceiverPermissionReadSecret, true) // Has access to all receivers.
}
// TODO: Add when resource permissions are implemented.
//if err := s.permissions.AuthorizePreConditions(ctx, user); err != nil {
// basePerms.Set(models.ReceiverPermissionAdmin, false) // Doesn't match the preconditions.
//} else if err := s.permissions.AuthorizeAll(ctx, user); err == nil {
// basePerms.Set(models.ReceiverPermissionAdmin, true) // Has access to all receivers.
//}
if err := s.permissions.AuthorizePreConditions(ctx, user); err != nil {
basePerms.Set(models.ReceiverPermissionAdmin, false) // Doesn't match the preconditions.
} else if err := s.permissions.AuthorizeAll(ctx, user); err == nil {
basePerms.Set(models.ReceiverPermissionAdmin, true) // Has access to all receivers.
}
if err := s.update.AuthorizePreConditions(ctx, user); err != nil {
basePerms.Set(models.ReceiverPermissionWrite, false) // Doesn't match the preconditions.
@@ -371,11 +415,10 @@ func (s ReceiverAccess[T]) Access(ctx context.Context, user identity.Requester,
permSet.Set(models.ReceiverPermissionReadSecret, err == nil)
}
// TODO: Add when resource permissions are implemented.
//if _, ok := permSet.Has(models.ReceiverPermissionAdmin); !ok {
// err := s.permissions.authorize(ctx, user, rcv)
// permSet.Set(models.ReceiverPermissionAdmin, err == nil)
//}
if _, ok := permSet.Has(models.ReceiverPermissionAdmin); !ok {
err := s.permissions.authorize(ctx, user, rcv)
permSet.Set(models.ReceiverPermissionAdmin, err == nil)
}
if _, ok := permSet.Has(models.ReceiverPermissionWrite); !ok {
err := s.update.authorize(ctx, user, rcv)
@@ -238,6 +238,65 @@ func TestReceiverAccess(t *testing.T) {
recv3.UID: permissions(),
},
},
// Receiver admin.
{
name: "receiver read permissions alone can't admin",
user: newViewUser(ac.Permission{Action: ac.ActionAlertingReceiversPermissionsRead, Scope: ScopeReceiversAll}),
expected: map[string]models.ReceiverPermissionSet{
recv1.UID: permissions(),
recv2.UID: permissions(),
recv3.UID: permissions(),
},
},
{
name: "receiver write permissions alone can't admin",
user: newViewUser(ac.Permission{Action: ac.ActionAlertingReceiversPermissionsWrite, Scope: ScopeReceiversAll}),
expected: map[string]models.ReceiverPermissionSet{
recv1.UID: permissions(),
recv2.UID: permissions(),
recv3.UID: permissions(),
},
},
{
name: "global receiver read + write permissions can admin",
user: newViewUser(
ac.Permission{Action: ac.ActionAlertingReceiversPermissionsRead, Scope: ScopeReceiversAll},
ac.Permission{Action: ac.ActionAlertingReceiversPermissionsWrite, Scope: ScopeReceiversAll},
),
expected: map[string]models.ReceiverPermissionSet{
recv1.UID: permissions(models.ReceiverPermissionAdmin),
recv2.UID: permissions(models.ReceiverPermissionAdmin),
recv3.UID: permissions(models.ReceiverPermissionAdmin),
},
},
{
name: "per-receiver read + write permissions should have per-receiver admin",
user: newViewUser(
ac.Permission{Action: ac.ActionAlertingReceiversPermissionsRead, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)},
ac.Permission{Action: ac.ActionAlertingReceiversPermissionsWrite, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)},
ac.Permission{Action: ac.ActionAlertingReceiversPermissionsRead, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)},
ac.Permission{Action: ac.ActionAlertingReceiversPermissionsWrite, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)},
),
expected: map[string]models.ReceiverPermissionSet{
recv1.UID: permissions(models.ReceiverPermissionAdmin),
recv2.UID: permissions(),
recv3.UID: permissions(models.ReceiverPermissionAdmin),
},
},
{
name: "per-receiver admin should require read",
user: newEmptyUser(
ac.Permission{Action: ac.ActionAlertingReceiversPermissionsRead, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)},
ac.Permission{Action: ac.ActionAlertingReceiversPermissionsWrite, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv1.UID)},
ac.Permission{Action: ac.ActionAlertingReceiversPermissionsRead, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)},
ac.Permission{Action: ac.ActionAlertingReceiversPermissionsWrite, Scope: ScopeReceiversProvider.GetResourceScopeUID(recv3.UID)},
),
expected: map[string]models.ReceiverPermissionSet{
recv1.UID: permissions(),
recv2.UID: permissions(),
recv3.UID: permissions(),
},
},
// Mixed permissions.
{
name: "legacy provisioning secret read, receiver write",