mirror of
https://github.com/absmach/magistrala.git
synced 2026-06-23 04:10:28 +00:00
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
# Copyright (c) Abstract Machines
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
services:
|
||||
atom-ui:
|
||||
profiles:
|
||||
- atom-ui
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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",
|
||||
},
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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 ""
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user