Files
magistrala/internal/atom/bootstrap.go
T
Arvindh 52645e2c39
Property Based Tests / api-test (push) Has been cancelled
Continuous Delivery / lint-and-build (push) Has been cancelled
Deploy GitHub Pages / swagger-ui (push) Has been cancelled
CI Pipeline / Lint Proto (push) Has been cancelled
CI Pipeline / Detect Changes (push) Has been cancelled
Continuous Delivery / Build and Push Docker Images (push) Has been cancelled
CI Pipeline / lint-and-build (push) Has been cancelled
CI Pipeline / Test ${{ matrix.module }} (push) Has been cancelled
CI Pipeline / Upload Coverage (push) Has been cancelled
NOISSUE - Fix Alarms roles (#3546)
Signed-off-by: Arvindh <arvindh91@gmail.com>
2026-07-06 17:26:39 +02:00

186 lines
8.9 KiB
Go

// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package atom
import (
"context"
"fmt"
)
var magistralaActionDescriptions = map[string]string{
atomActionRead: "Read / view an object",
atomActionWrite: "Create or update an object",
atomActionDelete: "Delete an object",
atomActionManage: "Full administrative control",
atomActionPublish: "Publish messages to a channel",
atomActionSubscribe: "Subscribe to channel messages",
atomActionExecute: "Execute a command or action",
atomActionList: "List objects",
atomActionAlarmRead: "Read alarms in a tenant or rule scope",
atomActionAlarmUpdate: "Update alarms in a tenant or rule scope",
atomActionAlarmDelete: "Delete alarms in a tenant or rule scope",
atomActionAlarmAssign: "Assign alarms in a tenant or rule scope",
atomActionAlarmAcknowledge: "Acknowledge alarms in a tenant or rule scope",
atomActionAlarmResolve: "Resolve alarms in a tenant or rule scope",
}
var magistralaActionApplicability = []CapabilityApplicabilitySpec{
{ActionName: atomActionWrite, ObjectKind: atomObjectKindTenant},
{ActionName: atomActionAlarmRead, ObjectKind: atomObjectKindTenant},
{ActionName: atomActionAlarmUpdate, ObjectKind: atomObjectKindTenant},
{ActionName: atomActionAlarmDelete, ObjectKind: atomObjectKindTenant},
{ActionName: atomActionAlarmAssign, ObjectKind: atomObjectKindTenant},
{ActionName: atomActionAlarmAcknowledge, ObjectKind: atomObjectKindTenant},
{ActionName: atomActionAlarmResolve, ObjectKind: atomObjectKindTenant},
{ActionName: atomActionRead, ObjectKind: atomObjectKindGroup},
{ActionName: atomActionWrite, ObjectKind: atomObjectKindGroup},
{ActionName: atomActionDelete, ObjectKind: atomObjectKindGroup},
{ActionName: atomActionManage, ObjectKind: atomObjectKindGroup},
{ActionName: atomActionList, ObjectKind: atomObjectKindGroup},
{ActionName: atomActionRead, ObjectKind: atomObjectKindResource, ObjectType: atomObjectTypeResourceChannel},
{ActionName: atomActionWrite, ObjectKind: atomObjectKindResource, ObjectType: atomObjectTypeResourceChannel},
{ActionName: atomActionDelete, ObjectKind: atomObjectKindResource, ObjectType: atomObjectTypeResourceChannel},
{ActionName: atomActionManage, ObjectKind: atomObjectKindResource, ObjectType: atomObjectTypeResourceChannel},
{ActionName: atomActionPublish, ObjectKind: atomObjectKindResource, ObjectType: atomObjectTypeResourceChannel},
{ActionName: atomActionSubscribe, ObjectKind: atomObjectKindResource, ObjectType: atomObjectTypeResourceChannel},
{ActionName: atomActionRead, ObjectKind: atomObjectKindResource, ObjectType: atomObjectTypeResourceRule},
{ActionName: atomActionWrite, ObjectKind: atomObjectKindResource, ObjectType: atomObjectTypeResourceRule},
{ActionName: atomActionDelete, ObjectKind: atomObjectKindResource, ObjectType: atomObjectTypeResourceRule},
{ActionName: atomActionManage, ObjectKind: atomObjectKindResource, ObjectType: atomObjectTypeResourceRule},
{ActionName: atomActionExecute, ObjectKind: atomObjectKindResource, ObjectType: atomObjectTypeResourceRule},
{ActionName: atomActionList, ObjectKind: atomObjectKindResource, ObjectType: atomObjectTypeResourceRule},
{ActionName: atomActionAlarmUpdate, ObjectKind: atomObjectKindResource, ObjectType: atomObjectTypeResourceRule},
{ActionName: atomActionAlarmDelete, ObjectKind: atomObjectKindResource, ObjectType: atomObjectTypeResourceRule},
{ActionName: atomActionAlarmAssign, ObjectKind: atomObjectKindResource, ObjectType: atomObjectTypeResourceRule},
{ActionName: atomActionAlarmAcknowledge, ObjectKind: atomObjectKindResource, ObjectType: atomObjectTypeResourceRule},
{ActionName: atomActionAlarmResolve, ObjectKind: atomObjectKindResource, ObjectType: atomObjectTypeResourceRule},
{ActionName: atomActionRead, ObjectKind: atomObjectKindResource, ObjectType: atomObjectTypeResourceReport},
{ActionName: atomActionWrite, ObjectKind: atomObjectKindResource, ObjectType: atomObjectTypeResourceReport},
{ActionName: atomActionDelete, ObjectKind: atomObjectKindResource, ObjectType: atomObjectTypeResourceReport},
{ActionName: atomActionManage, ObjectKind: atomObjectKindResource, ObjectType: atomObjectTypeResourceReport},
{ActionName: atomActionExecute, ObjectKind: atomObjectKindResource, ObjectType: atomObjectTypeResourceReport},
{ActionName: atomActionList, ObjectKind: atomObjectKindResource, ObjectType: atomObjectTypeResourceReport},
{ActionName: atomActionRead, ObjectKind: atomObjectKindResource, ObjectType: atomObjectTypeResourceAlarm},
{ActionName: atomActionWrite, ObjectKind: atomObjectKindResource, ObjectType: atomObjectTypeResourceAlarm},
{ActionName: atomActionDelete, ObjectKind: atomObjectKindResource, ObjectType: atomObjectTypeResourceAlarm},
{ActionName: atomActionManage, ObjectKind: atomObjectKindResource, ObjectType: atomObjectTypeResourceAlarm},
{ActionName: atomActionList, ObjectKind: atomObjectKindResource, ObjectType: atomObjectTypeResourceAlarm},
{ActionName: atomActionAlarmRead, ObjectKind: atomObjectKindResource, ObjectType: atomObjectTypeResourceAlarm},
{ActionName: atomActionAlarmUpdate, ObjectKind: atomObjectKindResource, ObjectType: atomObjectTypeResourceAlarm},
{ActionName: atomActionAlarmDelete, ObjectKind: atomObjectKindResource, ObjectType: atomObjectTypeResourceAlarm},
{ActionName: atomActionAlarmAssign, ObjectKind: atomObjectKindResource, ObjectType: atomObjectTypeResourceAlarm},
{ActionName: atomActionAlarmAcknowledge, ObjectKind: atomObjectKindResource, ObjectType: atomObjectTypeResourceAlarm},
{ActionName: atomActionAlarmResolve, ObjectKind: atomObjectKindResource, ObjectType: atomObjectTypeResourceAlarm},
}
var magistralaActionAssignmentRules = []ActionAssignmentRuleSpec{
{
EntityKind: atomKindDevice,
ActionName: atomActionPublish,
ObjectKind: atomObjectKindResource,
ObjectType: atomObjectTypeResourceChannel,
Decision: atomDecisionAllow,
},
{
EntityKind: atomKindDevice,
ActionName: atomActionSubscribe,
ObjectKind: atomObjectKindResource,
ObjectType: atomObjectTypeResourceChannel,
Decision: atomDecisionAllow,
},
}
// BootstrapMagistralaActions installs Magistrala-specific action applicability in Atom.
// It is safe to call repeatedly during startup.
func BootstrapMagistralaActions(ctx context.Context, client *Client) error {
if client == nil {
return fmt.Errorf("atom client is nil")
}
capabilities, err := client.ListCapabilities(ctx)
if err != nil {
return fmt.Errorf("list atom actions: %w", err)
}
byName := map[string]Capability{}
for _, capability := range capabilities.Items {
byName[capability.Name] = capability
}
for _, spec := range magistralaActionApplicability {
capability, ok := byName[spec.ActionName]
if !ok {
description := spec.Description
if description == "" {
description = magistralaActionDescriptions[spec.ActionName]
}
capability, err = client.CreateCapability(ctx, spec.ActionName, description)
if err != nil {
if !IsConflict(err) {
return fmt.Errorf("create atom action %q: %w", spec.ActionName, err)
}
id, lookupErr := client.CapabilityID(ctx, spec.ActionName)
if lookupErr != nil {
return fmt.Errorf("lookup existing atom action %q after conflict: %w", spec.ActionName, lookupErr)
}
capability = Capability{ID: id, Name: spec.ActionName, Description: description}
}
byName[spec.ActionName] = capability
}
if _, err := client.AddCapabilityApplicability(ctx, capability.ID, spec.ObjectKind, spec.ObjectType); err != nil {
return fmt.Errorf("add atom applicability %s -> %s:%s: %w", spec.ActionName, spec.ObjectKind, spec.ObjectType, err)
}
}
for _, spec := range magistralaActionAssignmentRules {
if err := ensureActionAssignmentRule(ctx, client, spec); err != nil {
return fmt.Errorf("ensure atom assignment guardrail %s %s %s:%s: %w", spec.EntityKind, spec.ActionName, spec.ObjectKind, spec.ObjectType, err)
}
}
return nil
}
func ensureActionAssignmentRule(ctx context.Context, client *Client, spec ActionAssignmentRuleSpec) error {
rules, err := client.ListActionAssignmentRules(ctx, spec)
if err != nil {
return err
}
if actionAssignmentRuleExists(rules.Items, spec) {
return nil
}
if _, err := client.CreateActionAssignmentRule(ctx, spec); err != nil {
if !IsConflict(err) {
return err
}
rules, lookupErr := client.ListActionAssignmentRules(ctx, spec)
if lookupErr != nil {
return fmt.Errorf("lookup existing rule after conflict: %w", lookupErr)
}
if actionAssignmentRuleExists(rules.Items, spec) {
return nil
}
return err
}
return nil
}
func actionAssignmentRuleExists(rules []ActionAssignmentRule, spec ActionAssignmentRuleSpec) bool {
for _, rule := range rules {
if rule.TenantID == spec.TenantID &&
rule.EntityKind == spec.EntityKind &&
rule.ActionName == spec.ActionName &&
rule.ObjectKind == spec.ObjectKind &&
rule.ObjectType == spec.ObjectType &&
rule.Decision == spec.Decision &&
rule.IsAbsolute == spec.IsAbsolute {
return true
}
}
return false
}