MG-370 - Add fine grained access control to rules engine (#402)

* update go mod file

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* fix rules endpoint tests

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* fix yaml file

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* fix build

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* address comments

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* remove roles from alarms

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* change approach for schema combaine

Signed-off-by: Arvindh <arvindh91@gmail.com>

* change approach for schema combaine

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix permissions for rules

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* fix authorization file

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* fix linter

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* fix linter

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

---------

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>
Co-authored-by: Arvindh <arvindh91@gmail.com>
This commit is contained in:
Steve Munene
2026-03-05 13:42:51 +03:00
committed by GitHub
parent 8e75edc9f5
commit 362a4fc76d
35 changed files with 5431 additions and 252 deletions
+1 -1
View File
@@ -51,7 +51,7 @@ jobs:
- name: Run linters
uses: golangci/golangci-lint-action@v9
with:
version: v2.4.0
version: latest
args: --config ./tools/config/.golangci.yaml
run-tests:
+124 -15
View File
@@ -20,33 +20,47 @@ import (
"github.com/absmach/magistrala/internal/email"
"github.com/absmach/magistrala/pkg/emailer"
pkglog "github.com/absmach/magistrala/pkg/logger"
"github.com/absmach/magistrala/pkg/prometheus"
"github.com/absmach/magistrala/pkg/ticker"
"github.com/absmach/magistrala/re"
httpapi "github.com/absmach/magistrala/re/api"
"github.com/absmach/magistrala/re/events"
"github.com/absmach/magistrala/re/middleware"
"github.com/absmach/magistrala/re/operations"
repg "github.com/absmach/magistrala/re/postgres"
grpcClient "github.com/absmach/magistrala/readers/api/grpc"
"github.com/absmach/supermq"
dpostgres "github.com/absmach/supermq/domains/postgres"
smqlog "github.com/absmach/supermq/logger"
smqauthn "github.com/absmach/supermq/pkg/authn"
authnsvc "github.com/absmach/supermq/pkg/authn/authsvc"
mgauthz "github.com/absmach/supermq/pkg/authz"
authzsvc "github.com/absmach/supermq/pkg/authz/authsvc"
"github.com/absmach/supermq/pkg/callout"
dconsumer "github.com/absmach/supermq/pkg/domains/events/consumer"
domainsAuthz "github.com/absmach/supermq/pkg/domains/grpcclient"
"github.com/absmach/supermq/pkg/grpcclient"
jaegerclient "github.com/absmach/supermq/pkg/jaeger"
"github.com/absmach/supermq/pkg/messaging"
smqbrokers "github.com/absmach/supermq/pkg/messaging/brokers"
brokerstracing "github.com/absmach/supermq/pkg/messaging/brokers/tracing"
"github.com/absmach/supermq/pkg/permissions"
"github.com/absmach/supermq/pkg/policies"
"github.com/absmach/supermq/pkg/policies/spicedb"
pgclient "github.com/absmach/supermq/pkg/postgres"
"github.com/absmach/supermq/pkg/roles"
"github.com/absmach/supermq/pkg/server"
httpserver "github.com/absmach/supermq/pkg/server/http"
spicedbdecoder "github.com/absmach/supermq/pkg/spicedb"
"github.com/absmach/supermq/pkg/uuid"
"github.com/authzed/authzed-go/v1"
"github.com/authzed/grpcutil"
"github.com/caarlos0/env/v11"
"github.com/go-chi/chi/v5"
"go.opentelemetry.io/otel/trace"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
const (
@@ -67,15 +81,21 @@ const (
const channBuffer = 256
type config struct {
LogLevel string `env:"MG_RE_LOG_LEVEL" envDefault:"info"`
InstanceID string `env:"MG_RE_INSTANCE_ID" envDefault:""`
JaegerURL url.URL `env:"SMQ_JAEGER_URL" envDefault:"http://localhost:4318/v1/traces"`
SendTelemetry bool `env:"SMQ_SEND_TELEMETRY" envDefault:"true"`
ESURL string `env:"SMQ_ES_URL" envDefault:"nats://localhost:4222"`
CacheURL string `env:"MG_RE_CACHE_URL" envDefault:"redis://localhost:6379/0"`
CacheKeyDuration time.Duration `env:"MG_RE_CACHE_KEY_DURATION" envDefault:"10m"`
TraceRatio float64 `env:"SMQ_JAEGER_TRACE_RATIO" envDefault:"1.0"`
BrokerURL string `env:"SMQ_MESSAGE_BROKER_URL" envDefault:"nats://localhost:4222"`
LogLevel string `env:"MG_RE_LOG_LEVEL" envDefault:"info"`
InstanceID string `env:"MG_RE_INSTANCE_ID" envDefault:""`
JaegerURL url.URL `env:"SMQ_JAEGER_URL" envDefault:"http://localhost:4318/v1/traces"`
SendTelemetry bool `env:"SMQ_SEND_TELEMETRY" envDefault:"true"`
ESURL string `env:"SMQ_ES_URL" envDefault:"nats://localhost:4222"`
ESConsumerName string `env:"MG_RE_EVENT_CONSUMER" envDefault:"rules_engine"`
CacheURL string `env:"MG_RE_CACHE_URL" envDefault:"redis://localhost:6379/0"`
CacheKeyDuration time.Duration `env:"MG_RE_CACHE_KEY_DURATION" envDefault:"10m"`
TraceRatio float64 `env:"SMQ_JAEGER_TRACE_RATIO" envDefault:"1.0"`
BrokerURL string `env:"SMQ_MESSAGE_BROKER_URL" envDefault:"nats://localhost:4222"`
SpicedbHost string `env:"SMQ_SPICEDB_HOST" envDefault:"localhost"`
SpicedbPort string `env:"SMQ_SPICEDB_PORT" envDefault:"50051"`
SpicedbPreSharedKey string `env:"SMQ_SPICEDB_PRE_SHARED_KEY" envDefault:"12345678"`
SpicedbSchemaFile string `env:"SMQ_SPICEDB_SCHEMA_FILE" envDefault:"schema.zed"`
PermissionsFile string `env:"SMQ_PERMISSIONS_FILE" envDefault:"permission.yaml"`
}
func main() {
@@ -126,7 +146,14 @@ func main() {
return
}
db, err := pgclient.Setup(dbConfig, *repg.Migration())
migration, err := repg.Migration()
if err != nil {
logger.Error(err.Error())
exitCode = 1
return
}
db, err := pgclient.Setup(dbConfig, *migration)
if err != nil {
logger.Error(err.Error())
exitCode = 1
@@ -238,6 +265,16 @@ func main() {
logger.Info("AuthZ successfully connected to auth gRPC server " + authnClient.Secure())
database := pgclient.NewDatabase(db, dbConfig, tracer)
ddatabase := pgclient.NewDatabase(db, dbConfig, tracer)
drepo := dpostgres.NewRepository(ddatabase)
if err := dconsumer.DomainsEventsSubscribe(ctx, drepo, cfg.ESURL, cfg.ESConsumerName, logger); err != nil {
logger.Error(fmt.Sprintf("failed to create domains event store : %s", err))
exitCode = 1
return
}
regrpcCfg := grpcclient.Config{}
if err := env.ParseWithOptions(&regrpcCfg, env.Options{Prefix: envPrefixGrpc}); err != nil {
logger.Error(fmt.Sprintf("failed to load clients gRPC client configuration : %s", err))
@@ -255,7 +292,7 @@ func main() {
readersClient := grpcClient.NewReadersClient(client.Connection(), regrpcCfg.Timeout)
logger.Info("Readers gRPC client successfully connected to readers gRPC server " + client.Secure())
svc, err := newService(ctx, database, runInfo, msgSub, writersPub, alarmsPub, authz, ec, logger, readersClient, callout, cfg)
svc, err := newService(ctx, cfg, database, runInfo, msgSub, writersPub, alarmsPub, authz, ec, logger, readersClient, callout, tracer)
if err != nil {
logger.Error(fmt.Sprintf("failed to create services: %s", err))
exitCode = 1
@@ -307,7 +344,7 @@ func main() {
}
}
func newService(ctx context.Context, db pgclient.Database, runInfo chan pkglog.RunInfo, rePubSub messaging.PubSub, writersPub, alarmsPub messaging.Publisher, authz mgauthz.Authorization, ec email.Config, logger *slog.Logger, readersClient grpcReadersV1.ReadersServiceClient, callout callout.Callout, cfg config) (re.Service, error) {
func newService(ctx context.Context, cfg config, db pgclient.Database, runInfo chan pkglog.RunInfo, rePubSub messaging.PubSub, writersPub, alarmsPub messaging.Publisher, authz mgauthz.Authorization, ec email.Config, logger *slog.Logger, readersClient grpcReadersV1.ReadersServiceClient, callout callout.Callout, tracer trace.Tracer) (re.Service, error) {
repo := repg.NewRepository(db)
idp := uuid.New()
@@ -316,21 +353,93 @@ func newService(ctx context.Context, db pgclient.Database, runInfo chan pkglog.R
logger.Error(fmt.Sprintf("failed to configure e-mailing util: %s", err.Error()))
}
csvc := re.NewService(repo, runInfo, idp, rePubSub, writersPub, alarmsPub, ticker.NewTicker(time.Second*30), emailerClient, readersClient)
policyService, err := newSpiceDBPolicyServiceEvaluator(cfg, logger)
if err != nil {
return nil, err
}
logger.Info("Policy service successfully connected to SpiceDB gRPC server")
availableActions, builtInRoles, err := availableActionsAndBuiltInRoles(cfg.SpicedbSchemaFile)
if err != nil {
return nil, fmt.Errorf("failed to get available actions and built-in roles: %w", err)
}
csvc, err := re.NewService(repo, runInfo, policyService, idp, rePubSub, writersPub, alarmsPub, ticker.NewTicker(time.Second*30), emailerClient, readersClient, availableActions, builtInRoles)
if err != nil {
return nil, fmt.Errorf("failed to create RE service: %w", err)
}
csvc, err = events.NewEventStoreMiddleware(ctx, csvc, cfg.ESURL)
if err != nil {
return nil, fmt.Errorf("failed to init re event store middleware: %w", err)
}
csvc, err = middleware.AuthorizationMiddleware(csvc, authz)
permConfig, err := permissions.ParsePermissionsFile(cfg.PermissionsFile)
if err != nil {
return nil, fmt.Errorf("failed to parse permissions file: %w", err)
}
ruleOps, ruleRoleOps, err := permConfig.GetEntityPermissions(operations.EntityType)
if err != nil {
return nil, fmt.Errorf("failed to get rule permissions: %w", err)
}
entitiesOps, err := permissions.NewEntitiesOperations(
permissions.EntitiesPermission{
operations.EntityType: ruleOps,
},
permissions.EntitiesOperationDetails[permissions.Operation]{
operations.EntityType: operations.OperationDetails(),
},
)
if err != nil {
return nil, fmt.Errorf("failed to create entities operations: %w", err)
}
roleOps, err := permissions.NewOperations(roles.Operations(), ruleRoleOps)
if err != nil {
return nil, fmt.Errorf("failed to create role operations: %w", err)
}
csvc, err = middleware.AuthorizationMiddleware(csvc, authz, entitiesOps, roleOps)
if err != nil {
return nil, err
}
csvc, err = middleware.NewCallout(csvc, callout)
csvc, err = middleware.NewCallout(csvc, callout, entitiesOps, roleOps)
if err != nil {
return nil, err
}
csvc = middleware.LoggingMiddleware(csvc, logger)
counter, latency := prometheus.MakeMetrics("re", "api")
csvc = middleware.NewMetricsMiddleware(counter, latency, csvc)
csvc = middleware.NewTracingMiddleware(tracer, csvc)
return csvc, nil
}
func newSpiceDBPolicyServiceEvaluator(cfg config, logger *slog.Logger) (policies.Service, error) {
client, err := authzed.NewClientWithExperimentalAPIs(
fmt.Sprintf("%s:%s", cfg.SpicedbHost, cfg.SpicedbPort),
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpcutil.WithInsecureBearerToken(cfg.SpicedbPreSharedKey),
)
if err != nil {
return nil, err
}
ps := spicedb.NewPolicyService(client, logger)
return ps, nil
}
func availableActionsAndBuiltInRoles(spicedbSchemaFile string) ([]roles.Action, map[roles.BuiltInRoleName][]roles.Action, error) {
availableActions, err := spicedbdecoder.GetActionsFromSchema(spicedbSchemaFile, operations.EntityType)
if err != nil {
return []roles.Action{}, map[roles.BuiltInRoleName][]roles.Action{}, err
}
builtInRoles := map[roles.BuiltInRoleName][]roles.Action{
re.BuiltInRoleAdmin: availableActions,
}
return availableActions, builtInRoles, err
}
+4 -1
View File
@@ -85,11 +85,14 @@ SMQ_SPICEDB_DB_PORT=5432
### SpiceDB config
SMQ_SPICEDB_PRE_SHARED_KEY="12345678"
SMQ_SPICEDB_SCHEMA_FILE="/schema.zed"
SMQ_SPICEDB_SCHEMA_FILE="/schemas/combined-schema.zed"
SMQ_SPICEDB_HOST=supermq-spicedb
SMQ_SPICEDB_PORT=50051
SMQ_SPICEDB_DATASTORE_ENGINE=postgres
### Permissions
SMQ_PERMISSIONS_FILE=/schemas/permission.yaml
### UI
SMQ_UI_PATH_PREFIX=/ui
+18
View File
@@ -253,6 +253,7 @@ services:
container_name: magistrala-re
depends_on:
- re-db
- spicedb-migrate
restart: on-failure
environment:
MG_RE_LOG_LEVEL: ${MG_RE_LOG_LEVEL}
@@ -290,6 +291,8 @@ services:
SMQ_SPICEDB_PRE_SHARED_KEY: ${SMQ_SPICEDB_PRE_SHARED_KEY}
SMQ_SPICEDB_HOST: ${SMQ_SPICEDB_HOST}
SMQ_SPICEDB_PORT: ${SMQ_SPICEDB_PORT}
SMQ_SPICEDB_SCHEMA_FILE: ${SMQ_SPICEDB_SCHEMA_FILE}
SMQ_PERMISSIONS_FILE: ${SMQ_PERMISSIONS_FILE}
MG_RE_INSTANCE_ID: ${MG_RE_INSTANCE_ID}
MG_EMAIL_HOST: ${MG_EMAIL_HOST}
MG_EMAIL_PORT: ${MG_EMAIL_PORT}
@@ -314,6 +317,8 @@ services:
networks:
- magistrala-base-net
volumes:
- ./permission.yaml:${SMQ_PERMISSIONS_FILE}
- ./spicedb/combined-schema.zed:${SMQ_SPICEDB_SCHEMA_FILE}
- ./templates/${MG_RE_EMAIL_TEMPLATE}:/email.tmpl
# Auth gRPC client certificates
- type: bind
@@ -353,6 +358,7 @@ services:
container_name: magistrala-alarms
depends_on:
- alarms-db
- spicedb-migrate
restart: on-failure
environment:
MG_ALARMS_LOG_LEVEL: ${MG_ALARMS_LOG_LEVEL}
@@ -382,6 +388,11 @@ services:
SMQ_DOMAINS_GRPC_CLIENT_CERT: ${SMQ_DOMAINS_GRPC_CLIENT_CERT:+/domains-grpc-client.crt}
SMQ_DOMAINS_GRPC_CLIENT_KEY: ${SMQ_DOMAINS_GRPC_CLIENT_KEY:+/domains-grpc-client.key}
SMQ_DOMAINS_GRPC_SERVER_CA_CERTS: ${SMQ_DOMAINS_GRPC_SERVER_CA_CERTS:+/domains-grpc-server-ca.crt}
SMQ_SPICEDB_PRE_SHARED_KEY: ${SMQ_SPICEDB_PRE_SHARED_KEY}
SMQ_SPICEDB_HOST: ${SMQ_SPICEDB_HOST}
SMQ_SPICEDB_PORT: ${SMQ_SPICEDB_PORT}
SMQ_SPICEDB_SCHEMA_FILE: ${SMQ_SPICEDB_SCHEMA_FILE}
SMQ_PERMISSIONS_FILE: ${SMQ_PERMISSIONS_FILE}
MG_ALARMS_INSTANCE_ID: ${MG_ALARMS_INSTANCE_ID}
SMQ_ALLOW_UNVERIFIED_USER: ${SMQ_ALLOW_UNVERIFIED_USER}
ports:
@@ -389,6 +400,8 @@ services:
networks:
- magistrala-base-net
volumes:
- ./permission.yaml:${SMQ_PERMISSIONS_FILE}
- ./spicedb/combined-schema.zed:${SMQ_SPICEDB_SCHEMA_FILE}
# Auth gRPC client certificates
- type: bind
source: ${SMQ_AUTH_GRPC_CLIENT_CERT:-ssl/certs/dummy/client_cert}
@@ -442,6 +455,7 @@ services:
container_name: magistrala-reports
depends_on:
- reports-db
- spicedb-migrate
restart: on-failure
environment:
MG_REPORTS_LOG_LEVEL: ${MG_REPORTS_LOG_LEVEL}
@@ -472,6 +486,8 @@ services:
SMQ_SPICEDB_PRE_SHARED_KEY: ${SMQ_SPICEDB_PRE_SHARED_KEY}
SMQ_SPICEDB_HOST: ${SMQ_SPICEDB_HOST}
SMQ_SPICEDB_PORT: ${SMQ_SPICEDB_PORT}
SMQ_SPICEDB_SCHEMA_FILE: ${SMQ_SPICEDB_SCHEMA_FILE}
SMQ_PERMISSIONS_FILE: ${SMQ_PERMISSIONS_FILE}
MG_REPORTS_INSTANCE_ID: ${MG_RE_INSTANCE_ID}
MG_EMAIL_HOST: ${MG_EMAIL_HOST}
MG_EMAIL_PORT: ${MG_EMAIL_PORT}
@@ -496,6 +512,8 @@ services:
networks:
- magistrala-base-net
volumes:
- ./permission.yaml:${SMQ_PERMISSIONS_FILE}
- ./spicedb/combined-schema.zed:${SMQ_SPICEDB_SCHEMA_FILE}
- ./templates/${MG_REPORTS_EMAIL_TEMPLATE}:/email.tmpl
# Auth gRPC client certificates
- type: bind
+71
View File
@@ -0,0 +1,71 @@
# Copyright (c) Abstract Machines
# SPDX-License-Identifier: Apache-2.0
alarm:
operations:
- add: alarm_create_permission
- list: alarm_read_permission
- view: read_permission
- update: update_permission
- enable: update_permission
- disable: update_permission
- delete: delete_permission
rule:
operations:
- add: rule_create_permission
- list: rule_read_permission
- view: read_permission
- update: update_permission
- update_tags: update_permission
- update_schedule: update_permission
- enable: update_permission
- disable: update_permission
- delete: delete_permission
roles_operations:
- add: manage_role_permission
- remove: manage_role_permission
- update: manage_role_permission
- retrieve: view_role_users_permission
- retrieve_all: view_role_users_permission
- add_actions: manage_role_permission
- list_actions: view_role_users_permission
- check_actions_exists: view_role_users_permission
- remove_actions: manage_role_permission
- remove_all_actions: manage_role_permission
- add_members: add_role_users_permission
- list_members: view_role_users_permission
- check_members_exists: view_role_users_permission
- remove_members: remove_role_users_permission
- remove_all_members: remove_role_users_permission
report:
operations:
- add: report_create_permission
- list: report_read_permission
- generate: report_create_permission
- view: read_permission
- update: update_permission
- update_schedule: update_permission
- enable: update_permission
- disable: update_permission
- delete: delete_permission
- update_template: update_permission
- view_template: read_permission
- delete_template: delete_permission
roles_operations:
- add: manage_role_permission
- remove: manage_role_permission
- update: manage_role_permission
- retrieve: view_role_users_permission
- retrieve_all: view_role_users_permission
- add_actions: manage_role_permission
- list_actions: view_role_users_permission
- check_actions_exists: view_role_users_permission
- remove_actions: manage_role_permission
- remove_all_actions: manage_role_permission
- add_members: add_role_users_permission
- list_members: view_role_users_permission
- check_members_exists: view_role_users_permission
- remove_members: remove_role_users_permission
- remove_all_members: remove_role_users_permission
+706
View File
@@ -0,0 +1,706 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
// Code generated by scripts/combine-schema.sh. DO NOT EDIT.
//
// Combined from:
// - docker/supermq-docker/spicedb/schema.zed
// - docker/spicedb/override-schema.zed
definition user {}
definition role {
relation entity: domain | group | channel | client
relation member: user
relation built_in_role: domain | group | channel | client
permission delete = entity->manage_role_permission - built_in_role->manage_role_permission
permission update = entity->manage_role_permission - built_in_role->manage_role_permission
permission read = entity->manage_role_permission - built_in_role->manage_role_permission
permission add_user = entity->add_role_users_permission
permission remove_user = entity->remove_role_users_permission
permission view_user = entity->view_role_users_permission
}
definition client {
relation domain: domain // This can't be clubbed with parent_group, but if parent_group is unassigned then we could not track belongs to which domain, so it safe to add domain
relation parent_group: group
relation update: role#member
relation read: role#member
relation delete: role#member
relation set_parent_group: role#member
relation connect_to_channel: role#member
relation manage_role: role#member
relation add_role_users: role#member
relation remove_role_users: role#member
relation view_role_users: role#member
permission update_permission = update + parent_group->client_update_permission + domain->client_update_permission
permission read_permission = read + parent_group->client_read_permission + domain->client_read_permission
permission delete_permission = delete + parent_group->client_delete_permission + domain->client_delete_permission
permission set_parent_group_permission = set_parent_group + parent_group->client_set_parent_group_permission + domain->client_set_parent_group_permission
permission connect_to_channel_permission = connect_to_channel + parent_group->client_connect_to_channel_permission + domain->client_connect_to_channel_permission
permission manage_role_permission = manage_role + parent_group->client_manage_role_permission + domain->client_manage_role_permission
permission add_role_users_permission = add_role_users + parent_group->client_add_role_users_permission + domain->client_add_role_users_permission
permission remove_role_users_permission = remove_role_users + parent_group->client_remove_role_users_permission + domain->client_remove_role_users_permission
permission view_role_users_permission = view_role_users + parent_group->client_view_role_users_permission + domain->client_view_role_users_permission
}
definition channel {
relation domain: domain // This can't be clubbed with parent_group, but if parent_group is unassigned then we could not track belongs to which domain, so it safe to add domain
relation parent_group: group
relation update: role#member
relation read: role#member
relation delete: role#member
relation set_parent_group: role#member
relation connect_to_client: role#member
relation publish: role#member | client
relation subscribe: role#member | client
relation manage_role: role#member
relation add_role_users: role#member
relation remove_role_users: role#member
relation view_role_users: role#member
permission update_permission = update + parent_group->channel_update_permission + domain->channel_update_permission
permission read_permission = read + parent_group->channel_read_permission + domain->channel_read_permission
permission delete_permission = delete + parent_group->channel_delete_permission + domain->channel_delete_permission
permission set_parent_group_permission = set_parent_group + parent_group->channel_set_parent_group_permission + domain->channel_set_parent_group_permission
permission connect_to_client_permission = connect_to_client + parent_group->channel_connect_to_client_permission + domain->channel_connect_to_client_permission
permission publish_permission = publish + parent_group->channel_publish_permission + domain->channel_publish_permission
permission subscribe_permission = subscribe + parent_group->channel_subscribe_permission + domain->channel_subscribe_permission
permission manage_role_permission = manage_role + parent_group->channel_manage_role_permission + domain->channel_manage_role_permission
permission add_role_users_permission = add_role_users + parent_group->channel_add_role_users_permission + domain->channel_add_role_users_permission
permission remove_role_users_permission = remove_role_users + parent_group->channel_remove_role_users_permission + domain->channel_remove_role_users_permission
permission view_role_users_permission = view_role_users + parent_group->channel_view_role_users_permission + domain->channel_view_role_users_permission
}
definition group {
relation domain: domain // This can't be clubbed with parent_group, but if parent_group is unassigned then we could not track belongs to which domain, so it is safe to add domain
relation parent_group: group
relation update: role#member
relation read: role#member
relation membership: role#member
relation delete: role#member
relation set_child: role#member
relation set_parent: role#member
relation manage_role: role#member
relation add_role_users: role#member
relation remove_role_users: role#member
relation view_role_users: role#member
relation client_create: role#member
relation channel_create: role#member
// this allows to add parent for group during the new group creation
relation subgroup_create: role#member
relation subgroup_client_create: role#member
relation subgroup_channel_create: role#member
relation client_update: role#member
relation client_read: role#member
relation client_delete: role#member
relation client_set_parent_group: role#member
relation client_connect_to_channel: role#member
relation client_manage_role: role#member
relation client_add_role_users: role#member
relation client_remove_role_users: role#member
relation client_view_role_users: role#member
relation channel_update: role#member
relation channel_read: role#member
relation channel_delete: role#member
relation channel_set_parent_group: role#member
relation channel_connect_to_client: role#member
relation channel_publish: role#member
relation channel_subscribe: role#member
relation channel_manage_role: role#member
relation channel_add_role_users: role#member
relation channel_remove_role_users: role#member
relation channel_view_role_users: role#member
relation subgroup_update: role#member
relation subgroup_read: role#member
relation subgroup_membership: role#member
relation subgroup_delete: role#member
relation subgroup_set_child: role#member
relation subgroup_set_parent: role#member
relation subgroup_manage_role: role#member
relation subgroup_add_role_users: role#member
relation subgroup_remove_role_users: role#member
relation subgroup_view_role_users: role#member
relation subgroup_client_update: role#member
relation subgroup_client_read: role#member
relation subgroup_client_delete: role#member
relation subgroup_client_set_parent_group: role#member
relation subgroup_client_connect_to_channel: role#member
relation subgroup_client_manage_role: role#member
relation subgroup_client_add_role_users: role#member
relation subgroup_client_remove_role_users: role#member
relation subgroup_client_view_role_users: role#member
relation subgroup_channel_update: role#member
relation subgroup_channel_read: role#member
relation subgroup_channel_delete: role#member
relation subgroup_channel_set_parent_group: role#member
relation subgroup_channel_connect_to_client: role#member
relation subgroup_channel_publish: role#member
relation subgroup_channel_subscribe: role#member
relation subgroup_channel_manage_role: role#member
relation subgroup_channel_add_role_users: role#member
relation subgroup_channel_remove_role_users: role#member
relation subgroup_channel_view_role_users: role#member
// Subgroup permission
permission subgroup_create_permission = subgroup_create + parent_group->subgroup_create_permission
permission subgroup_client_create_permission = subgroup_client_create + parent_group->subgroup_client_create_permission
permission subgroup_channel_create_permission = subgroup_channel_create + parent_group->subgroup_channel_create_permission
permission subgroup_update_permission = subgroup_update + parent_group->subgroup_update_permission
permission subgroup_membership_permission = subgroup_membership + parent_group->subgroup_membership_permission
permission subgroup_read_permission = subgroup_read + parent_group->subgroup_read_permission
permission subgroup_delete_permission = subgroup_delete + parent_group->subgroup_delete_permission
permission subgroup_set_child_permission = subgroup_set_child + parent_group->subgroup_set_child_permission
permission subgroup_set_parent_permission = subgroup_set_parent + parent_group->subgroup_set_parent_permission
permission subgroup_manage_role_permission = subgroup_manage_role + parent_group->subgroup_manage_role_permission
permission subgroup_add_role_users_permission = subgroup_add_role_users + parent_group->subgroup_add_role_users_permission
permission subgroup_remove_role_users_permission = subgroup_remove_role_users + parent_group->subgroup_remove_role_users_permission
permission subgroup_view_role_users_permission = subgroup_view_role_users + parent_group->subgroup_view_role_users_permission
// Group permission
permission update_permission = update + parent_group->subgroup_create_permission + domain->group_update_permission
permission membership_permission = membership + parent_group->subgroup_membership_permission + domain->group_membership_permission
permission read_permission = read + parent_group->subgroup_read_permission + domain->group_read_permission
permission delete_permission = delete + parent_group->subgroup_delete_permission + domain->group_delete_permission
permission set_child_permission = set_child + parent_group->subgroup_set_child_permission + domain->group_set_child_permission
permission set_parent_permission = set_parent + parent_group->subgroup_set_parent_permission + domain->group_set_parent_permission
permission manage_role_permission = manage_role + parent_group->subgroup_manage_role_permission + domain->group_manage_role_permission
permission add_role_users_permission = add_role_users + parent_group->subgroup_add_role_users_permission + domain->group_add_role_users_permission
permission remove_role_users_permission = remove_role_users + parent_group->subgroup_remove_role_users_permission + domain->group_remove_role_users_permission
permission view_role_users_permission = view_role_users + parent_group->subgroup_view_role_users_permission + domain->group_view_role_users_permission
// Subgroup clients permission
permission subgroup_client_update_permission = subgroup_client_update + parent_group->subgroup_client_update_permission
permission subgroup_client_read_permission = subgroup_client_read + parent_group->subgroup_client_read_permission
permission subgroup_client_delete_permission = subgroup_client_delete + parent_group->subgroup_client_delete_permission
permission subgroup_client_set_parent_group_permission = subgroup_client_set_parent_group + parent_group->subgroup_client_set_parent_group_permission
permission subgroup_client_connect_to_channel_permission = subgroup_client_connect_to_channel + parent_group->subgroup_client_connect_to_channel_permission
permission subgroup_client_manage_role_permission = subgroup_client_manage_role + parent_group->subgroup_client_manage_role_permission
permission subgroup_client_add_role_users_permission = subgroup_client_add_role_users + parent_group->subgroup_client_add_role_users_permission
permission subgroup_client_remove_role_users_permission = subgroup_client_remove_role_users + parent_group->subgroup_client_remove_role_users_permission
permission subgroup_client_view_role_users_permission = subgroup_client_view_role_users + parent_group->subgroup_client_view_role_users_permission
// Group clients permission
permission client_create_permission = client_create + parent_group->subgroup_client_create_permission + domain->client_create_permission
permission client_update_permission = client_update + parent_group->subgroup_client_update_permission + domain->client_update_permission
permission client_read_permission = client_read + parent_group->subgroup_client_read_permission + domain->client_read_permission
permission client_delete_permission = client_delete + parent_group->subgroup_client_delete_permission + domain->client_delete_permission
permission client_set_parent_group_permission = client_set_parent_group + parent_group->subgroup_client_set_parent_group_permission + domain->client_set_parent_group_permission
permission client_connect_to_channel_permission = client_connect_to_channel + parent_group->subgroup_client_connect_to_channel_permission + domain->client_connect_to_channel_permission
permission client_manage_role_permission = client_manage_role + parent_group->subgroup_client_manage_role_permission + domain->client_manage_role_permission
permission client_add_role_users_permission = client_add_role_users + parent_group->subgroup_client_add_role_users_permission + domain->client_add_role_users_permission
permission client_remove_role_users_permission = client_remove_role_users + parent_group->subgroup_client_remove_role_users_permission + domain->client_remove_role_users_permission
permission client_view_role_users_permission = client_view_role_users + parent_group->subgroup_client_view_role_users_permission + domain->client_view_role_users_permission
// Subgroup channels permission
permission subgroup_channel_update_permission = subgroup_channel_update + parent_group->subgroup_channel_update_permission
permission subgroup_channel_read_permission = subgroup_channel_read + parent_group->subgroup_channel_read_permission
permission subgroup_channel_delete_permission = subgroup_channel_delete + parent_group->subgroup_channel_delete_permission
permission subgroup_channel_set_parent_group_permission = subgroup_channel_set_parent_group + parent_group->subgroup_channel_set_parent_group_permission
permission subgroup_channel_connect_to_client_permission = subgroup_channel_connect_to_client + parent_group->subgroup_channel_connect_to_client_permission
permission subgroup_channel_publish_permission = subgroup_channel_publish + parent_group->subgroup_channel_publish_permission
permission subgroup_channel_subscribe_permission = subgroup_channel_subscribe + parent_group->subgroup_channel_subscribe_permission
permission subgroup_channel_manage_role_permission = subgroup_channel_manage_role + parent_group->subgroup_channel_manage_role_permission
permission subgroup_channel_add_role_users_permission = subgroup_channel_add_role_users + parent_group->subgroup_channel_add_role_users_permission
permission subgroup_channel_remove_role_users_permission = subgroup_channel_remove_role_users + parent_group->subgroup_channel_remove_role_users_permission
permission subgroup_channel_view_role_users_permission = subgroup_channel_view_role_users + parent_group->subgroup_channel_view_role_users_permission
// Group channels permission
permission channel_create_permission = channel_create + parent_group->subgroup_channel_create_permission + domain->channel_create_permission
permission channel_update_permission = channel_update + parent_group->subgroup_channel_update_permission + domain->channel_update_permission
permission channel_read_permission = channel_read + parent_group->subgroup_channel_read_permission + domain->channel_read_permission
permission channel_delete_permission = channel_delete + parent_group->subgroup_channel_delete_permission + domain->channel_delete_permission
permission channel_set_parent_group_permission = channel_set_parent_group + parent_group->subgroup_channel_set_parent_group_permission + domain->channel_set_parent_group_permission
permission channel_connect_to_client_permission = channel_connect_to_client + parent_group->subgroup_channel_connect_to_client_permission + domain->channel_connect_to_client_permission
permission channel_publish_permission = channel_publish + parent_group->subgroup_channel_publish_permission + domain->channel_publish_permission
permission channel_subscribe_permission = channel_subscribe + parent_group->subgroup_channel_subscribe_permission + domain->channel_subscribe_permission
permission channel_manage_role_permission = channel_manage_role + parent_group->channel_manage_role_permission + domain->channel_manage_role_permission
permission channel_add_role_users_permission = channel_add_role_users + parent_group->channel_add_role_users_permission + domain->channel_add_role_users_permission
permission channel_remove_role_users_permission = channel_remove_role_users + parent_group->channel_remove_role_users_permission + domain->channel_remove_role_users_permission
permission channel_view_role_users_permission = channel_view_role_users + parent_group->channel_view_role_users_permission + domain->channel_view_role_users_permission
}
definition domain {
//Replace platform with organization in future
relation organization: platform
relation team: team
relation update: role#member | team#member
relation enable: role#member | team#member
relation disable: role#member | team#member
relation read: role#member | team#member
relation delete: role#member | team#member
relation manage_role: role#member | team#member
relation add_role_users: role#member | team#member
relation remove_role_users: role#member | team#member
relation view_role_users: role#member | team#member
relation client_create: role#member | team#member
relation channel_create: role#member | team#member
relation group_create: role#member | team#member
relation client_update: role#member | team#member
relation client_read: role#member | team#member
relation client_delete: role#member | team#member
relation client_set_parent_group: role#member | team#member
relation client_connect_to_channel: role#member | team#member
relation client_manage_role: role#member | team#member
relation client_add_role_users: role#member | team#member
relation client_remove_role_users: role#member | team#member
relation client_view_role_users: role#member | team#member
relation channel_update: role#member | team#member
relation channel_read: role#member | team#member
relation channel_delete: role#member | team#member
relation channel_set_parent_group: role#member | team#member
relation channel_connect_to_client: role#member | team#member
relation channel_publish: role#member | team#member
relation channel_subscribe: role#member | team#member
relation channel_manage_role: role#member | team#member
relation channel_add_role_users: role#member | team#member
relation channel_remove_role_users: role#member | team#member
relation channel_view_role_users: role#member | team#member
relation group_update: role#member | team#member
relation group_membership: role#member | team#member
relation group_read: role#member | team#member
relation group_delete: role#member | team#member
relation group_set_child: role#member | team#member
relation group_set_parent: role#member | team#member
relation group_manage_role: role#member | team#member
relation group_add_role_users: role#member | team#member
relation group_remove_role_users: role#member | team#member
relation group_view_role_users: role#member | team#member
// Magistrala-specific relations
relation alarm_create: role#member | team#member
relation alarm_update: role#member | team#member
relation alarm_read: role#member | team#member
relation alarm_delete: role#member | team#member
relation alarm_manage_role: role#member | team#member
relation alarm_add_role_users: role#member | team#member
relation alarm_remove_role_users: role#member | team#member
relation alarm_view_role_users: role#member | team#member
relation rule_create: role#member | team#member
relation rule_update: role#member | team#member
relation rule_read: role#member | team#member
relation rule_delete: role#member | team#member
relation rule_manage_role: role#member | team#member
relation rule_add_role_users: role#member | team#member
relation rule_remove_role_users: role#member | team#member
relation rule_view_role_users: role#member | team#member
relation report_create: role#member | team#member
relation report_update: role#member | team#member
relation report_read: role#member | team#member
relation report_delete: role#member | team#member
relation report_manage_role: role#member | team#member
relation report_add_role_users: role#member | team#member
relation report_remove_role_users: role#member | team#member
relation report_view_role_users: role#member | team#member
permission update_permission = update + team->domain_update + organization->admin
permission read_permission = read + team->domain_read + organization->admin
permission enable_permission = enable + team->domain_update + organization->admin
permission disable_permission = disable + team->domain_update + organization->admin
permission delete_permission = delete + team->domain_delete + organization->admin
permission manage_role_permission = manage_role + team->domain_manage_role + organization->admin
permission add_role_users_permission = add_role_users + team->domain_add_role_users + organization->admin
permission remove_role_users_permission = remove_role_users + team->domain_remove_role_users + organization->admin
permission view_role_users_permission = view_role_users + team->domain_view_role_users + organization->admin
permission membership = read + update + enable + disable + delete +
manage_role + add_role_users + remove_role_users + view_role_users +
client_create + channel_create + group_create +
client_update + client_read + client_delete + client_set_parent_group + client_connect_to_channel +
client_manage_role + client_add_role_users + client_remove_role_users + client_view_role_users +
channel_update + channel_read + channel_delete + channel_set_parent_group + channel_connect_to_client + channel_publish + channel_subscribe +
channel_manage_role + channel_add_role_users + channel_remove_role_users + channel_view_role_users +
group_update + group_membership + group_read + group_delete + group_set_child + group_set_parent +
group_manage_role + group_add_role_users + group_remove_role_users + group_view_role_users +
alarm_create + alarm_update + alarm_read + alarm_delete + alarm_manage_role + alarm_add_role_users + alarm_remove_role_users + alarm_view_role_users + rule_create + rule_update + rule_read + rule_delete + rule_manage_role + rule_add_role_users + rule_remove_role_users + rule_view_role_users + report_create + report_update + report_read + report_delete + report_manage_role + report_add_role_users + report_remove_role_users + report_view_role_users +
organization->admin
permission admin = (read & update & enable & disable & delete & manage_role & add_role_users & remove_role_users & view_role_users) + organization->admin
permission client_create_permission = client_create + team->client_create + organization->admin
permission channel_create_permission = channel_create + team->channel_create + organization->admin
permission group_create_permission = group_create + team->group_create + organization->admin
permission client_update_permission = client_update + team->client_update + organization->admin
permission client_read_permission = client_read + team->client_read + organization->admin
permission client_delete_permission = client_delete + team->client_delete + organization->admin
permission client_set_parent_group_permission = client_set_parent_group + team->client_set_parent_group + organization->admin
permission client_connect_to_channel_permission = client_connect_to_channel + team->client_connect_to_channel + organization->admin
permission client_manage_role_permission = client_manage_role + team->client_manage_role + organization->admin
permission client_add_role_users_permission = client_add_role_users + team->client_add_role_users + organization->admin
permission client_remove_role_users_permission = client_remove_role_users + team->client_remove_role_users + organization->admin
permission client_view_role_users_permission = client_view_role_users + team->client_view_role_users + organization->admin
permission channel_update_permission = channel_update + team->channel_update + organization->admin
permission channel_read_permission = channel_read + team->channel_read + organization->admin
permission channel_delete_permission = channel_delete + team->channel_delete + organization->admin
permission channel_set_parent_group_permission = channel_set_parent_group + team->channel_set_parent_group + organization->admin
permission channel_connect_to_client_permission = channel_connect_to_client + team->channel_connect_to_client + organization->admin
permission channel_publish_permission = channel_publish + team->channel_publish + organization->admin
permission channel_subscribe_permission = channel_subscribe + team->channel_subscribe + organization->admin
permission channel_manage_role_permission = channel_manage_role + team->channel_manage_role + organization->admin
permission channel_add_role_users_permission = channel_add_role_users + team->channel_add_role_users + organization->admin
permission channel_remove_role_users_permission = channel_remove_role_users + team->channel_remove_role_users + organization->admin
permission channel_view_role_users_permission = channel_view_role_users + team->channel_view_role_users + organization->admin
permission group_update_permission = group_update + team->group_update + organization->admin
permission group_membership_permission = group_membership + team->group_membership + organization->admin
permission group_read_permission = group_read + team->group_read + organization->admin
permission group_delete_permission = group_delete + team->group_delete + organization->admin
permission group_set_child_permission = group_set_child + team->group_set_child + organization->admin
permission group_set_parent_permission = group_set_parent + team->group_set_parent + organization->admin
permission group_manage_role_permission = group_manage_role + team->group_manage_role + organization->admin
permission group_add_role_users_permission = group_add_role_users + team->group_add_role_users + organization->admin
permission group_remove_role_users_permission = group_remove_role_users + team->group_remove_role_users + organization->admin
permission group_view_role_users_permission = group_view_role_users + team->group_view_role_users + organization->admin
// Magistrala-specific permissions
permission alarm_create_permission = alarm_create + team->alarm_create + organization->admin
permission alarm_update_permission = alarm_update + team->alarm_update + organization->admin
permission alarm_read_permission = alarm_read + team->alarm_read + organization->admin
permission alarm_delete_permission = alarm_delete + team->alarm_delete + organization->admin
permission alarm_manage_role_permission = alarm_manage_role + team->alarm_manage_role + organization->admin
permission alarm_add_role_users_permission = alarm_add_role_users + team->alarm_add_role_users + organization->admin
permission alarm_remove_role_users_permission = alarm_remove_role_users + team->alarm_remove_role_users + organization->admin
permission alarm_view_role_users_permission = alarm_view_role_users + team->alarm_view_role_users + organization->admin
permission rule_create_permission = rule_create + team->rule_create + organization->admin
permission rule_update_permission = rule_update + team->rule_update + organization->admin
permission rule_read_permission = rule_read + team->rule_read + organization->admin
permission rule_delete_permission = rule_delete + team->rule_delete + organization->admin
permission rule_manage_role_permission = rule_manage_role + team->rule_manage_role + organization->admin
permission rule_add_role_users_permission = rule_add_role_users + team->rule_add_role_users + organization->admin
permission rule_remove_role_users_permission = rule_remove_role_users + team->rule_remove_role_users + organization->admin
permission rule_view_role_users_permission = rule_view_role_users + team->rule_view_role_users + organization->admin
permission report_create_permission = report_create + team->report_create + organization->admin
permission report_update_permission = report_update + team->report_update + organization->admin
permission report_read_permission = report_read + team->report_read + organization->admin
permission report_delete_permission = report_delete + team->report_delete + organization->admin
permission report_manage_role_permission = report_manage_role + team->report_manage_role + organization->admin
permission report_add_role_users_permission = report_add_role_users + team->report_add_role_users + organization->admin
permission report_remove_role_users_permission = report_remove_role_users + team->report_remove_role_users + organization->admin
permission report_view_role_users_permission = report_view_role_users + team->report_view_role_users + organization->admin
}
// Add this relation and permission in future while adding organization
definition team {
relation organization: organization
relation parent_team: team
relation delete: role#member
relation enable: role#member | team#member
relation disable: role#member | team#member
relation update: role#member
relation read: role#member
relation set_parent: role#member
relation set_child: role#member
relation member: role#member
relation manage_role: role#member
relation add_role_users: role#member
relation remove_role_users: role#member
relation view_role_users: role#member
relation subteam_delete: role#member
relation subteam_update: role#member
relation subteam_read: role#member
relation subteam_member: role#member
relation subteam_set_child: role#member
relation subteam_set_parent: role#member
relation subteam_manage_role: role#member
relation subteam_add_role_users: role#member
relation subteam_remove_role_users: role#member
relation subteam_view_role_users: role#member
// Domain related permission
relation domain_update: role#member | team#member
relation domain_read: role#member | team#member
relation domain_membership: role#member | team#member
relation domain_delete: role#member | team#member
relation domain_manage_role: role#member | team#member
relation domain_add_role_users: role#member | team#member
relation domain_remove_role_users: role#member | team#member
relation domain_view_role_users: role#member | team#member
relation client_create: role#member | team#member
relation channel_create: role#member | team#member
relation group_create: role#member | team#member
relation client_update: role#member | team#member
relation client_read: role#member | team#member
relation client_delete: role#member | team#member
relation client_set_parent_group: role#member | team#member
relation client_connect_to_channel: role#member | team#member
relation client_manage_role: role#member | team#member
relation client_add_role_users: role#member | team#member
relation client_remove_role_users: role#member | team#member
relation client_view_role_users: role#member | team#member
relation channel_update: role#member | team#member
relation channel_read: role#member | team#member
relation channel_delete: role#member | team#member
relation channel_set_parent_group: role#member | team#member
relation channel_connect_to_client: role#member | team#member
relation channel_publish: role#member | team#member
relation channel_subscribe: role#member | team#member
relation channel_manage_role: role#member | team#member
relation channel_add_role_users: role#member | team#member
relation channel_remove_role_users: role#member | team#member
relation channel_view_role_users: role#member | team#member
relation group_update: role#member | team#member
relation group_membership: role#member | team#member
relation group_read: role#member | team#member
relation group_delete: role#member | team#member
relation group_set_child: role#member | team#member
relation group_set_parent: role#member | team#member
relation group_manage_role: role#member | team#member
relation group_add_role_users: role#member | team#member
relation group_remove_role_users: role#member | team#member
relation group_view_role_users: role#member | team#member
// Magistrala-specific relations
relation alarm_create: role#member | team#member
relation alarm_update: role#member | team#member
relation alarm_read: role#member | team#member
relation alarm_delete: role#member | team#member
relation alarm_manage_role: role#member | team#member
relation alarm_add_role_users: role#member | team#member
relation alarm_remove_role_users: role#member | team#member
relation alarm_view_role_users: role#member | team#member
relation rule_create: role#member | team#member
relation rule_update: role#member | team#member
relation rule_read: role#member | team#member
relation rule_delete: role#member | team#member
relation rule_manage_role: role#member | team#member
relation rule_add_role_users: role#member | team#member
relation rule_remove_role_users: role#member | team#member
relation rule_view_role_users: role#member | team#member
relation report_create: role#member | team#member
relation report_update: role#member | team#member
relation report_read: role#member | team#member
relation report_delete: role#member | team#member
relation report_manage_role: role#member | team#member
relation report_add_role_users: role#member | team#member
relation report_remove_role_users: role#member | team#member
relation report_view_role_users: role#member | team#member
permission delete_permission = delete + organization->team_delete + parent_team->subteam_delete + organization->admin
permission update_permission = update + organization->team_update + parent_team->subteam_update + organization->admin
permission read_permission = read + organization->team_read + parent_team->subteam_read + organization->admin
permission set_parent_permission = set_parent + organization->team_set_parent + parent_team->subteam_set_parent + organization->admin
permission set_child_permisssion = set_child + organization->team_set_child + parent_team->subteam_set_child + organization->admin
permission membership = member + organization->team_member + parent_team->subteam_member + organization->admin
permission manage_role_permission = manage_role + organization->team_manage_role + parent_team->subteam_manage_role + organization->admin
permission add_role_users_permission = add_role_users + organization->team_add_role_users + parent_team->subteam_add_role_users + organization->admin
permission remove_role_users_permission = remove_role_users + organization->team_remove_role_users + parent_team->subteam_remove_role_users + organization->admin
permission view_role_users_permission = view_role_users + organization->team_view_role_users + parent_team->subteam_view_role_users + organization->admin
}
definition organization {
relation platform: platform
relation administrator: user
relation delete: role#member
relation update: role#member
relation read: role#member
relation member: role#member
relation manage_role: role#member
relation add_role_users: role#member
relation remove_role_users: role#member
relation view_role_users: role#member
relation team_create: role#member
relation team_delete: role#member
relation team_update: role#member
relation team_read: role#member
relation team_member: role#member // Will be member of all the teams in the organization
relation team_set_child: role#member
relation team_set_parent: role#member
relation team_manage_role: role#member
relation team_add_role_users: role#member
relation team_remove_role_users: role#member
relation team_view_role_users: role#member
permission admin = administrator + platform->administrator
permission delete_permission = admin + delete->member
permission update_permission = admin + update->member
permission read_permission = admin + read->member
permission membership = admin + member->member
permission team_create_permission = admin + team_create->member
permission manage_role_permission = admin + manage_role
permission add_role_users_permisson = admin + add_role_users
permission remove_role_users_permission = admin + remove_role_users
permission view_role_users_permission = admin + view_role_users
}
definition platform {
relation administrator: user
relation member: user
permission admin = administrator
permission membership = administrator + member
}
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
// Merge documentation
// - Source A (base): docker/supermq-docker/spicedb/schema.zed (SuperMQ upstream schema)
// - Source B (overlay): docker/spicedb/override-schema.zed (Magistrala schema extensions)
// - Merge script: scripts/combined-schema.sh
// - Output: docker/spicedb/combined-schema.zed
//
// How merge works:
// 1. The first `definition domain { ... }` block is treated as explicit domain overlay.
// 2. The first `definition team { ... }` block is treated as explicit team overlay.
// 3. Domain overlay relations/permissions are injected into SuperMQ `definition domain`.
// 4. Team overlay relations are injected into SuperMQ `definition team`.
// 5. `permission membership_extension = ...` from the domain overlay is injected into
// SuperMQ `domain.permission membership` before `organization->admin`.
// 6. Overlay `definition domain` and `definition team` blocks are removed before append,
// so only merged SuperMQ domain/team definitions remain.
// 7. Remaining definitions in this file (for example `alarm`, `rule`, `report`) are appended.
//
// Maintenance notes:
// - Keep all custom domain/team merge lines inside the two overlay blocks below.
// - Update `permission membership_extension` whenever domain membership additions change.
// - Regenerate combined schema with: `sh scripts/combine-schema.sh`
// - `scripts/supermq.sh` also regenerates combined schema after refreshing SuperMQ docker files.
// Overlay domain block consumed by scripts/combine-schema.sh during merge.
// Overlay team block consumed by scripts/combine-schema.sh during merge.
definition alarm {
relation domain: domain
relation update: role#member
relation read: role#member
relation delete: role#member
relation manage_role: role#member
relation add_role_users: role#member
relation remove_role_users: role#member
relation view_role_users: role#member
permission update_permission = update + domain->alarm_update_permission
permission read_permission = read + domain->alarm_read_permission
permission delete_permission = delete + domain->alarm_delete_permission
permission manage_role_permission = manage_role + domain->alarm_manage_role_permission
permission add_role_users_permission = add_role_users + domain->alarm_add_role_users_permission
permission remove_role_users_permission = remove_role_users + domain->alarm_remove_role_users_permission
permission view_role_users_permission = view_role_users + domain->alarm_view_role_users_permission
}
definition rule {
relation domain: domain
relation update: role#member
relation read: role#member
relation delete: role#member
relation manage_role: role#member
relation add_role_users: role#member
relation remove_role_users: role#member
relation view_role_users: role#member
permission update_permission = update + domain->rule_update_permission
permission read_permission = read + domain->rule_read_permission
permission delete_permission = delete + domain->rule_delete_permission
permission manage_role_permission = manage_role + domain->rule_manage_role_permission
permission add_role_users_permission = add_role_users + domain->rule_add_role_users_permission
permission remove_role_users_permission = remove_role_users + domain->rule_remove_role_users_permission
permission view_role_users_permission = view_role_users + domain->rule_view_role_users_permission
}
definition report {
relation domain: domain
relation update: role#member
relation read: role#member
relation delete: role#member
relation manage_role: role#member
relation add_role_users: role#member
relation remove_role_users: role#member
relation view_role_users: role#member
permission update_permission = update + domain->report_update_permission
permission read_permission = read + domain->report_read_permission
permission delete_permission = delete + domain->report_delete_permission
permission manage_role_permission = manage_role + domain->report_manage_role_permission
permission add_role_users_permission = add_role_users + domain->report_add_role_users_permission
permission remove_role_users_permission = remove_role_users + domain->report_remove_role_users_permission
permission view_role_users_permission = view_role_users + domain->report_view_role_users_permission
}
+187
View File
@@ -0,0 +1,187 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
// Merge documentation
// - Source A (base): docker/supermq-docker/spicedb/schema.zed (SuperMQ upstream schema)
// - Source B (overlay): docker/spicedb/override-schema.zed (Magistrala schema extensions)
// - Merge script: scripts/combined-schema.sh
// - Output: docker/spicedb/combined-schema.zed
//
// How merge works:
// 1. The first `definition domain { ... }` block is treated as explicit domain overlay.
// 2. The first `definition team { ... }` block is treated as explicit team overlay.
// 3. Domain overlay relations/permissions are injected into SuperMQ `definition domain`.
// 4. Team overlay relations are injected into SuperMQ `definition team`.
// 5. `permission membership_extension = ...` from the domain overlay is injected into
// SuperMQ `domain.permission membership` before `organization->admin`.
// 6. Overlay `definition domain` and `definition team` blocks are removed before append,
// so only merged SuperMQ domain/team definitions remain.
// 7. Remaining definitions in this file (for example `alarm`, `rule`, `report`) are appended.
//
// Maintenance notes:
// - Keep all custom domain/team merge lines inside the two overlay blocks below.
// - Update `permission membership_extension` whenever domain membership additions change.
// - Regenerate combined schema with: `sh scripts/combine-schema.sh`
// - `scripts/supermq.sh` also regenerates combined schema after refreshing SuperMQ docker files.
// Overlay domain block consumed by scripts/combine-schema.sh during merge.
definition domain {
// Magistrala-specific relations
relation alarm_create: role#member | team#member
relation alarm_update: role#member | team#member
relation alarm_read: role#member | team#member
relation alarm_delete: role#member | team#member
relation alarm_manage_role: role#member | team#member
relation alarm_add_role_users: role#member | team#member
relation alarm_remove_role_users: role#member | team#member
relation alarm_view_role_users: role#member | team#member
relation rule_create: role#member | team#member
relation rule_update: role#member | team#member
relation rule_read: role#member | team#member
relation rule_delete: role#member | team#member
relation rule_manage_role: role#member | team#member
relation rule_add_role_users: role#member | team#member
relation rule_remove_role_users: role#member | team#member
relation rule_view_role_users: role#member | team#member
relation report_create: role#member | team#member
relation report_update: role#member | team#member
relation report_read: role#member | team#member
relation report_delete: role#member | team#member
relation report_manage_role: role#member | team#member
relation report_add_role_users: role#member | team#member
relation report_remove_role_users: role#member | team#member
relation report_view_role_users: role#member | team#member
// Magistrala-specific permissions
permission alarm_create_permission = alarm_create + team->alarm_create + organization->admin
permission alarm_update_permission = alarm_update + team->alarm_update + organization->admin
permission alarm_read_permission = alarm_read + team->alarm_read + organization->admin
permission alarm_delete_permission = alarm_delete + team->alarm_delete + organization->admin
permission alarm_manage_role_permission = alarm_manage_role + team->alarm_manage_role + organization->admin
permission alarm_add_role_users_permission = alarm_add_role_users + team->alarm_add_role_users + organization->admin
permission alarm_remove_role_users_permission = alarm_remove_role_users + team->alarm_remove_role_users + organization->admin
permission alarm_view_role_users_permission = alarm_view_role_users + team->alarm_view_role_users + organization->admin
permission rule_create_permission = rule_create + team->rule_create + organization->admin
permission rule_update_permission = rule_update + team->rule_update + organization->admin
permission rule_read_permission = rule_read + team->rule_read + organization->admin
permission rule_delete_permission = rule_delete + team->rule_delete + organization->admin
permission rule_manage_role_permission = rule_manage_role + team->rule_manage_role + organization->admin
permission rule_add_role_users_permission = rule_add_role_users + team->rule_add_role_users + organization->admin
permission rule_remove_role_users_permission = rule_remove_role_users + team->rule_remove_role_users + organization->admin
permission rule_view_role_users_permission = rule_view_role_users + team->rule_view_role_users + organization->admin
permission report_create_permission = report_create + team->report_create + organization->admin
permission report_update_permission = report_update + team->report_update + organization->admin
permission report_read_permission = report_read + team->report_read + organization->admin
permission report_delete_permission = report_delete + team->report_delete + organization->admin
permission report_manage_role_permission = report_manage_role + team->report_manage_role + organization->admin
permission report_add_role_users_permission = report_add_role_users + team->report_add_role_users + organization->admin
permission report_remove_role_users_permission = report_remove_role_users + team->report_remove_role_users + organization->admin
permission report_view_role_users_permission = report_view_role_users + team->report_view_role_users + organization->admin
// Explicit extension injected into SuperMQ domain `permission membership`.
permission membership_extension = alarm_create + alarm_update + alarm_read + alarm_delete + alarm_manage_role + alarm_add_role_users + alarm_remove_role_users + alarm_view_role_users + rule_create + rule_update + rule_read + rule_delete + rule_manage_role + rule_add_role_users + rule_remove_role_users + rule_view_role_users + report_create + report_update + report_read + report_delete + report_manage_role + report_add_role_users + report_remove_role_users + report_view_role_users
}
// Overlay team block consumed by scripts/combine-schema.sh during merge.
definition team {
relation alarm_create: role#member | team#member
relation alarm_update: role#member | team#member
relation alarm_read: role#member | team#member
relation alarm_delete: role#member | team#member
relation alarm_manage_role: role#member | team#member
relation alarm_add_role_users: role#member | team#member
relation alarm_remove_role_users: role#member | team#member
relation alarm_view_role_users: role#member | team#member
relation rule_create: role#member | team#member
relation rule_update: role#member | team#member
relation rule_read: role#member | team#member
relation rule_delete: role#member | team#member
relation rule_manage_role: role#member | team#member
relation rule_add_role_users: role#member | team#member
relation rule_remove_role_users: role#member | team#member
relation rule_view_role_users: role#member | team#member
relation report_create: role#member | team#member
relation report_update: role#member | team#member
relation report_read: role#member | team#member
relation report_delete: role#member | team#member
relation report_manage_role: role#member | team#member
relation report_add_role_users: role#member | team#member
relation report_remove_role_users: role#member | team#member
relation report_view_role_users: role#member | team#member
}
definition alarm {
relation domain: domain
relation update: role#member
relation read: role#member
relation delete: role#member
relation manage_role: role#member
relation add_role_users: role#member
relation remove_role_users: role#member
relation view_role_users: role#member
permission update_permission = update + domain->alarm_update_permission
permission read_permission = read + domain->alarm_read_permission
permission delete_permission = delete + domain->alarm_delete_permission
permission manage_role_permission = manage_role + domain->alarm_manage_role_permission
permission add_role_users_permission = add_role_users + domain->alarm_add_role_users_permission
permission remove_role_users_permission = remove_role_users + domain->alarm_remove_role_users_permission
permission view_role_users_permission = view_role_users + domain->alarm_view_role_users_permission
}
definition rule {
relation domain: domain
relation update: role#member
relation read: role#member
relation delete: role#member
relation manage_role: role#member
relation add_role_users: role#member
relation remove_role_users: role#member
relation view_role_users: role#member
permission update_permission = update + domain->rule_update_permission
permission read_permission = read + domain->rule_read_permission
permission delete_permission = delete + domain->rule_delete_permission
permission manage_role_permission = manage_role + domain->rule_manage_role_permission
permission add_role_users_permission = add_role_users + domain->rule_add_role_users_permission
permission remove_role_users_permission = remove_role_users + domain->rule_remove_role_users_permission
permission view_role_users_permission = view_role_users + domain->rule_view_role_users_permission
}
definition report {
relation domain: domain
relation update: role#member
relation read: role#member
relation delete: role#member
relation manage_role: role#member
relation add_role_users: role#member
relation remove_role_users: role#member
relation view_role_users: role#member
permission update_permission = update + domain->report_update_permission
permission read_permission = read + domain->report_read_permission
permission delete_permission = delete + domain->report_delete_permission
permission manage_role_permission = manage_role + domain->report_manage_role_permission
permission add_role_users_permission = add_role_users + domain->report_add_role_users_permission
permission remove_role_users_permission = remove_role_users + domain->report_remove_role_users_permission
permission view_role_users_permission = view_role_users + domain->report_view_role_users_permission
}
+19 -3
View File
@@ -2,9 +2,12 @@
# SPDX-License-Identifier: Apache-2.0
services:
spicedb:
networks: !override
- magistrala-base-net
volumes:
- ../spicedb/combined-schema.zed:${SMQ_SPICEDB_SCHEMA_FILE}
spicedb-migrate:
networks: !override
@@ -17,7 +20,8 @@ services:
auth-db:
networks: !override
- magistrala-base-net
volumes:
- ../spicedb/combined-schema.zed:${SMQ_SPICEDB_SCHEMA_FILE}
auth-redis:
networks: !override
- magistrala-base-net
@@ -25,6 +29,8 @@ services:
auth:
networks: !override
- magistrala-base-net
volumes:
- ../spicedb/combined-schema.zed:${SMQ_SPICEDB_SCHEMA_FILE}
domains-db:
networks: !override
@@ -37,6 +43,8 @@ services:
domains:
networks: !override
- magistrala-base-net
volumes:
- ../spicedb/combined-schema.zed:${SMQ_SPICEDB_SCHEMA_FILE}
clients-db:
networks: !override
@@ -49,6 +57,8 @@ services:
clients:
networks: !override
- magistrala-base-net
volumes:
- ../spicedb/combined-schema.zed:${SMQ_SPICEDB_SCHEMA_FILE}
channels-redis:
networks: !override
@@ -61,6 +71,8 @@ services:
channels:
networks: !override
- magistrala-base-net
volumes:
- ../spicedb/combined-schema.zed:${SMQ_SPICEDB_SCHEMA_FILE}
users-db:
networks: !override
@@ -77,6 +89,8 @@ services:
groups:
networks: !override
- magistrala-base-net
volumes:
- ../spicedb/combined-schema.zed:${SMQ_SPICEDB_SCHEMA_FILE}
jaeger:
networks: !override
@@ -170,10 +184,10 @@ services:
AM_CERTS_OPENBAO_UNSEAL_KEY_2: ${AM_CERTS_OPENBAO_UNSEAL_KEY_2}
AM_CERTS_OPENBAO_UNSEAL_KEY_3: ${AM_CERTS_OPENBAO_UNSEAL_KEY_3}
AM_CERTS_OPENBAO_ROOT_TOKEN: ${AM_CERTS_OPENBAO_ROOT_TOKEN}
AM_JAEGER_URL: ${AM_JAEGER_URL}
AM_JAEGER_TRACE_RATIO: ${AM_JAEGER_TRACE_RATIO}
AM_AUTH_GRPC_URL: ${AM_AUTH_GRPC_URL}
AM_AUTH_GRPC_TIMEOUT: ${AM_AUTH_GRPC_TIMEOUT}
AM_AUTH_GRPC_CLIENT_CERT: ${AM_AUTH_GRPC_CLIENT_CERT}
@@ -209,3 +223,5 @@ services:
env_file: !override
- ./.env
- ../../docker/.env
+24 -12
View File
@@ -1,12 +1,12 @@
module github.com/absmach/magistrala
go 1.25.7
go 1.26.0
require (
github.com/0x6flab/namegenerator v1.4.0
github.com/absmach/callhome v0.18.2
github.com/absmach/certs v0.18.5
github.com/absmach/supermq v0.18.6-0.20260226153547-15a6c026e943
github.com/absmach/supermq v0.19.0
github.com/authzed/authzed-go v1.8.0
github.com/authzed/grpcutil v0.0.0-20250221190651-1985b19b35b8
github.com/caarlos0/env/v11 v11.4.0
@@ -36,9 +36,9 @@ require (
github.com/traefik/yaegi v0.16.1
github.com/vadv/gopher-lua-libs v0.8.0
github.com/yuin/gopher-lua v1.1.1
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0
go.opentelemetry.io/otel v1.40.0
go.opentelemetry.io/otel/trace v1.40.0
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.66.0
go.opentelemetry.io/otel v1.41.0
go.opentelemetry.io/otel/trace v1.41.0
golang.org/x/sync v0.19.0
gonum.org/v1/gonum v0.17.0
google.golang.org/grpc v1.79.1
@@ -47,13 +47,25 @@ require (
moul.io/http2curl v1.0.0
)
require go.uber.org/atomic v1.11.0 // indirect
require (
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
github.com/authzed/cel-go v0.20.2 // indirect
github.com/authzed/spicedb v1.49.2 // indirect
github.com/ccoveille/go-safecast/v2 v2.0.0 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/go-errors/errors v1.5.1 // indirect
github.com/go-logr/zerologr v1.2.3 // indirect
github.com/rs/zerolog v1.34.0 // indirect
github.com/stoewer/go-strcase v1.3.1 // indirect
go.uber.org/atomic v1.11.0 // indirect
golang.org/x/exp v0.0.0-20251017212417-90e834f514db // indirect
sigs.k8s.io/controller-runtime v0.22.4 // indirect
)
require (
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20251209175733-2a1774d88802.1 // indirect
cloud.google.com/go/compute/metadata v0.9.0 // indirect
dario.cat/mergo v1.0.2 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
filippo.io/edwards25519 v1.1.1 // indirect
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
@@ -115,7 +127,7 @@ require (
github.com/planetscale/vtprotobuf v0.6.1-0.20240917153116-6f2963f01587 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.67.4 // indirect
github.com/prometheus/common v0.67.5 // indirect
github.com/prometheus/procfs v0.19.2 // indirect
github.com/rabbitmq/amqp091-go v1.10.0 // indirect
github.com/sagikazarmark/locafero v0.12.0 // indirect
@@ -132,10 +144,10 @@ require (
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.65.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.41.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0 // indirect
go.opentelemetry.io/otel/metric v1.40.0 // indirect
go.opentelemetry.io/otel/sdk v1.40.0 // indirect
go.opentelemetry.io/otel/metric v1.41.0 // indirect
go.opentelemetry.io/otel/sdk v1.41.0 // indirect
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
go.yaml.in/yaml/v2 v2.4.3 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
+66 -21
View File
@@ -3,18 +3,23 @@ buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-202512091757
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20251209175733-2a1774d88802.1/go.mod h1:tvtbpgaVXZX4g6Pn+AnzFycuRK3MOz5HJfEGeEllXYM=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.122.0 h1:0JTLGrcSIs3HIGsgVPvTx3cfyFSP/k9CI8vLPHTd6Wc=
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
filippo.io/edwards25519 v1.1.1 h1:YpjwWWlNmGIDyXOn8zLzqiD+9TyIlPhGFG96P39uBpw=
filippo.io/edwards25519 v1.1.1/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/0x6flab/namegenerator v1.4.0 h1:QnkI813SZsI/hYnKD9pg3mkIlcYzCx0N4hnzb0YYME4=
github.com/0x6flab/namegenerator v1.4.0/go.mod h1:2sQzXuS6dX/KEwWtB6GJU729O3m4gBdD5oAU8hd0SyY=
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
@@ -30,17 +35,23 @@ github.com/absmach/mgate v0.5.0 h1:RV2Aalra3xIm+XTs13TM7iE7v4WTL2SKhKcPbKr22Ac=
github.com/absmach/mgate v0.5.0/go.mod h1:0KVq7mxM0wayosmyXPPxp1EL0c2d9kRp5V8NZCKdetA=
github.com/absmach/senml v1.0.8 h1:+opem/r4g6c6eA/JLyCIuksyEhj7eBdysY3pEmy1mqo=
github.com/absmach/senml v1.0.8/go.mod h1:DRhzHLgvQoIUHroBgpFrSWso+bJZO9E96RlHAHy+VRI=
github.com/absmach/supermq v0.18.6-0.20260226153547-15a6c026e943 h1:DbQ0t46WNqMVHcks8D4XcMIYU3m5lZ6hmbhr2Wcln+Y=
github.com/absmach/supermq v0.18.6-0.20260226153547-15a6c026e943/go.mod h1:VLInclhsnLm/mo1qng3ac7T0bJnteqH/y2mPxML6H+4=
github.com/absmach/supermq v0.19.0 h1:sbqfzmSiMp9GEaCWgpREiLC0tFsSntgLIyAaZs7SnRY=
github.com/absmach/supermq v0.19.0/go.mod h1:SG2yIzlJmc26ZjDVSkoapc6HZ6W13SUsaN3sAErfgC4=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ=
github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw=
github.com/authzed/authzed-go v1.8.0 h1:cRka8J8QXGl+nyNrhsiPSFJUluIG1tuTXnG8ad2LZ1Y=
github.com/authzed/authzed-go v1.8.0/go.mod h1:WC3x/SuVvclBlDYMg9V7e5c/J/KGGwG+cSw2WQBbodk=
github.com/authzed/cel-go v0.20.2 h1:GlmLecGry7Z8HU0k+hmaHHUV05ZHrsFxduXHtIePvck=
github.com/authzed/cel-go v0.20.2/go.mod h1:pJHVFWbqUHV1J+klQoZubdKswlbxcsbojda3mye9kiU=
github.com/authzed/grpcutil v0.0.0-20250221190651-1985b19b35b8 h1:y17oq4U8n+k1OcIGGDsjYdIdp4QywGcE7ZphIvtfEbo=
github.com/authzed/grpcutil v0.0.0-20250221190651-1985b19b35b8/go.mod h1:Pf1ZSi41EePvx1GC1DeEJw5dn35iUcxZHqpHuG1Rpic=
github.com/authzed/spicedb v1.49.2 h1:6LKOxiNN7K18x4xs2NB0dhnDNDypbpuOP7s06AwjCH8=
github.com/authzed/spicedb v1.49.2/go.mod h1:I9t8PtFBxUHsSZKfrkK6bxbMy8La7LYjkpgw6UpNHQs=
github.com/aws/aws-sdk-go v1.34.0/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
github.com/aws/aws-sdk-go v1.40.45 h1:QN1nsY27ssD/JmW4s83qmSb+uL6DG4GmCDzjmJB4xUI=
github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
@@ -57,6 +68,8 @@ github.com/caarlos0/env/v11 v11.4.0 h1:Kcb6t5kIIr4XkoQC9AF2j+8E1Jsrl3Wz/hhm1LtoG
github.com/caarlos0/env/v11 v11.4.0/go.mod h1:qupehSf/Y0TUTsxKywqRt/vJjN5nz6vauiYEUUr8P4U=
github.com/cbroglie/mustache v1.0.1 h1:ivMg8MguXq/rrz2eu3tw6g3b16+PQhoTn6EZAhst2mw=
github.com/cbroglie/mustache v1.0.1/go.mod h1:R/RUa+SobQ14qkP4jtx5Vke5sDytONDQXNLPY/PO69g=
github.com/ccoveille/go-safecast/v2 v2.0.0 h1:+5eyITXAUj3wMjad6cRVJKGnC7vDS55zk0INzJagub0=
github.com/ccoveille/go-safecast/v2 v2.0.0/go.mod h1:JIYA4CAR33blIDuE6fSwCp2sz1oOBahXnvmdBhOAABs=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
@@ -78,6 +91,7 @@ github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un
github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
@@ -109,6 +123,8 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/eclipse/paho.mqtt.golang v1.5.1 h1:/VSOv3oDLlpqR2Epjn1Q7b2bSTplJIeV2ISgCl2W7nE=
github.com/eclipse/paho.mqtt.golang v1.5.1/go.mod h1:1/yJCneuyOoCOzKSsOTUc0AJfpsItBGWvYpBLimhArU=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
@@ -131,6 +147,8 @@ github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sa
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
github.com/go-chi/chi/v5 v5.2.5 h1:Eg4myHZBjyvJmAFjFvWgrqDTXFyOzjj7YIm3L3mu6Ug=
github.com/go-chi/chi/v5 v5.2.5/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0=
github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs=
github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
@@ -150,17 +168,22 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-logr/zerologr v1.2.3 h1:up5N9vcH9Xck3jJkXzgyOxozT14R47IyDODz8LM1KSs=
github.com/go-logr/zerologr v1.2.3/go.mod h1:BxwGo7y5zgSHYR1BjbnHPyF/5ZjVKfKxAZANVu6E8Ho=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U=
github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gofrs/uuid/v5 v5.4.0 h1:EfbpCTjqMuGyq5ZJwxqzn3Cbr2d0rUZU7v5ycAk/e/0=
github.com/gofrs/uuid/v5 v5.4.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
@@ -191,6 +214,8 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 h1:EEHtgt9IwisQ2AZ4pIsMjahcegHh6rmhqxzIRQIyepY=
github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
@@ -291,8 +316,8 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@@ -330,6 +355,7 @@ github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcncea
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
@@ -337,6 +363,8 @@ github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
@@ -371,6 +399,10 @@ github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/oklog/ulid/v2 v2.1.1 h1:suPZ4ARWLOJLegGFiZZ1dFAkqzhMjL3J1TzI+5wHz8s=
github.com/oklog/ulid/v2 v2.1.1/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ=
github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns=
github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo=
github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A=
github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
@@ -417,8 +449,8 @@ github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvM
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/common v0.67.4 h1:yR3NqWO1/UyO1w2PhUvXlGQs/PtFmoveVO0KZ4+Lvsc=
github.com/prometheus/common v0.67.4/go.mod h1:gP0fq6YjjNCLssJCQp0yk4M8W6ikLURwkdd/YKtTbyI=
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
@@ -433,8 +465,11 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
github.com/rubenv/sql-migrate v1.8.1 h1:EPNwCvjAowHI3TnZ+4fQu3a915OpnQoPAjTXCGOy2U0=
github.com/rubenv/sql-migrate v1.8.1/go.mod h1:BTIKBORjzyxZDS6dzoiw6eAFYJ1iNlGAtjn4LGeVjS8=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -474,6 +509,8 @@ github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
github.com/sqids/sqids-go v0.4.1 h1:eQKYzmAZbLlRwHeHYPF35QhgxwZHLnlmVj9AkIj/rrw=
github.com/sqids/sqids-go v0.4.1/go.mod h1:EMwHuPQgSNFS0A49jESTfIQS+066XQTVhukrzEPScl8=
github.com/stoewer/go-strcase v1.3.1 h1:iS0MdW+kVTxgMoE1LAZyMiYJFKlOzLooE4MxjirtkAs=
github.com/stoewer/go-strcase v1.3.1/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
@@ -526,22 +563,22 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.65.0 h1:XmiuHzgJt067+a6kwyAzkhXooYVv3/TOw9cM2VfJgUM=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.65.0/go.mod h1:KDgtbWKTQs4bM+VPUr6WlL9m/WXcmkCcBlIzqxPGzmI=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 h1:7iP2uCb7sGddAr30RRS6xjKy7AZ2JtTOPA3oolgVSw8=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0/go.mod h1:c7hN3ddxs/z6q9xwvfLPk+UHlWRQyaeR1LdgfL/66l0=
go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 h1:QKdN8ly8zEMrByybbQgv8cWBcdAarwmIPZ6FThrWXJs=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0/go.mod h1:bTdK1nhqF76qiPoCCdyFIV+N/sRHYXYCTQc+3VCi3MI=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.66.0 h1:PnV4kVnw0zOmwwFkAzCN5O07fw1YOIQor120zrh0AVo=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.66.0/go.mod h1:ofAwF4uinaf8SXdVzzbL4OsxJ3VfeEg3f/F6CeF49/Y=
go.opentelemetry.io/otel v1.41.0 h1:YlEwVsGAlCvczDILpUXpIpPSL/VPugt7zHThEMLce1c=
go.opentelemetry.io/otel v1.41.0/go.mod h1:Yt4UwgEKeT05QbLwbyHXEwhnjxNO6D8L5PQP51/46dE=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.41.0 h1:ao6Oe+wSebTlQ1OEht7jlYTzQKE+pnx/iNywFvTbuuI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.41.0/go.mod h1:u3T6vz0gh/NVzgDgiwkgLxpsSF6PaPmo2il0apGJbls=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0 h1:wVZXIWjQSeSmMoxF74LzAnpVQOAFDo3pPji9Y4SOFKc=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0/go.mod h1:khvBS2IggMFNwZK/6lEeHg/W57h/IX6J4URh57fuI40=
go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=
go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc=
go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=
go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=
go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw=
go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg=
go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=
go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=
go.opentelemetry.io/otel/metric v1.41.0 h1:rFnDcs4gRzBcsO9tS8LCpgR0dxg4aaxWlJxCno7JlTQ=
go.opentelemetry.io/otel/metric v1.41.0/go.mod h1:xPvCwd9pU0VN8tPZYzDZV/BMj9CM9vs00GuBjeKhJps=
go.opentelemetry.io/otel/sdk v1.41.0 h1:YPIEXKmiAwkGl3Gu1huk1aYWwtpRLeskpV+wPisxBp8=
go.opentelemetry.io/otel/sdk v1.41.0/go.mod h1:ahFdU0G5y8IxglBf0QBJXgSe7agzjE4GiTJ6HT9ud90=
go.opentelemetry.io/otel/sdk/metric v1.41.0 h1:siZQIYBAUd1rlIWQT2uCxWJxcCO7q3TriaMlf08rXw8=
go.opentelemetry.io/otel/sdk/metric v1.41.0/go.mod h1:HNBuSvT7ROaGtGI50ArdRLUnvRTRGniSUZbxiWxSO8Y=
go.opentelemetry.io/otel/trace v1.41.0 h1:Vbk2co6bhj8L59ZJ6/xFTskY+tGAbOnCtQGVVa9TIN0=
go.opentelemetry.io/otel/trace v1.41.0/go.mod h1:U1NU4ULCoxeDKc09yCWdWe+3QoyweJcISEVa1RBzOis=
go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A=
go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
@@ -595,6 +632,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c=
golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -660,9 +699,11 @@ golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
@@ -703,6 +744,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -769,3 +812,5 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
moul.io/http2curl v1.0.0 h1:6XwpyZOYsgZJrU8exnG87ncVkU1FVCcTRpwzOkTDUi8=
moul.io/http2curl v1.0.0/go.mod h1:f6cULg+e4Md/oW1cYmwW4IWQOVl2lGbmCNGOHvzX2kE=
sigs.k8s.io/controller-runtime v0.22.4 h1:GEjV7KV3TY8e+tJ2LCTxUTanW4z/FmNB7l327UfMq9A=
sigs.k8s.io/controller-runtime v0.22.4/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8=
+6
View File
@@ -0,0 +1,6 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package policies
const RulesType = "rules"
+1 -1
View File
@@ -44,7 +44,7 @@ func viewRuleEndpoint(s re.Service) endpoint.Endpoint {
if err := req.validate(); err != nil {
return viewRuleRes{}, err
}
rule, err := s.ViewRule(ctx, session, req.id)
rule, err := s.ViewRule(ctx, session, req.id, req.withRoles)
if err != nil {
return viewRuleRes{}, err
}
+1 -1
View File
@@ -334,7 +334,7 @@ func TestViewRuleEndpoint(t *testing.T) {
}
authCall := authn.On("Authenticate", mock.Anything, tc.token).Return(tc.authnRes, tc.authnErr)
svcCall := svc.On("ViewRule", mock.Anything, tc.authnRes, tc.id).Return(tc.svcRes, tc.svcErr)
svcCall := svc.On("ViewRule", mock.Anything, tc.authnRes, tc.id, false).Return(tc.svcRes, tc.svcErr)
res, err := req.make()
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
+2 -1
View File
@@ -33,7 +33,8 @@ func (req addRuleReq) validate() error {
}
type viewRuleReq struct {
id string
id string
withRoles bool
}
func (req viewRuleReq) validate() error {
+12 -1
View File
@@ -16,6 +16,7 @@ import (
apiutil "github.com/absmach/supermq/api/http/util"
smqauthn "github.com/absmach/supermq/pkg/authn"
"github.com/absmach/supermq/pkg/errors"
roleManagerHttp "github.com/absmach/supermq/pkg/roles/rolemanager/api"
"github.com/go-chi/chi/v5"
kithttp "github.com/go-kit/kit/transport/http"
"github.com/prometheus/client_golang/prometheus/promhttp"
@@ -36,6 +37,8 @@ func MakeHandler(svc re.Service, authn smqauthn.AuthNMiddleware, mux *chi.Mux, l
r.Use(authn.WithOptions(smqauthn.WithDomainCheck(true)).Middleware())
r.Route("/{domainID}", func(r chi.Router) {
r.Route("/rules", func(r chi.Router) {
d := roleManagerHttp.NewDecoder("ruleID")
r.Post("/", otelhttp.NewHandler(kithttp.NewServer(
addRuleEndpoint(svc),
decodeAddRuleRequest,
@@ -50,6 +53,8 @@ func MakeHandler(svc re.Service, authn smqauthn.AuthNMiddleware, mux *chi.Mux, l
opts...,
), "list_rules").ServeHTTP)
r = roleManagerHttp.EntityAvailableActionsRouter(svc, d, r, opts)
r.Route("/{ruleID}", func(r chi.Router) {
r.Get("/", otelhttp.NewHandler(kithttp.NewServer(
viewRuleEndpoint(svc),
@@ -99,6 +104,8 @@ func MakeHandler(svc re.Service, authn smqauthn.AuthNMiddleware, mux *chi.Mux, l
api.EncodeResponse,
opts...,
), "disable_rule").ServeHTTP)
roleManagerHttp.EntityRoleMangerRouter(svc, d, r, opts)
})
})
})
@@ -123,7 +130,11 @@ func decodeAddRuleRequest(_ context.Context, r *http.Request) (any, error) {
func decodeViewRuleRequest(_ context.Context, r *http.Request) (any, error) {
id := chi.URLParam(r, ruleIdKey)
return viewRuleReq{id: id}, nil
withRoles, err := apiutil.ReadBoolQuery(r, api.RolesKey, false)
if err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
return viewRuleReq{id: id, withRoles: withRoles}, nil
}
func decodeUpdateRuleRequest(_ context.Context, r *http.Request) (any, error) {
+8
View File
@@ -0,0 +1,8 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package re
import "github.com/absmach/supermq/pkg/roles"
const BuiltInRoleAdmin roles.BuiltInRoleName = "admin"
+9 -4
View File
@@ -11,6 +11,7 @@ import (
"github.com/absmach/supermq/pkg/events"
"github.com/absmach/supermq/pkg/events/store"
"github.com/absmach/supermq/pkg/messaging"
rmEvents "github.com/absmach/supermq/pkg/roles/rolemanager/events"
"github.com/go-chi/chi/v5/middleware"
)
@@ -32,6 +33,7 @@ var _ re.Service = (*eventStore)(nil)
type eventStore struct {
events.Publisher
svc re.Service
rmEvents.RoleManagerEventStore
}
// NewEventStoreMiddleware returns wrapper around rules service that sends
@@ -42,9 +44,12 @@ func NewEventStoreMiddleware(ctx context.Context, svc re.Service, url string) (r
return nil, err
}
res := rmEvents.NewRoleManagerEventStore("alarms", supermqPrefix, svc, publisher)
return &eventStore{
svc: svc,
Publisher: publisher,
svc: svc,
Publisher: publisher,
RoleManagerEventStore: res,
}, nil
}
@@ -78,8 +83,8 @@ func (es *eventStore) ListRules(ctx context.Context, session authn.Session, pm r
return page, nil
}
func (es *eventStore) ViewRule(ctx context.Context, session authn.Session, id string) (re.Rule, error) {
rule, err := es.svc.ViewRule(ctx, session, id)
func (es *eventStore) ViewRule(ctx context.Context, session authn.Session, id string, withRoles bool) (re.Rule, error) {
rule, err := es.svc.ViewRule(ctx, session, id, withRoles)
if err != nil {
return rule, err
}
+36 -23
View File
@@ -7,12 +7,14 @@ import (
"context"
"github.com/absmach/magistrala/re"
"github.com/absmach/magistrala/re/operations"
"github.com/absmach/supermq/pkg/authn"
smqauthz "github.com/absmach/supermq/pkg/authz"
"github.com/absmach/supermq/pkg/errors"
"github.com/absmach/supermq/pkg/messaging"
"github.com/absmach/supermq/pkg/permissions"
"github.com/absmach/supermq/pkg/policies"
rolemgr "github.com/absmach/supermq/pkg/roles/rolemanager/middleware"
)
var (
@@ -23,36 +25,47 @@ var (
)
type authorizationMiddleware struct {
svc re.Service
authz smqauthz.Authorization
svc re.Service
authz smqauthz.Authorization
entitiesOps permissions.EntitiesOperations[permissions.Operation]
rolemgr.RoleManagerAuthorizationMiddleware
}
// AuthorizationMiddleware adds authorization to the re service.
func AuthorizationMiddleware(svc re.Service, authz smqauthz.Authorization) (re.Service, error) {
func AuthorizationMiddleware(svc re.Service, authz smqauthz.Authorization, entitiesOps permissions.EntitiesOperations[permissions.Operation], roleOps permissions.Operations[permissions.RoleOperation]) (re.Service, error) {
if err := entitiesOps.Validate(); err != nil {
return nil, err
}
ram, err := rolemgr.NewAuthorization(operations.EntityType, svc, authz, roleOps)
if err != nil {
return nil, err
}
return &authorizationMiddleware{
svc: svc,
authz: authz,
svc: svc,
authz: authz,
entitiesOps: entitiesOps,
RoleManagerAuthorizationMiddleware: ram,
}, nil
}
func (am *authorizationMiddleware) AddRule(ctx context.Context, session authn.Session, r re.Rule) (re.Rule, error) {
if err := am.authorize(ctx, re.OpAddRule, session); err != nil {
if err := am.authorize(ctx, operations.OpAddRule, session, policies.DomainType, session.DomainID); err != nil {
return re.Rule{}, errors.Wrap(errDomainCreateRules, err)
}
return am.svc.AddRule(ctx, session, r)
}
func (am *authorizationMiddleware) ViewRule(ctx context.Context, session authn.Session, id string) (re.Rule, error) {
if err := am.authorize(ctx, re.OpViewRule, session); err != nil {
func (am *authorizationMiddleware) ViewRule(ctx context.Context, session authn.Session, id string, withRoles bool) (re.Rule, error) {
if err := am.authorize(ctx, operations.OpViewRule, session, operations.EntityType, id); err != nil {
return re.Rule{}, errors.Wrap(errDomainViewRules, err)
}
return am.svc.ViewRule(ctx, session, id)
return am.svc.ViewRule(ctx, session, id, withRoles)
}
func (am *authorizationMiddleware) UpdateRule(ctx context.Context, session authn.Session, r re.Rule) (re.Rule, error) {
if err := am.authorize(ctx, re.OpUpdateRule, session); err != nil {
if err := am.authorize(ctx, operations.OpUpdateRule, session, operations.EntityType, r.ID); err != nil {
return re.Rule{}, errors.Wrap(errDomainUpdateRules, err)
}
@@ -60,7 +73,7 @@ func (am *authorizationMiddleware) UpdateRule(ctx context.Context, session authn
}
func (am *authorizationMiddleware) UpdateRuleTags(ctx context.Context, session authn.Session, r re.Rule) (re.Rule, error) {
if err := am.authorize(ctx, re.OpUpdateRuleTags, session); err != nil {
if err := am.authorize(ctx, operations.OpUpdateRuleTags, session, operations.EntityType, r.ID); err != nil {
return re.Rule{}, errors.Wrap(errDomainUpdateRules, err)
}
@@ -68,7 +81,7 @@ func (am *authorizationMiddleware) UpdateRuleTags(ctx context.Context, session a
}
func (am *authorizationMiddleware) UpdateRuleSchedule(ctx context.Context, session authn.Session, r re.Rule) (re.Rule, error) {
if err := am.authorize(ctx, re.OpUpdateRuleSchedule, session); err != nil {
if err := am.authorize(ctx, operations.OpUpdateRuleSchedule, session, operations.EntityType, r.ID); err != nil {
return re.Rule{}, errors.Wrap(errDomainUpdateRules, err)
}
@@ -76,7 +89,7 @@ func (am *authorizationMiddleware) UpdateRuleSchedule(ctx context.Context, sessi
}
func (am *authorizationMiddleware) ListRules(ctx context.Context, session authn.Session, pm re.PageMeta) (re.Page, error) {
if err := am.authorize(ctx, re.OpListRules, session); err != nil {
if err := am.authorize(ctx, operations.OpListRules, session, policies.DomainType, session.DomainID); err != nil {
return re.Page{}, errors.Wrap(errDomainViewRules, err)
}
@@ -84,7 +97,7 @@ func (am *authorizationMiddleware) ListRules(ctx context.Context, session authn.
}
func (am *authorizationMiddleware) RemoveRule(ctx context.Context, session authn.Session, id string) error {
if err := am.authorize(ctx, re.OpRemoveRule, session); err != nil {
if err := am.authorize(ctx, operations.OpRemoveRule, session, operations.EntityType, id); err != nil {
return errors.Wrap(errDomainDeleteRules, err)
}
@@ -92,7 +105,7 @@ func (am *authorizationMiddleware) RemoveRule(ctx context.Context, session authn
}
func (am *authorizationMiddleware) EnableRule(ctx context.Context, session authn.Session, id string) (re.Rule, error) {
if err := am.authorize(ctx, re.OpEnableRule, session); err != nil {
if err := am.authorize(ctx, operations.OpEnableRule, session, operations.EntityType, id); err != nil {
return re.Rule{}, errors.Wrap(errDomainUpdateRules, err)
}
@@ -100,7 +113,7 @@ func (am *authorizationMiddleware) EnableRule(ctx context.Context, session authn
}
func (am *authorizationMiddleware) DisableRule(ctx context.Context, session authn.Session, id string) (re.Rule, error) {
if err := am.authorize(ctx, re.OpDisableRule, session); err != nil {
if err := am.authorize(ctx, operations.OpDisableRule, session, operations.EntityType, id); err != nil {
return re.Rule{}, errors.Wrap(errDomainUpdateRules, err)
}
@@ -119,8 +132,8 @@ func (am *authorizationMiddleware) Cancel() error {
return am.svc.Cancel()
}
func (am *authorizationMiddleware) authorize(ctx context.Context, op permissions.Operation, session authn.Session) error {
perm, err := re.GetPermission(op)
func (am *authorizationMiddleware) authorize(ctx context.Context, op permissions.Operation, session authn.Session, objType, obj string) error {
perm, err := am.entitiesOps.GetPermission(operations.EntityType, op)
if err != nil {
return err
}
@@ -130,19 +143,19 @@ func (am *authorizationMiddleware) authorize(ctx context.Context, op permissions
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Subject: session.DomainUserID,
Object: session.DomainID,
ObjectType: policies.DomainType,
Permission: perm,
Object: obj,
ObjectType: objType,
Permission: perm.String(),
}
var pat *smqauthz.PATReq
if session.PatID != "" {
opName := re.OperationName(op)
opName := am.entitiesOps.OperationName(operations.EntityType, op)
pat = &smqauthz.PATReq{
UserID: session.UserID,
PatID: session.PatID,
EntityID: session.DomainID,
EntityType: re.EntityType,
EntityType: operations.EntityType,
Operation: opName,
Domain: session.DomainID,
}
+35 -18
View File
@@ -7,26 +7,43 @@ import (
"context"
"time"
mgPolicies "github.com/absmach/magistrala/pkg/policies"
"github.com/absmach/magistrala/re"
"github.com/absmach/magistrala/re/operations"
"github.com/absmach/supermq/pkg/authn"
"github.com/absmach/supermq/pkg/callout"
"github.com/absmach/supermq/pkg/messaging"
"github.com/absmach/supermq/pkg/permissions"
"github.com/absmach/supermq/pkg/policies"
rolemw "github.com/absmach/supermq/pkg/roles/rolemanager/middleware"
)
var _ re.Service = (*calloutMiddleware)(nil)
type calloutMiddleware struct {
svc re.Service
callout callout.Callout
svc re.Service
callout callout.Callout
entitiesOps permissions.EntitiesOperations[permissions.Operation]
rolemw.RoleManagerCalloutMiddleware
}
const entityType = "rule"
func NewCallout(svc re.Service, callout callout.Callout) (re.Service, error) {
func NewCallout(svc re.Service, callout callout.Callout, entitiesOps permissions.EntitiesOperations[permissions.Operation], roleOps permissions.Operations[permissions.RoleOperation]) (re.Service, error) {
call, err := rolemw.NewCallout(mgPolicies.RulesType, svc, callout, roleOps)
if err != nil {
return nil, err
}
if err := entitiesOps.Validate(); err != nil {
return nil, err
}
return &calloutMiddleware{
svc: svc,
callout: callout,
svc: svc,
callout: callout,
entitiesOps: entitiesOps,
RoleManagerCalloutMiddleware: call,
}, nil
}
@@ -36,23 +53,23 @@ func (cm *calloutMiddleware) AddRule(ctx context.Context, session authn.Session,
"count": 1,
}
if err := cm.callOut(ctx, session, re.OpAddRuleStr, params); err != nil {
if err := cm.callOut(ctx, session, operations.OpAddRule, params); err != nil {
return re.Rule{}, err
}
return cm.svc.AddRule(ctx, session, r)
}
func (cm *calloutMiddleware) ViewRule(ctx context.Context, session authn.Session, id string) (re.Rule, error) {
func (cm *calloutMiddleware) ViewRule(ctx context.Context, session authn.Session, id string, withRoles bool) (re.Rule, error) {
params := map[string]any{
"entity_id": id,
}
if err := cm.callOut(ctx, session, re.OpViewRuleStr, params); err != nil {
if err := cm.callOut(ctx, session, operations.OpViewRule, params); err != nil {
return re.Rule{}, err
}
return cm.svc.ViewRule(ctx, session, id)
return cm.svc.ViewRule(ctx, session, id, withRoles)
}
func (cm *calloutMiddleware) UpdateRule(ctx context.Context, session authn.Session, r re.Rule) (re.Rule, error) {
@@ -60,7 +77,7 @@ func (cm *calloutMiddleware) UpdateRule(ctx context.Context, session authn.Sessi
"entity_id": r.ID,
}
if err := cm.callOut(ctx, session, re.OpUpdateRuleStr, params); err != nil {
if err := cm.callOut(ctx, session, operations.OpUpdateRule, params); err != nil {
return re.Rule{}, err
}
@@ -72,7 +89,7 @@ func (cm *calloutMiddleware) UpdateRuleTags(ctx context.Context, session authn.S
"entity_id": r.ID,
}
if err := cm.callOut(ctx, session, re.OpUpdateRuleTagsStr, params); err != nil {
if err := cm.callOut(ctx, session, operations.OpUpdateRuleTags, params); err != nil {
return re.Rule{}, err
}
@@ -84,7 +101,7 @@ func (cm *calloutMiddleware) UpdateRuleSchedule(ctx context.Context, session aut
"entity_id": r.ID,
}
if err := cm.callOut(ctx, session, re.OpUpdateRuleScheduleStr, params); err != nil {
if err := cm.callOut(ctx, session, operations.OpUpdateRuleSchedule, params); err != nil {
return re.Rule{}, err
}
@@ -96,7 +113,7 @@ func (cm *calloutMiddleware) ListRules(ctx context.Context, session authn.Sessio
"pagemeta": pm,
}
if err := cm.callOut(ctx, session, re.OpListRulesStr, params); err != nil {
if err := cm.callOut(ctx, session, operations.OpListRules, params); err != nil {
return re.Page{}, err
}
@@ -108,7 +125,7 @@ func (cm *calloutMiddleware) RemoveRule(ctx context.Context, session authn.Sessi
"entity_id": id,
}
if err := cm.callOut(ctx, session, re.OpRemoveRuleStr, params); err != nil {
if err := cm.callOut(ctx, session, operations.OpRemoveRule, params); err != nil {
return err
}
@@ -120,7 +137,7 @@ func (cm *calloutMiddleware) EnableRule(ctx context.Context, session authn.Sessi
"entity_id": id,
}
if err := cm.callOut(ctx, session, re.OpEnableRuleStr, params); err != nil {
if err := cm.callOut(ctx, session, operations.OpEnableRule, params); err != nil {
return re.Rule{}, err
}
@@ -132,7 +149,7 @@ func (cm *calloutMiddleware) DisableRule(ctx context.Context, session authn.Sess
"entity_id": id,
}
if err := cm.callOut(ctx, session, re.OpDisableRuleStr, params); err != nil {
if err := cm.callOut(ctx, session, operations.OpDisableRule, params); err != nil {
return re.Rule{}, err
}
@@ -151,7 +168,7 @@ func (cm *calloutMiddleware) Cancel() error {
return cm.svc.Cancel()
}
func (cm *calloutMiddleware) callOut(ctx context.Context, session authn.Session, op string, pld map[string]any) error {
func (cm *calloutMiddleware) callOut(ctx context.Context, session authn.Session, op permissions.Operation, pld map[string]any) error {
var entityID string
if id, ok := pld["entity_id"].(string); ok {
entityID = id
@@ -159,7 +176,7 @@ func (cm *calloutMiddleware) callOut(ctx context.Context, session authn.Session,
req := callout.Request{
BaseRequest: callout.BaseRequest{
Operation: op,
Operation: cm.entitiesOps.OperationName(entityType, op),
EntityType: entityType,
EntityID: entityID,
CallerID: session.UserID,
+9 -3
View File
@@ -12,6 +12,7 @@ import (
"github.com/absmach/magistrala/re"
"github.com/absmach/supermq/pkg/authn"
"github.com/absmach/supermq/pkg/messaging"
rolemw "github.com/absmach/supermq/pkg/roles/rolemanager/middleware"
)
var _ re.Service = (*loggingMiddleware)(nil)
@@ -19,10 +20,15 @@ var _ re.Service = (*loggingMiddleware)(nil)
type loggingMiddleware struct {
logger *slog.Logger
svc re.Service
rolemw.RoleManagerLoggingMiddleware
}
func LoggingMiddleware(svc re.Service, logger *slog.Logger) re.Service {
return &loggingMiddleware{logger, svc}
return &loggingMiddleware{
logger: logger,
svc: svc,
RoleManagerLoggingMiddleware: rolemw.NewLogging("re", svc, logger),
}
}
func (lm *loggingMiddleware) AddRule(ctx context.Context, session authn.Session, r re.Rule) (res re.Rule, err error) {
@@ -42,7 +48,7 @@ func (lm *loggingMiddleware) AddRule(ctx context.Context, session authn.Session,
return lm.svc.AddRule(ctx, session, r)
}
func (lm *loggingMiddleware) ViewRule(ctx context.Context, session authn.Session, id string) (res re.Rule, err error) {
func (lm *loggingMiddleware) ViewRule(ctx context.Context, session authn.Session, id string, withRoles bool) (res re.Rule, err error) {
defer func(begin time.Time) {
args := []any{
slog.String("duration", time.Since(begin).String()),
@@ -59,7 +65,7 @@ func (lm *loggingMiddleware) ViewRule(ctx context.Context, session authn.Session
}
lm.logger.Info("View rule completed successfully", args...)
}(time.Now())
return lm.svc.ViewRule(ctx, session, id)
return lm.svc.ViewRule(ctx, session, id, withRoles)
}
func (lm *loggingMiddleware) UpdateRule(ctx context.Context, session authn.Session, r re.Rule) (res re.Rule, err error) {
+136
View File
@@ -0,0 +1,136 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package middleware
import (
"context"
"time"
"github.com/absmach/magistrala/re"
"github.com/absmach/supermq/pkg/authn"
"github.com/absmach/supermq/pkg/messaging"
rolemw "github.com/absmach/supermq/pkg/roles/rolemanager/middleware"
"github.com/go-kit/kit/metrics"
)
type metricsMiddleware struct {
counter metrics.Counter
latency metrics.Histogram
service re.Service
rolemw.RoleManagerMetricsMiddleware
}
var _ re.Service = (*metricsMiddleware)(nil)
func NewMetricsMiddleware(counter metrics.Counter, latency metrics.Histogram, service re.Service) re.Service {
return &metricsMiddleware{
counter: counter,
latency: latency,
service: service,
RoleManagerMetricsMiddleware: rolemw.NewMetrics("re", service, counter, latency),
}
}
func (mm *metricsMiddleware) AddRule(ctx context.Context, session authn.Session, r re.Rule) (re.Rule, error) {
defer func(begin time.Time) {
mm.counter.With("method", "add_rule").Add(1)
mm.latency.With("method", "add_rule").Observe(time.Since(begin).Seconds())
}(time.Now())
return mm.service.AddRule(ctx, session, r)
}
func (mm *metricsMiddleware) ViewRule(ctx context.Context, session authn.Session, id string, withRoles bool) (re.Rule, error) {
defer func(begin time.Time) {
mm.counter.With("method", "view_rule").Add(1)
mm.latency.With("method", "view_rule").Observe(time.Since(begin).Seconds())
}(time.Now())
return mm.service.ViewRule(ctx, session, id, withRoles)
}
func (mm *metricsMiddleware) UpdateRule(ctx context.Context, session authn.Session, r re.Rule) (re.Rule, error) {
defer func(begin time.Time) {
mm.counter.With("method", "update_rule").Add(1)
mm.latency.With("method", "update_rule").Observe(time.Since(begin).Seconds())
}(time.Now())
return mm.service.UpdateRule(ctx, session, r)
}
func (mm *metricsMiddleware) UpdateRuleTags(ctx context.Context, session authn.Session, r re.Rule) (re.Rule, error) {
defer func(begin time.Time) {
mm.counter.With("method", "update_rule_tags").Add(1)
mm.latency.With("method", "update_rule_tags").Observe(time.Since(begin).Seconds())
}(time.Now())
return mm.service.UpdateRuleTags(ctx, session, r)
}
func (mm *metricsMiddleware) UpdateRuleSchedule(ctx context.Context, session authn.Session, r re.Rule) (re.Rule, error) {
defer func(begin time.Time) {
mm.counter.With("method", "update_rule_schedule").Add(1)
mm.latency.With("method", "update_rule_schedule").Observe(time.Since(begin).Seconds())
}(time.Now())
return mm.service.UpdateRuleSchedule(ctx, session, r)
}
func (mm *metricsMiddleware) ListRules(ctx context.Context, session authn.Session, pm re.PageMeta) (re.Page, error) {
defer func(begin time.Time) {
mm.counter.With("method", "list_rules").Add(1)
mm.latency.With("method", "list_rules").Observe(time.Since(begin).Seconds())
}(time.Now())
return mm.service.ListRules(ctx, session, pm)
}
func (mm *metricsMiddleware) RemoveRule(ctx context.Context, session authn.Session, id string) error {
defer func(begin time.Time) {
mm.counter.With("method", "remove_rule").Add(1)
mm.latency.With("method", "remove_rule").Observe(time.Since(begin).Seconds())
}(time.Now())
return mm.service.RemoveRule(ctx, session, id)
}
func (mm *metricsMiddleware) EnableRule(ctx context.Context, session authn.Session, id string) (re.Rule, error) {
defer func(begin time.Time) {
mm.counter.With("method", "enable_rule").Add(1)
mm.latency.With("method", "enable_rule").Observe(time.Since(begin).Seconds())
}(time.Now())
return mm.service.EnableRule(ctx, session, id)
}
func (mm *metricsMiddleware) DisableRule(ctx context.Context, session authn.Session, id string) (re.Rule, error) {
defer func(begin time.Time) {
mm.counter.With("method", "disable_rule").Add(1)
mm.latency.With("method", "disable_rule").Observe(time.Since(begin).Seconds())
}(time.Now())
return mm.service.DisableRule(ctx, session, id)
}
func (mm *metricsMiddleware) Handle(msg *messaging.Message) error {
defer func(begin time.Time) {
mm.counter.With("method", "handle").Add(1)
mm.latency.With("method", "handle").Observe(time.Since(begin).Seconds())
}(time.Now())
return mm.service.Handle(msg)
}
func (mm *metricsMiddleware) StartScheduler(ctx context.Context) error {
defer func(begin time.Time) {
mm.counter.With("method", "start_scheduler").Add(1)
mm.latency.With("method", "start_scheduler").Observe(time.Since(begin).Seconds())
}(time.Now())
return mm.service.StartScheduler(ctx)
}
func (mm *metricsMiddleware) Cancel() error {
return mm.service.Cancel()
}
+136
View File
@@ -0,0 +1,136 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package middleware
import (
"context"
"github.com/absmach/magistrala/re"
"github.com/absmach/supermq/pkg/authn"
"github.com/absmach/supermq/pkg/messaging"
rolemw "github.com/absmach/supermq/pkg/roles/rolemanager/middleware"
smqTracing "github.com/absmach/supermq/pkg/tracing"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
)
type tracingMiddleware struct {
tracer trace.Tracer
svc re.Service
rolemw.RoleManagerTracing
}
var _ re.Service = (*tracingMiddleware)(nil)
func NewTracingMiddleware(tracer trace.Tracer, svc re.Service) re.Service {
return &tracingMiddleware{
tracer: tracer,
svc: svc,
RoleManagerTracing: rolemw.NewTracing("re", svc, tracer),
}
}
func (tm *tracingMiddleware) AddRule(ctx context.Context, session authn.Session, r re.Rule) (re.Rule, error) {
ctx, span := smqTracing.StartSpan(ctx, tm.tracer, "add_rule", trace.WithAttributes(
attribute.String("name", r.Name),
attribute.String("domain_id", r.DomainID),
))
defer span.End()
return tm.svc.AddRule(ctx, session, r)
}
func (tm *tracingMiddleware) ViewRule(ctx context.Context, session authn.Session, id string, withRoles bool) (re.Rule, error) {
ctx, span := smqTracing.StartSpan(ctx, tm.tracer, "view_rule", trace.WithAttributes(
attribute.String("id", id),
))
defer span.End()
return tm.svc.ViewRule(ctx, session, id, withRoles)
}
func (tm *tracingMiddleware) UpdateRule(ctx context.Context, session authn.Session, r re.Rule) (re.Rule, error) {
ctx, span := smqTracing.StartSpan(ctx, tm.tracer, "update_rule", trace.WithAttributes(
attribute.String("id", r.ID),
))
defer span.End()
return tm.svc.UpdateRule(ctx, session, r)
}
func (tm *tracingMiddleware) UpdateRuleTags(ctx context.Context, session authn.Session, r re.Rule) (re.Rule, error) {
ctx, span := smqTracing.StartSpan(ctx, tm.tracer, "update_rule_tags", trace.WithAttributes(
attribute.String("id", r.ID),
))
defer span.End()
return tm.svc.UpdateRuleTags(ctx, session, r)
}
func (tm *tracingMiddleware) UpdateRuleSchedule(ctx context.Context, session authn.Session, r re.Rule) (re.Rule, error) {
ctx, span := smqTracing.StartSpan(ctx, tm.tracer, "update_rule_schedule", trace.WithAttributes(
attribute.String("id", r.ID),
))
defer span.End()
return tm.svc.UpdateRuleSchedule(ctx, session, r)
}
func (tm *tracingMiddleware) ListRules(ctx context.Context, session authn.Session, pm re.PageMeta) (re.Page, error) {
ctx, span := smqTracing.StartSpan(ctx, tm.tracer, "list_rules", trace.WithAttributes(
attribute.Int("offset", int(pm.Offset)),
attribute.Int("limit", int(pm.Limit)),
))
defer span.End()
return tm.svc.ListRules(ctx, session, pm)
}
func (tm *tracingMiddleware) RemoveRule(ctx context.Context, session authn.Session, id string) error {
ctx, span := smqTracing.StartSpan(ctx, tm.tracer, "remove_rule", trace.WithAttributes(
attribute.String("id", id),
))
defer span.End()
return tm.svc.RemoveRule(ctx, session, id)
}
func (tm *tracingMiddleware) EnableRule(ctx context.Context, session authn.Session, id string) (re.Rule, error) {
ctx, span := smqTracing.StartSpan(ctx, tm.tracer, "enable_rule", trace.WithAttributes(
attribute.String("id", id),
))
defer span.End()
return tm.svc.EnableRule(ctx, session, id)
}
func (tm *tracingMiddleware) DisableRule(ctx context.Context, session authn.Session, id string) (re.Rule, error) {
ctx, span := smqTracing.StartSpan(ctx, tm.tracer, "disable_rule", trace.WithAttributes(
attribute.String("id", id),
))
defer span.End()
return tm.svc.DisableRule(ctx, session, id)
}
func (tm *tracingMiddleware) Handle(msg *messaging.Message) error {
_, span := smqTracing.StartSpan(context.Background(), tm.tracer, "handle", trace.WithAttributes(
attribute.String("channel", msg.Channel),
attribute.String("subtopic", msg.Subtopic),
))
defer span.End()
return tm.svc.Handle(msg)
}
func (tm *tracingMiddleware) StartScheduler(ctx context.Context) error {
ctx, span := smqTracing.StartSpan(ctx, tm.tracer, "start_scheduler")
defer span.End()
return tm.svc.StartScheduler(ctx)
}
func (tm *tracingMiddleware) Cancel() error {
return tm.svc.Cancel()
}
File diff suppressed because it is too large Load Diff
+1500 -12
View File
File diff suppressed because it is too large Load Diff
-68
View File
@@ -1,68 +0,0 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package re
import (
"github.com/absmach/supermq/pkg/errors"
"github.com/absmach/supermq/pkg/permissions"
"github.com/absmach/supermq/pkg/policies"
)
const EntityType = "rules"
const (
OpAddRule permissions.Operation = iota
OpViewRule
OpUpdateRule
OpUpdateRuleTags
OpUpdateRuleSchedule
OpListRules
OpRemoveRule
OpEnableRule
OpDisableRule
)
const (
OpAddRuleStr = "OpAddRule"
OpViewRuleStr = "OpViewRule"
OpUpdateRuleStr = "OpUpdateRule"
OpUpdateRuleTagsStr = "OpUpdateRuleTags"
OpUpdateRuleScheduleStr = "OpUpdateRuleSchedule"
OpListRulesStr = "OpListRules"
OpRemoveRuleStr = "OpRemoveRule"
OpEnableRuleStr = "OpEnableRule"
OpDisableRuleStr = "OpDisableRule"
)
func GetPermission(op permissions.Operation) (string, error) {
if op < OpAddRule || op > OpDisableRule {
return "", errors.New("invalid operation")
}
return policies.MembershipPermission, nil
}
func OperationName(op permissions.Operation) string {
switch op {
case OpAddRule:
return OpAddRuleStr
case OpViewRule:
return OpViewRuleStr
case OpUpdateRule:
return OpUpdateRuleStr
case OpUpdateRuleTags:
return OpUpdateRuleTagsStr
case OpUpdateRuleSchedule:
return OpUpdateRuleScheduleStr
case OpListRules:
return OpListRulesStr
case OpRemoveRule:
return OpRemoveRuleStr
case OpEnableRule:
return OpEnableRuleStr
case OpDisableRule:
return OpDisableRuleStr
default:
return "unknown"
}
}
+64
View File
@@ -0,0 +1,64 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package operations
import (
"github.com/absmach/supermq/pkg/permissions"
)
const EntityType = "rule"
// Rule Operations.
const (
OpAddRule permissions.Operation = iota
OpViewRule
OpUpdateRule
OpUpdateRuleTags
OpUpdateRuleSchedule
OpRemoveRule
OpListRules
OpEnableRule
OpDisableRule
)
func OperationDetails() map[permissions.Operation]permissions.OperationDetails {
return map[permissions.Operation]permissions.OperationDetails{
OpAddRule: {
Name: "add",
PermissionRequired: true,
},
OpViewRule: {
Name: "view",
PermissionRequired: true,
},
OpUpdateRule: {
Name: "update",
PermissionRequired: true,
},
OpUpdateRuleTags: {
Name: "update_tags",
PermissionRequired: true,
},
OpUpdateRuleSchedule: {
Name: "update_schedule",
PermissionRequired: true,
},
OpRemoveRule: {
Name: "delete",
PermissionRequired: true,
},
OpListRules: {
Name: "list",
PermissionRequired: true,
},
OpEnableRule: {
Name: "enable",
PermissionRequired: true,
},
OpDisableRule: {
Name: "disable",
PermissionRequired: true,
},
}
}
+20 -2
View File
@@ -4,12 +4,20 @@
package postgres
import (
dpostgres "github.com/absmach/supermq/domains/postgres"
"github.com/absmach/supermq/pkg/errors"
repoerr "github.com/absmach/supermq/pkg/errors/repository"
rolesPostgres "github.com/absmach/supermq/pkg/roles/repo/postgres"
_ "github.com/jackc/pgx/v5/stdlib" // required for SQL access
migrate "github.com/rubenv/sql-migrate"
)
func Migration() *migrate.MemoryMigrationSource {
return &migrate.MemoryMigrationSource{
func Migration() (*migrate.MemoryMigrationSource, error) {
rolesMigration, err := rolesPostgres.Migration(rolesTableNamePrefix, entityTableName, entityIDColumnName)
if err != nil {
return &migrate.MemoryMigrationSource{}, errors.Wrap(repoerr.ErrRoleMigration, err)
}
rulesMigration := &migrate.MemoryMigrationSource{
Migrations: []*migrate.Migration{
{
Id: "rules_01",
@@ -65,4 +73,14 @@ func Migration() *migrate.MemoryMigrationSource {
},
},
}
rulesMigration.Migrations = append(rulesMigration.Migrations, rolesMigration.Migrations...)
domainsMigration, err := dpostgres.Migration()
if err != nil {
return &migrate.MemoryMigrationSource{}, errors.Wrap(repoerr.ErrRoleMigration, err)
}
rulesMigration.Migrations = append(rulesMigration.Migrations, domainsMigration.Migrations...)
return rulesMigration, nil
}
+165 -1
View File
@@ -10,19 +10,32 @@ import (
"strings"
"time"
mgPolicies "github.com/absmach/magistrala/pkg/policies"
"github.com/absmach/magistrala/re"
api "github.com/absmach/supermq/api/http"
"github.com/absmach/supermq/pkg/errors"
repoerr "github.com/absmach/supermq/pkg/errors/repository"
"github.com/absmach/supermq/pkg/postgres"
rolesPostgres "github.com/absmach/supermq/pkg/roles/repo/postgres"
)
const (
rolesTableNamePrefix = "rules"
entityTableName = "rules"
entityIDColumnName = "id"
)
type PostgresRepository struct {
DB postgres.Database
rolesPostgres.Repository
}
func NewRepository(db postgres.Database) re.Repository {
return &PostgresRepository{DB: db}
rolesRepo := rolesPostgres.NewRepository(db, mgPolicies.RulesType, rolesTableNamePrefix, entityTableName, entityIDColumnName)
return &PostgresRepository{
DB: db,
Repository: rolesRepo,
}
}
func (repo *PostgresRepository) AddRule(ctx context.Context, r re.Rule) (re.Rule, error) {
@@ -82,6 +95,157 @@ func (repo *PostgresRepository) ViewRule(ctx context.Context, id string) (re.Rul
return ret, nil
}
func (repo *PostgresRepository) RetrieveByIDWithRoles(ctx context.Context, id, memberID string) (re.Rule, error) {
query := `
WITH selected_rule AS (
SELECT
r.id,
r.domain_id
FROM
rules r
WHERE
r.id = :id
LIMIT 1
),
selected_rule_roles AS (
SELECT
rr.entity_id AS rule_id,
rrm.member_id AS member_id,
rr.id AS role_id,
rr."name" AS role_name,
jsonb_agg(DISTINCT rra."action") AS actions,
'direct' AS access_type,
'' AS access_provider_id
FROM
rules_roles rr
JOIN
rules_role_members rrm ON rr.id = rrm.role_id
JOIN
rules_role_actions rra ON rr.id = rra.role_id
JOIN
selected_rule sr ON sr.id = rr.entity_id
AND rrm.member_id = :member_id
GROUP BY
rr.entity_id, rr.id, rr.name, rrm.member_id
),
selected_domain_roles AS (
SELECT
sr.id AS rule_id,
drm.member_id AS member_id,
dr.id AS role_id,
dr."name" AS role_name,
jsonb_agg(DISTINCT all_actions."action") AS actions,
'domain' AS access_type,
dr.entity_id AS access_provider_id
FROM
domains d
JOIN
selected_rule sr ON sr.domain_id = d.id
JOIN
domains_roles dr ON dr.entity_id = d.id
JOIN
domains_role_members drm ON dr.id = drm.role_id
JOIN
domains_role_actions dra ON dr.id = dra.role_id
JOIN
domains_role_actions all_actions ON dr.id = all_actions.role_id
WHERE
drm.member_id = :member_id
AND dra."action" LIKE 'rule%'
GROUP BY
sr.id, dr.entity_id, dr.id, dr."name", drm.member_id
),
all_roles AS (
SELECT
srr.rule_id,
srr.member_id,
srr.role_id,
srr.role_name,
srr.actions,
srr.access_type,
srr.access_provider_id
FROM
selected_rule_roles srr
UNION
SELECT
sdr.rule_id,
sdr.member_id,
sdr.role_id,
sdr.role_name,
sdr.actions,
sdr.access_type,
sdr.access_provider_id
FROM
selected_domain_roles sdr
),
final_roles AS (
SELECT
ar.rule_id,
ar.member_id,
jsonb_agg(
jsonb_build_object(
'role_id', ar.role_id,
'role_name', ar.role_name,
'actions', ar.actions,
'access_type', ar.access_type,
'access_provider_id', ar.access_provider_id
)
) AS roles
FROM all_roles ar
GROUP BY
ar.rule_id, ar.member_id
)
SELECT
r2.id,
r2."name",
r2.domain_id,
r2.tags,
r2.metadata,
r2.input_channel,
r2.input_topic,
r2.outputs,
r2.status,
r2.logic_type,
r2.logic_value,
r2.time,
r2.recurring,
r2.recurring_period,
r2.start_datetime,
r2.created_at,
r2.created_by,
r2.updated_at,
r2.updated_by,
fr.member_id,
fr.roles
FROM rules r2
JOIN final_roles fr ON fr.rule_id = r2.id
`
parameters := map[string]any{
"id": id,
"member_id": memberID,
}
row, err := repo.DB.NamedQueryContext(ctx, query, parameters)
if err != nil {
return re.Rule{}, errors.Wrap(repoerr.ErrViewEntity, err)
}
defer row.Close()
dbrule := dbRule{}
if !row.Next() {
return re.Rule{}, repoerr.ErrNotFound
}
if err := row.StructScan(&dbrule); err != nil {
return re.Rule{}, errors.Wrap(repoerr.ErrViewEntity, err)
}
r, err := dbToRule(dbrule)
if err != nil {
return re.Rule{}, errors.Wrap(repoerr.ErrViewEntity, err)
}
return r, nil
}
func (repo *PostgresRepository) UpdateRuleStatus(ctx context.Context, r re.Rule) (re.Rule, error) {
q := `UPDATE rules
SET status = :status, updated_at = :updated_at, updated_by = :updated_by
+11
View File
@@ -11,6 +11,7 @@ import (
"github.com/absmach/magistrala/pkg/schedule"
"github.com/absmach/magistrala/re"
"github.com/absmach/supermq/pkg/errors"
"github.com/absmach/supermq/pkg/roles"
"github.com/jackc/pgtype"
)
@@ -35,6 +36,8 @@ type dbRule struct {
CreatedBy string `db:"created_by"`
UpdatedAt time.Time `db:"updated_at"`
UpdatedBy string `db:"updated_by"`
MemberID string `db:"member_id,omitempty"`
Roles json.RawMessage `db:"roles,omitempty"`
}
func ruleToDb(r re.Rule) (dbRule, error) {
@@ -108,6 +111,13 @@ func dbToRule(dto dbRule) (re.Rule, error) {
}
}
var roles []roles.MemberRoleActions
if dto.Roles != nil {
if err := json.Unmarshal(dto.Roles, &roles); err != nil {
return re.Rule{}, errors.Wrap(errors.ErrMalformedEntity, err)
}
}
return re.Rule{
ID: dto.ID,
Name: dto.Name,
@@ -132,6 +142,7 @@ func dbToRule(dto dbRule) (re.Rule, error) {
CreatedBy: dto.CreatedBy,
UpdatedAt: dto.UpdatedAt,
UpdatedBy: dto.UpdatedBy,
Roles: roles,
}, nil
}
+5 -1
View File
@@ -75,7 +75,11 @@ func TestMain(m *testing.M) {
SSLRootCert: "",
}
if db, err = postgres.Setup(dbConfig, *repostgres.Migration()); err != nil {
migration, err := repostgres.Migration()
if err != nil {
log.Fatalf("Could not get migration: %s", err)
}
if db, err = postgres.Setup(dbConfig, *migration); err != nil {
log.Fatalf("Could not setup test DB connection: %s", err)
}
+21 -16
View File
@@ -13,6 +13,7 @@ import (
"github.com/absmach/supermq/pkg/authn"
"github.com/absmach/supermq/pkg/errors"
"github.com/absmach/supermq/pkg/messaging"
"github.com/absmach/supermq/pkg/roles"
)
const (
@@ -42,21 +43,22 @@ var outputRegistry = map[outputs.OutputType]func() Runnable{
}
type Rule struct {
ID string `json:"id"`
Name string `json:"name"`
DomainID string `json:"domain"`
Metadata Metadata `json:"metadata,omitempty"`
Tags []string `json:"tags,omitempty"`
InputChannel string `json:"input_channel"`
InputTopic string `json:"input_topic"`
Logic Script `json:"logic"`
Outputs Outputs `json:"outputs,omitempty"`
Schedule schedule.Schedule `json:"schedule,omitempty"`
Status Status `json:"status"`
CreatedAt time.Time `json:"created_at"`
CreatedBy string `json:"created_by"`
UpdatedAt time.Time `json:"updated_at"`
UpdatedBy string `json:"updated_by"`
ID string `json:"id"`
Name string `json:"name"`
DomainID string `json:"domain"`
Metadata Metadata `json:"metadata,omitempty"`
Tags []string `json:"tags,omitempty"`
InputChannel string `json:"input_channel"`
InputTopic string `json:"input_topic"`
Logic Script `json:"logic"`
Outputs Outputs `json:"outputs,omitempty"`
Schedule schedule.Schedule `json:"schedule,omitempty"`
Status Status `json:"status"`
CreatedAt time.Time `json:"created_at"`
CreatedBy string `json:"created_by"`
UpdatedAt time.Time `json:"updated_at"`
UpdatedBy string `json:"updated_by"`
Roles []roles.MemberRoleActions `json:"roles,omitempty"`
}
// EventEncode converts a Rule struct to map[string]any at event producer.
@@ -224,7 +226,7 @@ type Page struct {
type Service interface {
messaging.MessageHandler
AddRule(ctx context.Context, session authn.Session, r Rule) (Rule, error)
ViewRule(ctx context.Context, session authn.Session, id string) (Rule, error)
ViewRule(ctx context.Context, session authn.Session, id string, withRoles bool) (Rule, error)
UpdateRule(ctx context.Context, session authn.Session, r Rule) (Rule, error)
UpdateRuleTags(ctx context.Context, session authn.Session, r Rule) (Rule, error)
UpdateRuleSchedule(ctx context.Context, session authn.Session, r Rule) (Rule, error)
@@ -234,11 +236,13 @@ type Service interface {
DisableRule(ctx context.Context, session authn.Session, id string) (Rule, error)
StartScheduler(ctx context.Context) error
roles.RoleManager
}
type Repository interface {
AddRule(ctx context.Context, r Rule) (Rule, error)
ViewRule(ctx context.Context, id string) (Rule, error)
RetrieveByIDWithRoles(ctx context.Context, id, memberID string) (Rule, error)
UpdateRule(ctx context.Context, r Rule) (Rule, error)
UpdateRuleTags(ctx context.Context, r Rule) (Rule, error)
UpdateRuleSchedule(ctx context.Context, r Rule) (Rule, error)
@@ -246,4 +250,5 @@ type Repository interface {
UpdateRuleStatus(ctx context.Context, r Rule) (Rule, error)
ListRules(ctx context.Context, pm PageMeta) (Page, error)
UpdateRuleDue(ctx context.Context, id string, due time.Time) (Rule, error)
roles.Repository
}
+57 -14
View File
@@ -11,11 +11,14 @@ import (
"github.com/absmach/magistrala/pkg/emailer"
pkglog "github.com/absmach/magistrala/pkg/logger"
"github.com/absmach/magistrala/pkg/ticker"
"github.com/absmach/magistrala/re/operations"
"github.com/absmach/supermq"
"github.com/absmach/supermq/pkg/authn"
"github.com/absmach/supermq/pkg/errors"
svcerr "github.com/absmach/supermq/pkg/errors/service"
"github.com/absmach/supermq/pkg/messaging"
"github.com/absmach/supermq/pkg/policies"
"github.com/absmach/supermq/pkg/roles"
)
var ErrGoroutinesNotAllowed = errors.New("goroutines are not allowed in Go scripts")
@@ -30,23 +33,29 @@ type re struct {
ticker ticker.Ticker
email emailer.Emailer
readers grpcReadersV1.ReadersServiceClient
roles.ProvisionManageService
}
func NewService(repo Repository, runInfo chan pkglog.RunInfo, idp supermq.IDProvider, rePubSub messaging.PubSub, writersPub, alarmsPub messaging.Publisher, tck ticker.Ticker, emailer emailer.Emailer, readers grpcReadersV1.ReadersServiceClient) Service {
return &re{
repo: repo,
idp: idp,
runInfo: runInfo,
rePubSub: rePubSub,
writersPub: writersPub,
alarmsPub: alarmsPub,
ticker: tck,
email: emailer,
readers: readers,
func NewService(repo Repository, runInfo chan pkglog.RunInfo, policy policies.Service, idp supermq.IDProvider, rePubSub messaging.PubSub, writersPub, alarmsPub messaging.Publisher, tck ticker.Ticker, emailer emailer.Emailer, readers grpcReadersV1.ReadersServiceClient, availableActions []roles.Action, builtInRoles map[roles.BuiltInRoleName][]roles.Action) (Service, error) {
rpms, err := roles.NewProvisionManageService(operations.EntityType, repo, policy, idp, availableActions, builtInRoles)
if err != nil {
return nil, err
}
return &re{
repo: repo,
idp: idp,
runInfo: runInfo,
rePubSub: rePubSub,
writersPub: writersPub,
alarmsPub: alarmsPub,
ticker: tck,
email: emailer,
readers: readers,
ProvisionManageService: rpms,
}, nil
}
func (re *re) AddRule(ctx context.Context, session authn.Session, r Rule) (Rule, error) {
func (re *re) AddRule(ctx context.Context, session authn.Session, r Rule) (retRule Rule, retErr error) {
if r.Logic.Type == GoType && goKeywordRegex.MatchString(r.Logic.Value) {
return Rule{}, errors.Wrap(svcerr.ErrMalformedEntity, ErrGoroutinesNotAllowed)
}
@@ -72,11 +81,45 @@ func (re *re) AddRule(ctx context.Context, session authn.Session, r Rule) (Rule,
return Rule{}, errors.Wrap(svcerr.ErrCreateEntity, err)
}
defer func() {
if retErr != nil {
if errRollBack := re.repo.RemoveRule(ctx, rule.ID); errRollBack != nil {
retErr = errors.Wrap(retErr, errors.Wrap(svcerr.ErrRollbackRepo, errRollBack))
}
}
}()
newBuiltInRoleMembers := map[roles.BuiltInRoleName][]roles.Member{
BuiltInRoleAdmin: {roles.Member(session.UserID)},
}
optionalPolicies := []policies.Policy{
{
SubjectType: policies.DomainType,
Subject: session.DomainID,
Relation: policies.DomainRelation,
ObjectType: operations.EntityType,
Object: rule.ID,
},
}
_, err = re.AddNewEntitiesRoles(ctx, session.DomainID, session.UserID, []string{rule.ID}, optionalPolicies, newBuiltInRoleMembers)
if err != nil {
return Rule{}, errors.Wrap(svcerr.ErrAddPolicies, err)
}
return rule, nil
}
func (re *re) ViewRule(ctx context.Context, session authn.Session, id string) (Rule, error) {
rule, err := re.repo.ViewRule(ctx, id)
func (re *re) ViewRule(ctx context.Context, session authn.Session, id string, withRoles bool) (Rule, error) {
var rule Rule
var err error
switch withRoles {
case true:
rule, err = re.repo.RetrieveByIDWithRoles(ctx, id, session.UserID)
default:
rule, err = re.repo.ViewRule(ctx, id)
}
if err != nil {
return Rule{}, errors.Wrap(svcerr.ErrViewEntity, err)
}
+197 -32
View File
@@ -26,6 +26,8 @@ import (
svcerr "github.com/absmach/supermq/pkg/errors/service"
"github.com/absmach/supermq/pkg/messaging"
pubsubmocks "github.com/absmach/supermq/pkg/messaging/mocks"
policymocks "github.com/absmach/supermq/pkg/policies/mocks"
"github.com/absmach/supermq/pkg/roles"
"github.com/absmach/supermq/pkg/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
@@ -59,27 +61,40 @@ var (
}
)
func newService(t *testing.T, runInfo chan pkglog.RunInfo) (re.Service, *mocks.Repository, *pubsubmocks.PubSub, *tmocks.Ticker, *emocks.Emailer) {
func newService(t *testing.T, runInfo chan pkglog.RunInfo) (re.Service, *mocks.Repository, *pubsubmocks.PubSub, *tmocks.Ticker, *emocks.Emailer, *policymocks.Service) {
repo := new(mocks.Repository)
mockTicker := new(tmocks.Ticker)
idProvider := uuid.NewMock()
pubsub := pubsubmocks.NewPubSub(t)
readersSvc := new(readmocks.ReadersServiceClient)
e := new(emocks.Emailer)
return re.NewService(repo, runInfo, idProvider, pubsub, pubsub, pubsub, mockTicker, e, readersSvc), repo, pubsub, mockTicker, e
policy := new(policymocks.Service)
availableActions := []roles.Action{}
builtInRoles := map[roles.BuiltInRoleName][]roles.Action{
"admin": availableActions,
}
svc, err := re.NewService(repo, runInfo, policy, idProvider, pubsub, pubsub, pubsub, mockTicker, e, readersSvc, availableActions, builtInRoles)
if err != nil {
t.Fatalf("Failed to create service: %v", err)
}
return svc, repo, pubsub, mockTicker, e, policy
}
func TestAddRule(t *testing.T) {
// nolint:dogsled
svc, repo, _, _, _ := newService(t, make(chan pkglog.RunInfo))
svc, repo, _, _, _, policies := newService(t, make(chan pkglog.RunInfo))
ruleName := namegen.Generate()
now := time.Now().Add(time.Hour)
cases := []struct {
desc string
session authn.Session
rule re.Rule
res re.Rule
err error
desc string
session authn.Session
rule re.Rule
res re.Rule
err error
addPoliciesErr error
deletePolicies error
addRoleErr error
deleteErr error
}{
{
desc: "Add rule successfully",
@@ -109,7 +124,10 @@ func TestAddRule(t *testing.T) {
CreatedBy: userID,
DomainID: domainID,
},
err: nil,
err: nil,
addPoliciesErr: nil,
addRoleErr: nil,
deleteErr: nil,
},
{
desc: "Add rule with failed repo",
@@ -126,7 +144,11 @@ func TestAddRule(t *testing.T) {
Time: now,
},
},
err: repoerr.ErrCreateEntity,
err: repoerr.ErrCreateEntity,
addPoliciesErr: nil,
deletePolicies: nil,
addRoleErr: nil,
deleteErr: nil,
},
{
desc: "Add rule with non-zero StartDateTime",
@@ -158,7 +180,136 @@ func TestAddRule(t *testing.T) {
CreatedBy: userID,
DomainID: domainID,
},
err: nil,
err: nil,
addPoliciesErr: nil,
addRoleErr: nil,
deleteErr: nil,
},
{
desc: "Add rule with failed to add roles and failed to delete policies",
session: authn.Session{
UserID: userID,
DomainID: domainID,
},
rule: re.Rule{
Name: ruleName,
InputChannel: inputChannel,
Schedule: pkgSch.Schedule{
Recurring: pkgSch.Daily,
RecurringPeriod: 1,
Time: now,
},
},
res: re.Rule{
Name: ruleName,
ID: ruleID,
InputChannel: inputChannel,
Schedule: pkgSch.Schedule{
Recurring: pkgSch.Daily,
RecurringPeriod: 1,
Time: now,
},
Status: re.EnabledStatus,
CreatedBy: userID,
DomainID: domainID,
},
addRoleErr: svcerr.ErrCreateEntity,
deletePolicies: svcerr.ErrRemoveEntity,
err: svcerr.ErrRemoveEntity,
},
{
desc: "Add rule with failed to add policies",
session: authn.Session{
UserID: userID,
DomainID: domainID,
},
rule: re.Rule{
Name: ruleName,
InputChannel: inputChannel,
Schedule: pkgSch.Schedule{
Recurring: pkgSch.Daily,
RecurringPeriod: 1,
Time: now,
},
},
res: re.Rule{
Name: ruleName,
ID: ruleID,
InputChannel: inputChannel,
Schedule: pkgSch.Schedule{
Recurring: pkgSch.Daily,
RecurringPeriod: 1,
Time: now,
},
Status: re.EnabledStatus,
CreatedBy: userID,
DomainID: domainID,
},
addPoliciesErr: svcerr.ErrAuthorization,
err: svcerr.ErrAddPolicies,
},
{
desc: "Add rule with failed to add policies and failed rollback",
session: authn.Session{
UserID: userID,
DomainID: domainID,
},
rule: re.Rule{
Name: ruleName,
InputChannel: inputChannel,
Schedule: pkgSch.Schedule{
Recurring: pkgSch.Daily,
RecurringPeriod: 1,
Time: now,
},
},
res: re.Rule{
Name: ruleName,
ID: ruleID,
InputChannel: inputChannel,
Schedule: pkgSch.Schedule{
Recurring: pkgSch.Daily,
RecurringPeriod: 1,
Time: now,
},
Status: re.EnabledStatus,
CreatedBy: userID,
DomainID: domainID,
},
addPoliciesErr: svcerr.ErrAuthorization,
deleteErr: svcerr.ErrRemoveEntity,
err: svcerr.ErrRollbackRepo,
},
{
desc: "Add rule with failed to add roles",
session: authn.Session{
UserID: userID,
DomainID: domainID,
},
rule: re.Rule{
Name: ruleName,
InputChannel: inputChannel,
Schedule: pkgSch.Schedule{
Recurring: pkgSch.Daily,
RecurringPeriod: 1,
Time: now,
},
},
res: re.Rule{
Name: ruleName,
ID: ruleID,
InputChannel: inputChannel,
Schedule: pkgSch.Schedule{
Recurring: pkgSch.Daily,
RecurringPeriod: 1,
Time: now,
},
Status: re.EnabledStatus,
CreatedBy: userID,
DomainID: domainID,
},
addRoleErr: svcerr.ErrCreateEntity,
err: svcerr.ErrAddPolicies,
},
{
desc: "Add rule with Go script containing goroutines",
@@ -186,6 +337,10 @@ func TestAddRule(t *testing.T) {
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
repoCall := repo.On("AddRule", mock.Anything, mock.Anything).Return(tc.res, tc.err)
policyCall := policies.On("AddPolicies", context.Background(), mock.Anything).Return(tc.addPoliciesErr)
policyCall2 := policies.On("DeletePolicies", context.Background(), mock.Anything).Return(tc.deletePolicies).Maybe()
repoCall1 := repo.On("AddRoles", context.Background(), mock.Anything).Return([]roles.RoleProvision{}, tc.addRoleErr)
repoCall2 := repo.On("Remove", context.Background(), mock.Anything).Return(tc.deleteErr).Maybe()
res, err := svc.AddRule(context.Background(), tc.session, tc.rule)
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
if err == nil {
@@ -193,14 +348,18 @@ func TestAddRule(t *testing.T) {
assert.Equal(t, tc.rule.Name, res.Name)
assert.Equal(t, tc.rule.Schedule, res.Schedule)
}
defer repoCall.Unset()
policyCall.Unset()
policyCall2.Unset()
repoCall.Unset()
repoCall1.Unset()
repoCall2.Unset()
})
}
}
func TestViewRule(t *testing.T) {
// nolint:dogsled
svc, repo, _, _, _ := newService(t, make(chan pkglog.RunInfo))
svc, repo, _, _, _, _ := newService(t, make(chan pkglog.RunInfo))
now := time.Now().Add(time.Hour)
cases := []struct {
@@ -246,7 +405,7 @@ func TestViewRule(t *testing.T) {
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
repoCall := repo.On("ViewRule", mock.Anything, mock.Anything).Return(tc.res, tc.err)
res, err := svc.ViewRule(context.Background(), tc.session, tc.id)
res, err := svc.ViewRule(context.Background(), tc.session, tc.id, false)
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
if err == nil {
@@ -259,7 +418,7 @@ func TestViewRule(t *testing.T) {
func TestUpdateRule(t *testing.T) {
// nolint:dogsled
svc, repo, _, _, _ := newService(t, make(chan pkglog.RunInfo))
svc, repo, _, _, _, _ := newService(t, make(chan pkglog.RunInfo))
newName := namegen.Generate()
now := time.Now().Add(time.Hour)
@@ -370,7 +529,7 @@ func TestUpdateRule(t *testing.T) {
func TestUpdateRuleTags(t *testing.T) {
// nolint:dogsled
svc, repo, _, _, _ := newService(t, make(chan pkglog.RunInfo))
svc, repo, _, _, _, _ := newService(t, make(chan pkglog.RunInfo))
cases := []struct {
desc string
@@ -427,7 +586,7 @@ func TestUpdateRuleTags(t *testing.T) {
func TestUpdateRuleSchedule(t *testing.T) {
// nolint:dogsled
svc, repo, _, _, _ := newService(t, make(chan pkglog.RunInfo))
svc, repo, _, _, _, _ := newService(t, make(chan pkglog.RunInfo))
now := time.Now().UTC()
future := now.Add(2 * time.Hour)
@@ -495,7 +654,7 @@ func TestUpdateRuleSchedule(t *testing.T) {
func TestListRules(t *testing.T) {
// nolint:dogsled
svc, repo, _, _, _ := newService(t, make(chan pkglog.RunInfo))
svc, repo, _, _, _, _ := newService(t, make(chan pkglog.RunInfo))
numRules := 50
now := time.Now().Add(time.Hour)
var rules []re.Rule
@@ -629,13 +788,14 @@ func TestListRules(t *testing.T) {
func TestRemoveRule(t *testing.T) {
// nolint:dogsled
svc, repo, _, _, _ := newService(t, make(chan pkglog.RunInfo))
svc, repo, _, _, _, policies := newService(t, make(chan pkglog.RunInfo))
cases := []struct {
desc string
session authn.Session
id string
err error
desc string
session authn.Session
id string
err error
deletePoliciesErr error
}{
{
desc: "remove rule successfully",
@@ -643,8 +803,9 @@ func TestRemoveRule(t *testing.T) {
UserID: userID,
DomainID: domainID,
},
id: ruleID,
err: nil,
id: ruleID,
err: nil,
deletePoliciesErr: nil,
},
{
desc: "remove rule with failed repo",
@@ -652,25 +813,28 @@ func TestRemoveRule(t *testing.T) {
UserID: userID,
DomainID: domainID,
},
id: ruleID,
err: svcerr.ErrRemoveEntity,
id: ruleID,
err: svcerr.ErrRemoveEntity,
deletePoliciesErr: nil,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
repoCall := repo.On("RemoveRule", mock.Anything, mock.Anything).Return(tc.err)
policyCall := policies.On("DeletePolicies", context.Background(), mock.Anything).Return(tc.deletePoliciesErr)
err := svc.RemoveRule(context.Background(), tc.session, tc.id)
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
defer repoCall.Unset()
policyCall.Unset()
repoCall.Unset()
})
}
}
func TestEnableRule(t *testing.T) {
// nolint:dogsled
svc, repo, _, _, _ := newService(t, make(chan pkglog.RunInfo))
svc, repo, _, _, _, _ := newService(t, make(chan pkglog.RunInfo))
now := time.Now()
@@ -730,7 +894,7 @@ func TestEnableRule(t *testing.T) {
func TestDisableRule(t *testing.T) {
// nolint:dogsled
svc, repo, _, _, _ := newService(t, make(chan pkglog.RunInfo))
svc, repo, _, _, _, _ := newService(t, make(chan pkglog.RunInfo))
now := time.Now()
@@ -789,7 +953,7 @@ func TestDisableRule(t *testing.T) {
}
func TestHandle(t *testing.T) {
svc, repo, pubmocks, _, emailer := newService(t, make(chan pkglog.RunInfo))
svc, repo, pubmocks, _, emailer, _ := newService(t, make(chan pkglog.RunInfo))
now := time.Now()
scheduled := false
@@ -1461,7 +1625,8 @@ func TestHandle(t *testing.T) {
func TestStartScheduler(t *testing.T) {
now := time.Now().Truncate(time.Minute)
ri := make(chan pkglog.RunInfo)
svc, repo, _, ticker, _ := newService(t, ri)
// nolint:dogsled
svc, repo, _, ticker, _, _ := newService(t, ri)
ctxCases := []struct {
desc string
+350
View File
@@ -0,0 +1,350 @@
#!/bin/sh
# Copyright (c) Abstract Machines
# SPDX-License-Identifier: Apache-2.0
set -eu
SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
REPO_ROOT=$(CDPATH= cd -- "$SCRIPT_DIR/.." && pwd)
SUPERMQ_SCHEMA="$REPO_ROOT/docker/supermq-docker/spicedb/schema.zed"
OVERRIDE_SCHEMA="$REPO_ROOT/docker/spicedb/override-schema.zed"
OUTPUT_SCHEMA="$REPO_ROOT/docker/spicedb/combined-schema.zed"
if [ ! -f "$SUPERMQ_SCHEMA" ]; then
echo "ERROR: $SUPERMQ_SCHEMA not found" >&2
exit 1
fi
if [ ! -f "$OVERRIDE_SCHEMA" ]; then
echo "ERROR: $OVERRIDE_SCHEMA not found" >&2
exit 1
fi
mkdir -p "$(dirname "$OUTPUT_SCHEMA")"
tmp_supermq_schema=$(mktemp)
tmp_supermq_merged=$(mktemp)
tmp_override_remaining=$(mktemp)
tmp_overlay_domain=$(mktemp)
tmp_overlay_team=$(mktemp)
tmp_overlay_domain_relations=$(mktemp)
tmp_overlay_team_relations=$(mktemp)
tmp_overlay_domain_permissions=$(mktemp)
tmp_overlay_membership_extension=$(mktemp)
cleanup() {
rm -f "$tmp_supermq_schema"
rm -f "$tmp_supermq_merged"
rm -f "$tmp_override_remaining"
rm -f "$tmp_overlay_domain"
rm -f "$tmp_overlay_team"
rm -f "$tmp_overlay_domain_relations"
rm -f "$tmp_overlay_team_relations"
rm -f "$tmp_overlay_domain_permissions"
rm -f "$tmp_overlay_membership_extension"
}
trap cleanup EXIT INT TERM
cp "$SUPERMQ_SCHEMA" "$tmp_supermq_schema"
# Extract the first domain overlay block from override schema.
if ! awk '
BEGIN {
in_domain = 0
found = 0
}
!found && /^definition domain[[:space:]]*{/ {
in_domain = 1
found = 1
next
}
in_domain {
if ($0 ~ /^}/) {
in_domain = 0
next
}
print
}
END {
if (!found) {
exit 1
}
}
' "$OVERRIDE_SCHEMA" > "$tmp_overlay_domain"; then
echo "ERROR: definition domain block not found in $OVERRIDE_SCHEMA" >&2
exit 1
fi
# Extract the first team overlay block from override schema.
if ! awk '
BEGIN {
in_team = 0
found = 0
}
!found && /^definition team[[:space:]]*{/ {
in_team = 1
found = 1
next
}
in_team {
if ($0 ~ /^}/) {
in_team = 0
next
}
print
}
END {
if (!found) {
exit 1
}
}
' "$OVERRIDE_SCHEMA" > "$tmp_overlay_team"; then
echo "ERROR: definition team block not found in $OVERRIDE_SCHEMA" >&2
exit 1
fi
# Read explicit domain overlay relations from override domain block.
awk '
/^[[:space:]]*relation[[:space:]]+[A-Za-z0-9_]+:[[:space:]]*role#member[[:space:]]*\|[[:space:]]*team#member[[:space:]]*$/ {
line = $0
sub(/^[[:space:]]*/, "\t", line)
print line
}
' "$tmp_overlay_domain" > "$tmp_overlay_domain_relations"
# Read explicit team overlay relations from override team block.
awk '
/^[[:space:]]*relation[[:space:]]+[A-Za-z0-9_]+:[[:space:]]*role#member[[:space:]]*\|[[:space:]]*team#member[[:space:]]*$/ {
line = $0
sub(/^[[:space:]]*/, "\t", line)
print line
}
' "$tmp_overlay_team" > "$tmp_overlay_team_relations"
# Read explicit domain overlay permissions from override domain block.
awk '
/^[[:space:]]*permission[[:space:]]+[A-Za-z0-9_]+_permission[[:space:]]*=/ {
line = $0
sub(/^[[:space:]]*/, "\t", line)
print line
}
' "$tmp_overlay_domain" > "$tmp_overlay_domain_permissions"
# Read explicit domain membership extension expression from override domain block.
awk '
/^[[:space:]]*permission[[:space:]]+membership_extension[[:space:]]*=/ {
line = $0
sub(/^[[:space:]]*permission[[:space:]]+membership_extension[[:space:]]*=[[:space:]]*/, "", line)
print line
exit
}
' "$tmp_overlay_domain" > "$tmp_overlay_membership_extension"
if [ ! -s "$tmp_overlay_domain_relations" ]; then
echo "ERROR: no domain relation overlay lines found in definition domain block of $OVERRIDE_SCHEMA" >&2
exit 1
fi
if [ ! -s "$tmp_overlay_team_relations" ]; then
echo "ERROR: no team relation overlay lines found in definition team block of $OVERRIDE_SCHEMA" >&2
exit 1
fi
if [ ! -s "$tmp_overlay_domain_permissions" ]; then
echo "ERROR: no domain permission overlay lines found in definition domain block of $OVERRIDE_SCHEMA" >&2
exit 1
fi
if [ ! -s "$tmp_overlay_membership_extension" ]; then
echo "ERROR: permission membership_extension not found in definition domain block of $OVERRIDE_SCHEMA" >&2
exit 1
fi
# Remove the first domain and first team overlay blocks from override schema before appending.
if ! awk '
BEGIN {
skip_domain = 0
skip_team = 0
removed_domain = 0
removed_team = 0
}
!removed_domain && /^definition domain[[:space:]]*{/ {
skip_domain = 1
removed_domain = 1
next
}
skip_domain {
if ($0 ~ /^}/) {
skip_domain = 0
}
next
}
!removed_team && /^definition team[[:space:]]*{/ {
skip_team = 1
removed_team = 1
next
}
skip_team {
if ($0 ~ /^}/) {
skip_team = 0
}
next
}
{ print }
END {
if (!removed_domain || !removed_team) {
exit 1
}
}
' "$OVERRIDE_SCHEMA" > "$tmp_override_remaining"; then
echo "ERROR: failed to strip definition domain/team blocks from $OVERRIDE_SCHEMA" >&2
exit 1
fi
# Inject explicit override lines into SuperMQ domain and team definitions.
awk \
-v domain_relations_file="$tmp_overlay_domain_relations" \
-v team_relations_file="$tmp_overlay_team_relations" \
-v domain_permissions_file="$tmp_overlay_domain_permissions" \
-v membership_extension_file="$tmp_overlay_membership_extension" '
BEGIN {
while ((getline line < domain_relations_file) > 0) {
domain_relations = domain_relations line ORS
}
close(domain_relations_file)
while ((getline line < team_relations_file) > 0) {
team_relations = team_relations line ORS
}
close(team_relations_file)
while ((getline line < domain_permissions_file) > 0) {
domain_permissions = domain_permissions line ORS
}
close(domain_permissions_file)
membership_extension = ""
if ((getline line < membership_extension_file) > 0) {
membership_extension = line
}
close(membership_extension_file)
in_domain = 0
in_team = 0
in_domain_membership = 0
inserted_domain_relations = 0
inserted_team_relations = 0
inserted_domain_permissions = 0
inserted_domain_membership = 0
}
{
if ($0 ~ /^definition domain[[:space:]]*{/) {
in_domain = 1
} else if ($0 ~ /^definition team[[:space:]]*{/) {
in_team = 1
}
if (in_domain && $0 ~ /^[[:space:]]*permission membership[[:space:]]*=/) {
in_domain_membership = 1
}
if (in_domain && in_domain_membership && $0 ~ /organization->admin[[:space:]]*$/) {
membership_tail = $0
sub(/[[:space:]]*\+[[:space:]]*organization->admin[[:space:]]*$/, " +", membership_tail)
print membership_tail
print "\t" membership_extension " +"
print "\torganization->admin"
in_domain_membership = 0
inserted_domain_membership = 1
next
}
print $0
if (in_domain && $0 ~ /^[[:space:]]*relation group_view_role_users: role#member \| team#member[[:space:]]*$/) {
print ""
print "\t// Magistrala-specific relations"
printf "%s", domain_relations
inserted_domain_relations = 1
}
if (in_team && $0 ~ /^[[:space:]]*relation group_view_role_users: role#member \| team#member[[:space:]]*$/) {
print ""
print "\t// Magistrala-specific relations"
printf "%s", team_relations
inserted_team_relations = 1
}
if (in_domain && $0 ~ /^[[:space:]]*permission group_view_role_users_permission = group_view_role_users \+ team->group_view_role_users \+ organization->admin[[:space:]]*$/) {
print ""
print "\t// Magistrala-specific permissions"
printf "%s", domain_permissions
inserted_domain_permissions = 1
}
if (in_domain && $0 ~ /^}/) {
in_domain = 0
in_domain_membership = 0
} else if (in_team && $0 ~ /^}/) {
in_team = 0
}
}
END {
if (!inserted_domain_relations || !inserted_team_relations || !inserted_domain_permissions || !inserted_domain_membership) {
exit 1
}
}
' "$tmp_supermq_schema" > "$tmp_supermq_merged" || {
echo "ERROR: failed to merge override schema into SuperMQ schema" >&2
exit 1
}
first_domain_relation=$(awk 'NR == 1 {print $2}' "$tmp_overlay_domain_relations")
first_team_relation=$(awk 'NR == 1 {print $2}' "$tmp_overlay_team_relations")
first_domain_permission=$(awk 'NR == 1 {print $2}' "$tmp_overlay_domain_permissions")
if [ -z "$first_domain_relation" ] || [ -z "$first_team_relation" ] || [ -z "$first_domain_permission" ]; then
echo "ERROR: failed to verify merged overlay lines" >&2
exit 1
fi
sub_first_domain_relation=${first_domain_relation%%:*}
sub_first_team_relation=${first_team_relation%%:*}
if ! grep -Fq "relation $sub_first_domain_relation: role#member | team#member" "$tmp_supermq_merged"; then
echo "ERROR: merged schema is missing domain relation $sub_first_domain_relation" >&2
exit 1
fi
if ! grep -Fq "relation $sub_first_team_relation: role#member | team#member" "$tmp_supermq_merged"; then
echo "ERROR: merged schema is missing team relation $sub_first_team_relation" >&2
exit 1
fi
if ! grep -Fq "permission $first_domain_permission" "$tmp_supermq_merged"; then
echo "ERROR: merged schema is missing domain permission $first_domain_permission" >&2
exit 1
fi
membership_extension_value=$(cat "$tmp_overlay_membership_extension")
if ! grep -Fq "$membership_extension_value +" "$tmp_supermq_merged"; then
echo "ERROR: merged schema is missing domain membership_extension values" >&2
exit 1
fi
{
cat <<'EOF'
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
// Code generated by scripts/combine-schema.sh. DO NOT EDIT.
//
// Combined from:
// - docker/supermq-docker/spicedb/schema.zed
// - docker/spicedb/override-schema.zed
EOF
cat "$tmp_supermq_merged" "$tmp_override_remaining"
} > "$OUTPUT_SCHEMA"
echo "Combined schema generated at: $OUTPUT_SCHEMA"
+4 -1
View File
@@ -13,7 +13,8 @@ REPO_URL=https://github.com/absmach/supermq
TEMP_DIR="supermq"
DOCKER_DIR="docker"
DOCKER_DST_DIR="../docker"
DEST_DIR="../../docker/supermq-docker"
DEST_DIR="../docker/supermq-docker"
COMBINE_SCHEMA_SCRIPT="./combine-schema.sh"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR" || exit 1
@@ -40,3 +41,5 @@ mkdir -p "$DEST_DIR"
mv -f "$DOCKER_DIR"/.??* "$DOCKER_DIR"/* "$DEST_DIR"/
cd ..
rm -rf "$TEMP_DIR"
sh "$COMBINE_SCHEMA_SCRIPT"