mirror of
https://github.com/absmach/magistrala.git
synced 2026-08-07 07:14:46 +00:00
168e8b90cb
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
Signed-off-by: dusan <borovcanindusan1@gmail.com>
175 lines
7.8 KiB
Go
175 lines
7.8 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: atomActionAlarmRead, 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},
|
|
}
|
|
|
|
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
|
|
}
|