Signed-off-by: Arvindh <arvindh91@gmail.com>
This commit is contained in:
Arvindh
2026-06-19 17:55:11 +05:30
parent 002e3c7fe7
commit 4cc2f297d7
20 changed files with 202 additions and 215 deletions
+2 -2
View File
@@ -154,7 +154,7 @@ jobs:
run: make all -j $(nproc) && make dockers_dev -j $(nproc)
- name: Start containers
run: make run_latest up args="-d" && make run_addons up args="-d"
run: make run_latest_ci up args="-d" && make run_addons up args="-d"
- name: Wait for services to be ready
run: |
@@ -289,4 +289,4 @@ jobs:
- name: Stop containers
if: always()
run: make run_latest down args="-v" && make run_addons down args="-v"
run: make run_latest_ci down args="-v" && make run_addons down args="-v"
@@ -50,6 +50,7 @@ jobs:
- "pkg/messaging/*.pb.go"
mocks:
- "tools/config/.mockery.yaml"
- ".github/workflows/check-generated-files.yaml"
- "pkg/sdk/sdk.go"
- "users/postgres/clients.go"
+5 -1
View File
@@ -144,7 +144,7 @@ FILTERED_SERVICES = $(filter-out $(RUN_ADDON_ARGS), $(SERVICES))
all: $(SERVICES)
.PHONY: all $(SERVICES) dockers dockers_dev latest release run_latest run_tls run_stable run_addons grpc_mtls_certs check_mtls check_certs test_api mocks
.PHONY: all $(SERVICES) dockers dockers_dev latest release run_latest run_latest_ci run_tls run_stable run_addons grpc_mtls_certs check_mtls check_certs test_api mocks
clean:
rm -rf ${BUILD_DIR}
@@ -292,6 +292,10 @@ run_latest: check_certs
$(SED_INPLACE) 's/^MG_RELEASE_TAG=.*/MG_RELEASE_TAG=latest/' docker/.env
$(DOCKER_PLATFORM) docker compose -f docker/docker-compose.yaml --env-file docker/.env -p $(DOCKER_PROJECT) $(DOCKER_COMPOSE_COMMAND) $(args)
run_latest_ci: check_certs
$(SED_INPLACE) 's/^MG_RELEASE_TAG=.*/MG_RELEASE_TAG=latest/' docker/.env
$(DOCKER_PLATFORM) docker compose -f docker/docker-compose.yaml -f docker/docker-compose-ci.yaml --env-file docker/.env -p $(DOCKER_PROJECT) $(DOCKER_COMPOSE_COMMAND) $(args)
run_tls:
@test -n "$(host)" || (echo "Usage: make run_tls host=example.com [email=admin@example.com] [letsencrypt=false] [staging=true] [force=true]" && exit 2)
@if [ "$(or $(letsencrypt),true)" != "false" ] && [ -z "$(email)" ]; then echo "Usage: make run_tls host=example.com email=admin@example.com [letsencrypt=false] [staging=true] [force=true]"; exit 2; fi
+7
View File
@@ -0,0 +1,7 @@
# Copyright (c) Abstract Machines
# SPDX-License-Identifier: Apache-2.0
services:
atom-ui:
profiles:
- atom-ui
+9 -3
View File
@@ -61,7 +61,9 @@ func MakePublishHandler(
r.Get("/health", func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", contentType)
w.WriteHeader(http.StatusOK)
_ = json.NewEncoder(w).Encode(publishResponse{Status: "ok"})
if err := json.NewEncoder(w).Encode(publishResponse{Status: "ok"}); err != nil {
return
}
})
return r
}
@@ -129,7 +131,9 @@ func (h publishHandler) publish(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", contentType)
w.WriteHeader(http.StatusAccepted)
_ = json.NewEncoder(w).Encode(publishResponse{Status: "accepted"})
if err := json.NewEncoder(w).Encode(publishResponse{Status: "accepted"}); err != nil {
return
}
}
func (h publishHandler) ensureUserPublish(
@@ -248,5 +252,7 @@ func attrString(attrs atom.Attributes, key string) string {
func writeError(w http.ResponseWriter, status int, message string) {
w.Header().Set("Content-Type", contentType)
w.WriteHeader(status)
_ = json.NewEncoder(w).Encode(errorResponse{Error: message})
if err := json.NewEncoder(w).Encode(errorResponse{Error: message}); err != nil {
return
}
}
+9 -9
View File
@@ -47,23 +47,23 @@ func SubjectID(session authn.Session) string {
func ObjectKind(legacyObjectType, resourceKind string) string {
switch legacyObjectType {
case policies.DomainType:
return "tenant"
return atomObjectKindTenant
case policies.PlatformType:
return policies.PlatformType
case policies.ClientType:
return "entity"
return atomObjectKindEntity
case policies.GroupType:
return "group"
return atomObjectKindGroup
case policies.ChannelType, policies.RulesType, policies.ReportsType, policies.AlarmsType:
return "resource"
return atomObjectKindResource
}
switch resourceKind {
case KindClient, "device":
return "entity"
case "group":
return "group"
case KindClient, atomKindDevice:
return atomObjectKindEntity
case atomKindGroup:
return atomObjectKindGroup
case KindChannel, KindRule, KindReport, KindAlarm:
return "resource"
return atomObjectKindResource
default:
return resourceKind
}
+12 -12
View File
@@ -57,12 +57,12 @@ func CapabilityName(action string) string {
case normalized == policies.AdminPermission,
normalized == "admin_permission",
strings.Contains(normalized, "manage_role"):
return "manage"
return atomActionManage
case normalized == policies.ViewPermission,
normalized == "read",
normalized == atomActionRead,
strings.Contains(normalized, "read"),
strings.Contains(normalized, "view"):
return "read"
return atomActionRead
case normalized == policies.CreatePermission,
normalized == "write",
strings.Contains(normalized, "create"),
@@ -73,17 +73,17 @@ func CapabilityName(action string) string {
strings.Contains(normalized, "assign"),
strings.Contains(normalized, "acknowledge"),
strings.Contains(normalized, "resolve"):
return "write"
return atomActionWrite
case normalized == policies.DeletePermission,
strings.Contains(normalized, "delete"),
strings.Contains(normalized, "remove"):
return "delete"
return atomActionDelete
case normalized == policies.PublishPermission:
return "publish"
return atomActionPublish
case normalized == policies.SubscribePermission:
return "subscribe"
return atomActionSubscribe
case normalized == "generate", normalized == "execute":
return "execute"
return atomActionExecute
case normalized == "list":
return "list"
default:
@@ -98,9 +98,9 @@ func legacyResourceKind(objectKind, objectType string) string {
case policies.ClientsKind, policies.NewClientKind:
return "client"
case policies.GroupsKind, policies.NewGroupKind:
return "group"
return atomObjectKindGroup
case policies.DomainsKind:
return "tenant"
return atomObjectKindTenant
default:
switch objectType {
case policies.ChannelType:
@@ -108,9 +108,9 @@ func legacyResourceKind(objectKind, objectType string) string {
case policies.ClientType:
return "client"
case policies.GroupType:
return "group"
return atomObjectKindGroup
case policies.DomainType:
return "tenant"
return atomObjectKindTenant
case policies.RulesType:
return KindRule
case policies.ReportsType:
+33 -33
View File
@@ -9,53 +9,53 @@ import (
)
var magistralaActionDescriptions = map[string]string{
"read": "Read / view an object",
"write": "Create or update an object",
"delete": "Delete an object",
"manage": "Full administrative control",
"publish": "Publish messages to a channel",
"subscribe": "Subscribe to channel messages",
"execute": "Execute a command or action",
atomActionRead: "Read / view an object",
atomActionWrite: "Create or update an object",
atomActionDelete: "Delete an object",
atomActionManage: "Full administrative control",
atomActionPublish: "Publish messages to a channel",
atomActionSubscribe: "Subscribe to channel messages",
atomActionExecute: "Execute a command or action",
}
var magistralaActionApplicability = []CapabilityApplicabilitySpec{
{ActionName: "read", ObjectKind: "resource", ObjectType: "resource:channel"},
{ActionName: "write", ObjectKind: "resource", ObjectType: "resource:channel"},
{ActionName: "delete", ObjectKind: "resource", ObjectType: "resource:channel"},
{ActionName: "manage", ObjectKind: "resource", ObjectType: "resource:channel"},
{ActionName: "publish", ObjectKind: "resource", ObjectType: "resource:channel"},
{ActionName: "subscribe", ObjectKind: "resource", ObjectType: "resource:channel"},
{ActionName: atomActionRead, ObjectKind: atomObjectKindResource, ObjectType: "resource:channel"},
{ActionName: atomActionWrite, ObjectKind: atomObjectKindResource, ObjectType: "resource:channel"},
{ActionName: atomActionDelete, ObjectKind: atomObjectKindResource, ObjectType: "resource:channel"},
{ActionName: atomActionManage, ObjectKind: atomObjectKindResource, ObjectType: "resource:channel"},
{ActionName: atomActionPublish, ObjectKind: atomObjectKindResource, ObjectType: "resource:channel"},
{ActionName: atomActionSubscribe, ObjectKind: atomObjectKindResource, ObjectType: "resource:channel"},
{ActionName: "read", ObjectKind: "resource", ObjectType: "resource:rule"},
{ActionName: "write", ObjectKind: "resource", ObjectType: "resource:rule"},
{ActionName: "delete", ObjectKind: "resource", ObjectType: "resource:rule"},
{ActionName: "manage", ObjectKind: "resource", ObjectType: "resource:rule"},
{ActionName: "execute", ObjectKind: "resource", ObjectType: "resource:rule"},
{ActionName: atomActionRead, ObjectKind: atomObjectKindResource, ObjectType: "resource:rule"},
{ActionName: atomActionWrite, ObjectKind: atomObjectKindResource, ObjectType: "resource:rule"},
{ActionName: atomActionDelete, ObjectKind: atomObjectKindResource, ObjectType: "resource:rule"},
{ActionName: atomActionManage, ObjectKind: atomObjectKindResource, ObjectType: "resource:rule"},
{ActionName: atomActionExecute, ObjectKind: atomObjectKindResource, ObjectType: "resource:rule"},
{ActionName: "read", ObjectKind: "resource", ObjectType: "resource:report"},
{ActionName: "write", ObjectKind: "resource", ObjectType: "resource:report"},
{ActionName: "delete", ObjectKind: "resource", ObjectType: "resource:report"},
{ActionName: "manage", ObjectKind: "resource", ObjectType: "resource:report"},
{ActionName: "execute", ObjectKind: "resource", ObjectType: "resource:report"},
{ActionName: atomActionRead, ObjectKind: atomObjectKindResource, ObjectType: "resource:report"},
{ActionName: atomActionWrite, ObjectKind: atomObjectKindResource, ObjectType: "resource:report"},
{ActionName: atomActionDelete, ObjectKind: atomObjectKindResource, ObjectType: "resource:report"},
{ActionName: atomActionManage, ObjectKind: atomObjectKindResource, ObjectType: "resource:report"},
{ActionName: atomActionExecute, ObjectKind: atomObjectKindResource, ObjectType: "resource:report"},
{ActionName: "read", ObjectKind: "resource", ObjectType: "resource:alarm"},
{ActionName: "write", ObjectKind: "resource", ObjectType: "resource:alarm"},
{ActionName: "delete", ObjectKind: "resource", ObjectType: "resource:alarm"},
{ActionName: "manage", ObjectKind: "resource", ObjectType: "resource:alarm"},
{ActionName: atomActionRead, ObjectKind: atomObjectKindResource, ObjectType: "resource:alarm"},
{ActionName: atomActionWrite, ObjectKind: atomObjectKindResource, ObjectType: "resource:alarm"},
{ActionName: atomActionDelete, ObjectKind: atomObjectKindResource, ObjectType: "resource:alarm"},
{ActionName: atomActionManage, ObjectKind: atomObjectKindResource, ObjectType: "resource:alarm"},
}
var magistralaActionAssignmentRules = []ActionAssignmentRuleSpec{
{
EntityKind: "device",
ActionName: "publish",
ObjectKind: "resource",
EntityKind: atomKindDevice,
ActionName: atomActionPublish,
ObjectKind: atomObjectKindResource,
ObjectType: "resource:channel",
Decision: "allow",
},
{
EntityKind: "device",
ActionName: "subscribe",
ObjectKind: "resource",
EntityKind: atomKindDevice,
ActionName: atomActionSubscribe,
ObjectKind: atomObjectKindResource,
ObjectType: "resource:channel",
Decision: "allow",
},
+9 -9
View File
@@ -15,16 +15,16 @@ import (
func TestBootstrapMagistralaActionsCreatesMissingActionsAndApplicability(t *testing.T) {
actions := map[string]Capability{
"read": {ID: "read-id", Name: "read"},
"write": {ID: "write-id", Name: "write"},
"delete": {ID: "delete-id", Name: "delete"},
"manage": {ID: "manage-id", Name: "manage"},
atomActionRead: {ID: "read-id", Name: atomActionRead},
atomActionWrite: {ID: "write-id", Name: atomActionWrite},
atomActionDelete: {ID: "delete-id", Name: atomActionDelete},
atomActionManage: {ID: "manage-id", Name: atomActionManage},
}
var applicability []map[string]any
var assignmentRules []map[string]any
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost || r.URL.Path != "/graphql" {
if r.Method != http.MethodPost || r.URL.Path != atomGraphQLPath {
t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path)
}
var payload struct {
@@ -103,7 +103,7 @@ func TestBootstrapMagistralaActionsCreatesMissingActionsAndApplicability(t *test
t.Fatalf("bootstrap failed: %v", err)
}
for _, name := range []string{"read", "write", "delete", "manage", "publish", "subscribe", "execute"} {
for _, name := range []string{atomActionRead, atomActionWrite, atomActionDelete, atomActionManage, atomActionPublish, atomActionSubscribe, atomActionExecute} {
if _, ok := actions[name]; !ok {
t.Fatalf("action %q was not ensured", name)
}
@@ -118,14 +118,14 @@ func TestBootstrapMagistralaActionsCreatesMissingActionsAndApplicability(t *test
if len(assignmentRules) != len(magistralaActionAssignmentRules) {
t.Fatalf("unexpected assignment guardrail count: got %d want %d", len(assignmentRules), len(magistralaActionAssignmentRules))
}
assertAssignmentRule(t, assignmentRules, "device", "publish", "resource", "resource:channel", "allow")
assertAssignmentRule(t, assignmentRules, "device", "subscribe", "resource", "resource:channel", "allow")
assertAssignmentRule(t, assignmentRules, atomKindDevice, atomActionPublish, atomObjectKindResource, "resource:channel", "allow")
assertAssignmentRule(t, assignmentRules, atomKindDevice, atomActionSubscribe, atomObjectKindResource, "resource:channel", "allow")
}
func assertApplicability(t *testing.T, entries []map[string]any, actionID, objectType string) {
t.Helper()
for _, entry := range entries {
if entry["actionId"] == actionID && entry["objectKind"] == "resource" && entry["objectType"] == objectType {
if entry["actionId"] == actionID && entry["objectKind"] == atomObjectKindResource && entry["objectType"] == objectType {
return
}
}
+4 -4
View File
@@ -17,7 +17,7 @@ import (
func TestUpsertResourceCreatesThenUpdatesOnConflict(t *testing.T) {
var operations []string
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost || r.URL.Path != "/graphql" {
if r.Method != http.MethodPost || r.URL.Path != atomGraphQLPath {
t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path)
}
body, _ := io.ReadAll(r.Body)
@@ -61,7 +61,7 @@ func TestUpsertResourceCreatesThenUpdatesOnConflict(t *testing.T) {
func TestListResources(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost || r.URL.Path != "/graphql" {
if r.Method != http.MethodPost || r.URL.Path != atomGraphQLPath {
t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path)
}
var payload struct {
@@ -70,7 +70,7 @@ func TestListResources(t *testing.T) {
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
t.Fatalf("decode request: %v", err)
}
if payload.Variables["kind"] != KindRule || payload.Variables["tenantId"] != "domain-1" {
if payload.Variables["kind"] != KindRule || payload.Variables["tenantId"] != testDomainID {
t.Fatalf("unexpected variables: %+v", payload.Variables)
}
_ = json.NewEncoder(w).Encode(map[string]any{
@@ -85,7 +85,7 @@ func TestListResources(t *testing.T) {
defer srv.Close()
client := NewClient(Config{URL: srv.URL, Timeout: time.Second})
got, err := client.ListResources(context.Background(), Query{Kind: KindRule, TenantID: "domain-1"})
got, err := client.ListResources(context.Background(), Query{Kind: KindRule, TenantID: testDomainID})
if err != nil {
t.Fatalf("list failed: %v", err)
}
+4 -2
View File
@@ -10,8 +10,10 @@ import (
"time"
)
const defaultTimeout = 5 * time.Second
const defaultAdminUsername = "admin"
const (
defaultTimeout = 5 * time.Second
defaultAdminUsername = "admin"
)
// Config controls Magistrala's optional Atom integration.
type Config struct {
+41
View File
@@ -0,0 +1,41 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package atom
const (
atomActionRead = "read"
atomActionWrite = "write"
atomActionDelete = "delete"
atomActionManage = "manage"
atomActionPublish = "publish"
atomActionSubscribe = "subscribe"
atomActionExecute = "execute"
)
const (
atomStatusActive = "active"
atomStatusInactive = "inactive"
atomStatusEnabled = "enabled"
atomStatusDisabled = "disabled"
atomStatusFrozen = "frozen"
atomStatusSuspended = "suspended"
atomStatusDeleted = "deleted"
)
const (
atomKindDevice = "device"
atomKindGroup = "group"
atomKindHuman = "human"
)
const (
atomObjectKindEntity = "entity"
atomObjectKindGroup = "group"
atomObjectKindResource = "resource"
atomObjectKindTenant = "tenant"
)
const atomScopeModeObject = "object"
const atomGraphQLPath = "/graphql"
+4 -4
View File
@@ -145,7 +145,7 @@ func (c AtomChannelsCompat) Authorize(ctx context.Context, in *channelsv1.AuthzR
SubjectID: subjectID,
Action: action,
ResourceID: in.GetChannelId(),
ObjectKind: "resource",
ObjectKind: atomObjectKindResource,
ObjectID: in.GetChannelId(),
Context: map[string]any{
"domain_id": in.GetDomainId(),
@@ -196,11 +196,11 @@ func (c AtomChannelsCompat) RetrieveIDByRoute(ctx context.Context, in *commonv1.
func atomStatusCode(value string) uint32 {
switch strings.ToLower(value) {
case "", "active", "enabled":
case "", atomStatusActive, atomStatusEnabled:
return 0
case "inactive", "disabled", "frozen", "suspended":
case atomStatusInactive, atomStatusDisabled, atomStatusFrozen, atomStatusSuspended:
return 1
case "deleted":
case atomStatusDeleted:
return 2
default:
return 0
+6 -6
View File
@@ -37,12 +37,12 @@ func TestAtomClientsCompatAuthenticatesBasicPasswordWithAtomLogin(t *testing.T)
if err := json.NewDecoder(r.Body).Decode(&got); err != nil {
t.Fatalf("decode login request: %v", err)
}
if got.Identifier != "entity-1" || got.Secret != "device-secret" || got.Kind != "password" {
if got.Identifier != testEntityID || got.Secret != testDeviceSecret || got.Kind != "password" {
t.Fatalf("unexpected login request: %+v", got)
}
_ = json.NewEncoder(w).Encode(LoginResponse{
Token: "jwt",
EntityID: "entity-1",
EntityID: testEntityID,
SessionID: "session-1",
ExpiresAt: time.Now().Add(time.Hour),
})
@@ -51,13 +51,13 @@ func TestAtomClientsCompatAuthenticatesBasicPasswordWithAtomLogin(t *testing.T)
fallback := &recordingAuthn{}
compat := NewClientsCompat(fallback, NewClient(Config{URL: srv.URL, Timeout: time.Second}))
token := smqauthn.AuthPack(smqauthn.BasicAuth, "entity-1", "device-secret")
token := smqauthn.AuthPack(smqauthn.BasicAuth, testEntityID, testDeviceSecret)
res, err := compat.Authenticate(context.Background(), &clientsv1.AuthnReq{Token: token})
if err != nil {
t.Fatalf("authenticate basic password: %v", err)
}
if !res.GetAuthenticated() || res.GetId() != "entity-1" {
if !res.GetAuthenticated() || res.GetId() != testEntityID {
t.Fatalf("unexpected response: %+v", res)
}
if fallback.called {
@@ -73,7 +73,7 @@ func TestAtomClientsCompatFallsBackToBearerTokenWhenBasicPasswordRejected(t *tes
fallback := &recordingAuthn{session: smqauthn.Session{UserID: "entity-2"}}
compat := NewClientsCompat(fallback, NewClient(Config{URL: srv.URL, Timeout: time.Second}))
token := smqauthn.AuthPack(smqauthn.BasicAuth, "entity-1", "atom_token")
token := smqauthn.AuthPack(smqauthn.BasicAuth, testEntityID, "atom_token")
res, err := compat.Authenticate(context.Background(), &clientsv1.AuthnReq{Token: token})
if err != nil {
@@ -95,7 +95,7 @@ func TestAtomClientsCompatDoesNotHideAtomPasswordLoginFailures(t *testing.T) {
fallback := &recordingAuthn{session: smqauthn.Session{UserID: "entity-2"}}
compat := NewClientsCompat(fallback, NewClient(Config{URL: srv.URL, Timeout: time.Second}))
token := smqauthn.AuthPack(smqauthn.BasicAuth, "entity-1", "device-secret")
token := smqauthn.AuthPack(smqauthn.BasicAuth, testEntityID, testDeviceSecret)
_, err := compat.Authenticate(context.Background(), &clientsv1.AuthnReq{Token: token})
if err == nil {
+15 -14
View File
@@ -53,14 +53,14 @@ func EntityFromFields(f ObjectFields) Entity {
func tenantStatus(status string) string {
switch status {
case "enabled":
return "active"
case "disabled":
return "inactive"
case atomStatusEnabled:
return atomStatusActive
case atomStatusDisabled:
return atomStatusInactive
case "freezed":
return "frozen"
case "deleted":
return "deleted"
return atomStatusFrozen
case atomStatusDeleted:
return atomStatusDeleted
default:
return status
}
@@ -68,10 +68,10 @@ func tenantStatus(status string) string {
func entityStatus(status string) string {
switch status {
case "enabled":
return "active"
case "disabled", "deleted":
return "inactive"
case atomStatusEnabled:
return atomStatusActive
case atomStatusDisabled, atomStatusDeleted:
return atomStatusInactive
default:
return status
}
@@ -80,9 +80,9 @@ func entityStatus(status string) string {
func entityKind(kind string) string {
switch kind {
case KindUser:
return "human"
return atomKindHuman
case KindClient:
return "device"
return atomKindDevice
default:
return kind
}
@@ -175,7 +175,8 @@ func cloneMap(in map[string]any) map[string]any {
func timeString(t interface {
IsZero() bool
Format(string) string
}) string {
},
) string {
if t.IsZero() {
return ""
}
+7 -7
View File
@@ -58,21 +58,21 @@ func policyAction(pr policies.Policy) string {
func policyObjectKind(pr policies.Policy) string {
switch pr.ObjectType {
case policies.DomainType:
return "tenant"
return atomObjectKindTenant
case policies.PlatformType:
return policies.PlatformType
case policies.ClientType:
return "entity"
return atomObjectKindEntity
case policies.GroupType:
return "group"
return atomObjectKindGroup
case policies.ChannelType:
return "resource"
return atomObjectKindResource
case policies.RulesType:
return "resource"
return atomObjectKindResource
case policies.ReportsType:
return "resource"
return atomObjectKindResource
case policies.AlarmsType:
return "resource"
return atomObjectKindResource
default:
return pr.ObjectType
}
+9 -9
View File
@@ -188,9 +188,9 @@ func isSupportedObjectList(pr policies.Policy) bool {
func policyGrantSubjectKind(pr policies.Policy) string {
if pr.SubjectType == policies.GroupType || pr.SubjectKind == policies.GroupsKind {
return "group"
return atomObjectKindGroup
}
return "entity"
return atomObjectKindEntity
}
func policyGrantScopeMode(pr policies.Policy) string {
@@ -198,28 +198,28 @@ func policyGrantScopeMode(pr policies.Policy) string {
case policies.PlatformType:
return "platform"
case policies.DomainType:
return "tenant"
return atomObjectKindTenant
default:
return "object"
return atomScopeModeObject
}
}
func policyGrantObjectKind(pr policies.Policy) string {
if policyGrantScopeMode(pr) != "object" {
if policyGrantScopeMode(pr) != atomScopeModeObject {
return ""
}
if pr.ObjectType == policies.ClientType {
return "entity"
return atomObjectKindEntity
}
return "resource"
return atomObjectKindResource
}
func policyGrantObjectType(pr policies.Policy) string {
if policyGrantScopeMode(pr) != "object" {
if policyGrantScopeMode(pr) != atomScopeModeObject {
return ""
}
if pr.ObjectType == policies.ClientType {
return "entity:" + entityKind(KindClient)
return atomObjectKindEntity + ":" + entityKind(KindClient)
}
switch pr.ObjectType {
case policies.ChannelType:
+15 -15
View File
@@ -73,8 +73,8 @@ func TestPolicyServiceListAllObjectsUsesAtomAuthorizedObjectIds(t *testing.T) {
page, err := svc.ListAllObjects(context.Background(), policies.Policy{
SubjectType: policies.UserType,
Subject: "domain-1_user-1",
Domain: "domain-1",
Subject: testDomainID + "_user-1",
Domain: testDomainID,
ObjectType: policies.ClientType,
Permission: policies.ViewPermission,
})
@@ -89,10 +89,10 @@ func TestPolicyServiceListAllObjectsUsesAtomAuthorizedObjectIds(t *testing.T) {
}
query := client.queries[0]
if query.SubjectID != "user-1" ||
query.Action != "read" ||
query.ObjectKind != "entity" ||
query.Action != atomActionRead ||
query.ObjectKind != atomObjectKindEntity ||
query.ObjectType != entityKind(KindClient) ||
query.TenantID != "domain-1" {
query.TenantID != testDomainID {
t.Fatalf("unexpected authorized object query: %+v", query)
}
}
@@ -102,8 +102,8 @@ func TestPolicyServiceAddPolicyCreatesInternalCapabilityPolicy(t *testing.T) {
svc := NewPolicyService(client)
err := svc.AddPolicy(context.Background(), policies.Policy{
Domain: "domain-1",
Subject: "domain-1_client-1",
Domain: testDomainID,
Subject: testDomainID + "_client-1",
SubjectType: policies.ClientType,
Object: "channel-1",
ObjectType: policies.ChannelType,
@@ -116,9 +116,9 @@ func TestPolicyServiceAddPolicyCreatesInternalCapabilityPolicy(t *testing.T) {
t.Fatalf("expected one permission block and direct policy, got %d/%d", len(client.blocks), len(client.created))
}
block := client.blocks[0]
if block.TenantID != "domain-1" ||
block.ScopeMode != "object" ||
block.ObjectKind != "resource" ||
if block.TenantID != testDomainID ||
block.ScopeMode != atomScopeModeObject ||
block.ObjectKind != atomObjectKindResource ||
block.ObjectType != "resource:channel" ||
block.ObjectID != "channel-1" ||
block.Effect != "allow" ||
@@ -127,8 +127,8 @@ func TestPolicyServiceAddPolicyCreatesInternalCapabilityPolicy(t *testing.T) {
t.Fatalf("unexpected permission block: %+v", block)
}
created := client.created[0]
if created.TenantID != "domain-1" ||
created.SubjectKind != "entity" ||
if created.TenantID != testDomainID ||
created.SubjectKind != atomObjectKindEntity ||
created.SubjectID != "client-1" ||
created.PermissionBlockID != "block-1" {
t.Fatalf("unexpected direct policy: %+v", created)
@@ -144,7 +144,7 @@ func TestPolicyServiceDeletePolicyFilterRemovesMatchingCapabilityPolicy(t *testi
PermissionBlock: PermissionBlock{
ID: "keep-block",
ScopeMode: "object",
ObjectKind: "resource",
ObjectKind: atomObjectKindResource,
ObjectType: "resource:channel",
ObjectID: "channel-1",
Actions: []Capability{{ID: "cap-other"}},
@@ -155,7 +155,7 @@ func TestPolicyServiceDeletePolicyFilterRemovesMatchingCapabilityPolicy(t *testi
PermissionBlock: PermissionBlock{
ID: "delete-block",
ScopeMode: "object",
ObjectKind: "resource",
ObjectKind: atomObjectKindResource,
ObjectType: "resource:channel",
ObjectID: "channel-1",
Actions: []Capability{{ID: "cap-subscribe"}},
@@ -166,7 +166,7 @@ func TestPolicyServiceDeletePolicyFilterRemovesMatchingCapabilityPolicy(t *testi
svc := NewPolicyService(client)
err := svc.DeletePolicyFilter(context.Background(), policies.Policy{
Domain: "domain-1",
Domain: testDomainID,
Subject: "domain-1_client-1",
SubjectType: policies.ClientType,
Object: "channel-1",
+10
View File
@@ -0,0 +1,10 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package atom
const (
testDeviceSecret = "device-secret"
testDomainID = "domain-1"
testEntityID = "entity-1"
)
-85
View File
@@ -12,20 +12,6 @@ force-file-write: true
include-auto-generated: true
packages:
github.com/absmach/magistrala/api/grpc/clients/v1:
interfaces:
ClientsServiceClient:
config:
dir: "./clients/mocks"
structname: "ClientsServiceClient"
filename: "clients_client.go"
github.com/absmach/magistrala/api/grpc/domains/v1:
interfaces:
DomainsServiceClient:
config:
dir: "./domains/mocks"
structname: "DomainsServiceClient"
filename: "domains_client.go"
github.com/absmach/magistrala/api/grpc/token/v1:
interfaces:
TokenServiceClient:
@@ -33,20 +19,6 @@ packages:
dir: "./auth/mocks"
structname: "TokenServiceClient"
filename: "token_client.go"
github.com/absmach/magistrala/api/grpc/channels/v1:
interfaces:
ChannelsServiceClient:
config:
dir: "./channels/mocks"
structname: "ChannelsServiceClient"
filename: "channels_client.go"
github.com/absmach/magistrala/api/grpc/groups/v1:
interfaces:
GroupsServiceClient:
config:
dir: "./groups/mocks"
structname: "GroupsServiceClient"
filename: "groups_client.go"
github.com/absmach/magistrala/api/grpc/certs/v1:
interfaces:
CertsServiceClient:
@@ -79,22 +51,6 @@ packages:
PATS:
PATSRepository:
Service:
github.com/absmach/magistrala/channels:
interfaces:
Cache:
Repository:
Service:
github.com/absmach/magistrala/channels/private:
interfaces:
Service:
github.com/absmach/magistrala/clients:
interfaces:
Repository:
Cache:
Service:
github.com/absmach/magistrala/clients/private:
interfaces:
Service:
github.com/absmach/magistrala/certs:
interfaces:
Agent:
@@ -107,21 +63,6 @@ packages:
interfaces:
Service:
SubscriptionsRepository:
github.com/absmach/magistrala/domains:
interfaces:
Repository:
Cache:
Service:
github.com/absmach/magistrala/domains/private:
interfaces:
Service:
github.com/absmach/magistrala/groups:
interfaces:
Repository:
Service:
github.com/absmach/magistrala/groups/private:
interfaces:
Service:
github.com/absmach/magistrala/journal:
interfaces:
Repository:
@@ -143,18 +84,10 @@ packages:
github.com/absmach/magistrala/pkg/messaging:
interfaces:
PubSub:
github.com/absmach/magistrala/pkg/oauth2:
interfaces:
Provider:
github.com/absmach/magistrala/pkg/policies:
interfaces:
Evaluator:
Service:
github.com/absmach/magistrala/pkg/roles:
interfaces:
Provisioner:
RoleManager:
Repository:
github.com/absmach/magistrala/pkg/callout:
interfaces:
Callout:
@@ -168,18 +101,6 @@ packages:
interfaces:
Repository:
Service:
github.com/absmach/magistrala/bootstrap:
interfaces:
ConfigRepository:
ConfigReader:
Service:
ProfileRepository:
BindingStore:
BindingResolver:
Renderer:
github.com/absmach/magistrala/provision:
interfaces:
Service:
github.com/absmach/magistrala/alarms:
interfaces:
Service:
@@ -188,12 +109,6 @@ packages:
interfaces:
Service:
Repository:
github.com/absmach/magistrala/users:
interfaces:
Emailer:
Hasher:
Repository:
Service:
github.com/absmach/magistrala/notifications:
interfaces:
Notifier: