Files
Dušan Borovčanin 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
NOISSUE - Move rules engine, alarms, reports, journal and notifications to EE (#3552)
Signed-off-by: dusan <borovcanindusan1@gmail.com>
2026-07-30 17:55:57 +02:00

267 lines
7.2 KiB
Go

// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package atom
import (
"context"
stderrors "errors"
"github.com/absmach/magistrala/pkg/policies"
)
const policyPageLimit uint64 = 100
var errUnsupportedPolicyOperation = stderrors.New("atom policy service: unsupported policy operation")
type policyClient interface {
Authorizer
AuthorizedObjectIDs(ctx context.Context, q AuthorizedObjectIDsQuery) (AuthorizedObjectIDs, error)
}
type policyWriter interface {
CapabilityID(ctx context.Context, name string) (string, error)
CreatePermissionBlock(ctx context.Context, block CreatePermissionBlock) (PermissionBlock, error)
CreateDirectPolicy(ctx context.Context, policy CreateDirectPolicy) (DirectPolicy, error)
ListDirectPolicies(ctx context.Context, q DirectPolicyQuery) (DirectPolicyList, error)
DeleteDirectPolicy(ctx context.Context, id string) error
}
type PolicyService struct {
client policyClient
}
func NewPolicyService(client policyClient) PolicyService {
return PolicyService{client: client}
}
func (ps PolicyService) AddPolicy(ctx context.Context, pr policies.Policy) error {
writer, ok := ps.client.(policyWriter)
if !ok {
return errUnsupportedPolicyOperation
}
capID, err := writer.CapabilityID(ctx, CapabilityName(pr.Permission))
if err != nil {
return err
}
block, err := writer.CreatePermissionBlock(ctx, CreatePermissionBlock{
TenantID: pr.Domain,
ScopeMode: policyGrantScopeMode(pr),
ObjectKind: policyGrantObjectKind(pr),
ObjectType: policyGrantObjectType(pr),
ObjectID: policyGrantObjectID(pr),
Effect: atomDecisionAllow,
Conditions: map[string]any{},
ActionIDs: []string{capID},
})
if err != nil {
return err
}
_, err = writer.CreateDirectPolicy(ctx, CreateDirectPolicy{
TenantID: pr.Domain,
SubjectKind: policyGrantSubjectKind(pr),
SubjectID: policySubjectID(pr),
PermissionBlockID: block.ID,
})
return err
}
func (ps PolicyService) AddPolicies(ctx context.Context, prs []policies.Policy) error {
for _, pr := range prs {
if err := ps.AddPolicy(ctx, pr); err != nil {
return err
}
}
return nil
}
func (ps PolicyService) DeletePolicyFilter(ctx context.Context, pr policies.Policy) error {
writer, ok := ps.client.(policyWriter)
if !ok {
return errUnsupportedPolicyOperation
}
capID, err := writer.CapabilityID(ctx, CapabilityName(pr.Permission))
if err != nil {
return err
}
page, err := writer.ListDirectPolicies(ctx, DirectPolicyQuery{
TenantID: pr.Domain,
SubjectKind: policyGrantSubjectKind(pr),
SubjectID: policySubjectID(pr),
Limit: policyPageLimit,
})
if err != nil {
return err
}
for _, policy := range page.Items {
if !directPolicyMatches(policy, capID, pr) {
continue
}
if err := writer.DeleteDirectPolicy(ctx, policy.ID); err != nil {
return err
}
}
return nil
}
func (ps PolicyService) DeletePolicies(ctx context.Context, prs []policies.Policy) error {
for _, pr := range prs {
if err := ps.DeletePolicyFilter(ctx, pr); err != nil {
return err
}
}
return nil
}
func (ps PolicyService) ListObjects(ctx context.Context, pr policies.Policy, _ string, limit uint64) (policies.PolicyPage, error) {
page, err := ps.ListAllObjects(ctx, pr)
if err != nil {
return policies.PolicyPage{}, err
}
if limit == 0 || uint64(len(page.Policies)) <= limit {
return page, nil
}
page.Policies = page.Policies[:limit]
return page, nil
}
func (ps PolicyService) ListAllObjects(ctx context.Context, pr policies.Policy) (policies.PolicyPage, error) {
if !isSupportedObjectList(pr) {
return policies.PolicyPage{}, errUnsupportedPolicyOperation
}
var ids []string
for offset := uint64(0); ; offset += policyPageLimit {
page, err := ps.client.AuthorizedObjectIDs(ctx, AuthorizedObjectIDsQuery{
SubjectID: policySubjectID(pr),
Action: CapabilityName(pr.Permission),
ObjectKind: policyObjectKind(pr),
ObjectType: entityKind(KindClient),
TenantID: pr.Domain,
Limit: policyPageLimit,
Offset: offset,
})
if err != nil {
return policies.PolicyPage{}, err
}
ids = append(ids, page.IDs...)
if uint64(len(page.IDs)) < policyPageLimit || offset+uint64(len(page.IDs)) >= page.Total {
break
}
}
return policies.PolicyPage{Policies: ids}, nil
}
func (ps PolicyService) CountObjects(ctx context.Context, pr policies.Policy) (uint64, error) {
page, err := ps.ListAllObjects(ctx, pr)
if err != nil {
return 0, err
}
return uint64(len(page.Policies)), nil
}
func (ps PolicyService) ListSubjects(context.Context, policies.Policy, string, uint64) (policies.PolicyPage, error) {
return policies.PolicyPage{}, errUnsupportedPolicyOperation
}
func (ps PolicyService) ListAllSubjects(context.Context, policies.Policy) (policies.PolicyPage, error) {
return policies.PolicyPage{}, errUnsupportedPolicyOperation
}
func (ps PolicyService) CountSubjects(context.Context, policies.Policy) (uint64, error) {
return 0, errUnsupportedPolicyOperation
}
func (ps PolicyService) ListPermissions(context.Context, policies.Policy, []string) (policies.Permissions, error) {
return nil, errUnsupportedPolicyOperation
}
func isSupportedObjectList(pr policies.Policy) bool {
return pr.SubjectType == policies.UserType &&
pr.Subject != "" &&
pr.ObjectType == policies.ClientType &&
pr.Permission == policies.ViewPermission
}
func policyGrantSubjectKind(pr policies.Policy) string {
if pr.SubjectType == policies.GroupType || pr.SubjectKind == policies.GroupsKind {
return atomObjectKindGroup
}
return atomObjectKindEntity
}
func policyGrantScopeMode(pr policies.Policy) string {
switch pr.ObjectType {
case policies.PlatformType:
return "platform"
case policies.DomainType:
return atomObjectKindTenant
default:
return atomScopeModeObject
}
}
func policyGrantObjectKind(pr policies.Policy) string {
if policyGrantScopeMode(pr) != atomScopeModeObject {
return ""
}
switch pr.ObjectType {
case policies.ClientType:
return atomObjectKindEntity
case policies.GroupType:
return atomObjectKindGroup
}
return atomObjectKindResource
}
func policyGrantObjectType(pr policies.Policy) string {
if policyGrantScopeMode(pr) != atomScopeModeObject {
return ""
}
if pr.ObjectType == policies.ClientType {
return atomObjectKindEntity + ":" + entityKind(KindClient)
}
switch pr.ObjectType {
case policies.ChannelType:
return "resource:" + KindChannel
case policies.RulesType:
return "resource:" + KindRule
case policies.ReportsType:
return "resource:" + KindReport
case policies.AlarmsType:
return "resource:" + KindAlarm
case policies.GroupType:
return ""
default:
return ""
}
}
func policyGrantObjectID(pr policies.Policy) string {
if policyGrantScopeMode(pr) != atomScopeModeObject {
return ""
}
return policyResourceID(pr)
}
func directPolicyMatches(policy DirectPolicy, actionID string, pr policies.Policy) bool {
block := policy.PermissionBlock
if block.ID == "" || block.ScopeMode != policyGrantScopeMode(pr) {
return false
}
if block.ObjectKind != policyGrantObjectKind(pr) ||
block.ObjectType != policyGrantObjectType(pr) ||
block.ObjectID != policyGrantObjectID(pr) {
return false
}
for _, action := range block.Actions {
if action.ID == actionID {
return true
}
}
return false
}