Files
Arvindh 16ba29cf4a
Property Based Tests / api-test (push) Has been cancelled
Continuous Delivery / lint-and-build (push) Has been cancelled
Deploy GitHub Pages / swagger-ui (push) Has been cancelled
CI Pipeline / Lint Proto (push) Has been cancelled
CI Pipeline / Detect Changes (push) Has been cancelled
Continuous Delivery / Build and Push Docker Images (push) Has been cancelled
CI Pipeline / lint-and-build (push) Has been cancelled
CI Pipeline / Test ${{ matrix.module }} (push) Has been cancelled
CI Pipeline / Upload Coverage (push) Has been cancelled
MG-3537 - Migrate Magistrala to ATOM for identity & authorization (#3532)
Signed-off-by: Arvindh <arvindh91@gmail.com>
Signed-off-by: dusan <borovcanindusan1@gmail.com>
Signed-off-by: Rodney Osodo <socials@rodneyosodo.com>
Co-authored-by: Dušan Borovčanin <dusan.borovcanin@absmach.eu>
Co-authored-by: Rodney Osodo <socials@rodneyosodo.com>
Co-authored-by: dusan <borovcanindusan1@gmail.com>
2026-06-26 22:06:57 +02:00

146 lines
3.9 KiB
Go

// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package main
import (
"regexp"
"strings"
"github.com/google/uuid"
)
// uuidNS is the deterministic namespace for derived Atom UUIDs (roles,
// permission blocks) whose Magistrala source id is not a UUID. Using uuidv5
// keeps the migration idempotent across re-runs.
var uuidNS = uuid.MustParse("a70a0000-0000-5000-a000-000000000000")
const (
statusActive = "active"
actionCreate = "create"
actionDelete = "delete"
actionManage = "manage"
actionPublish = "publish"
actionRead = "read"
actionSubscribe = "subscribe"
)
func derivedUUID(parts ...string) string {
return uuid.NewSHA1(uuidNS, []byte(strings.Join(parts, "|"))).String()
}
// --- status mapping (Magistrala smallint -> Atom enum text) ---
// entityStatus maps to entities.status / object_groups.status (active/inactive/suspended).
func entityStatus(s int16) string {
switch s {
case 0:
return statusActive
case 1:
return "inactive"
default:
return "suspended"
}
}
// tenantStatus maps to tenants.status (active/inactive/frozen/deleted).
func tenantStatus(s int16) string {
switch s {
case 0:
return statusActive
case 1:
return "inactive"
case 2:
return "frozen"
default:
return statusActive
}
}
// --- alias normalization (Atom 004/005 slug rules) ---
var (
slugRe = regexp.MustCompile(`^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$`)
uuidRe = regexp.MustCompile(`^([0-9a-f]{32}|[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$`)
)
// normalizeAlias lowercases and validates a candidate alias. Returns ("", false)
// when the value cannot be a valid Atom alias (caller keeps the UUID identity and
// drops the alias, logging it).
func normalizeAlias(raw string) (string, bool) {
a := strings.ToLower(strings.TrimSpace(raw))
if a == "" {
return "", false
}
if uuidRe.MatchString(a) {
return "", false // UUID-shaped aliases collide with id-addressing
}
if !slugRe.MatchString(a) {
return "", false
}
return a, true
}
// --- action vocabulary mapping (Magistrala -> Atom) ---
// Atom actions: read create write delete revoke rotate publish subscribe execute
// manage policy.manage role.manage authz.check.
//
// mapAction translates a Magistrala role action string. Returns ("", false) when
// unmapped; caller decides fallback per --unmapped-action.
func mapAction(a string) (string, bool) {
a = strings.ToLower(strings.TrimSpace(a))
switch a {
case actionPublish:
return actionPublish, true
case actionSubscribe:
return actionSubscribe, true
case actionCreate:
return actionCreate, true
case "update":
return "write", true
case actionDelete:
return actionDelete, true
case "read", "view":
return actionRead, true
case "admin", actionManage:
return actionManage, true
}
switch {
case strings.HasSuffix(a, "_publish"):
return actionPublish, true
case strings.HasSuffix(a, "_subscribe"):
return actionSubscribe, true
case strings.HasSuffix(a, "_view_role_users"), strings.HasSuffix(a, "_read"),
strings.HasSuffix(a, "_view"), strings.Contains(a, "_read_"):
return actionRead, true
case strings.Contains(a, actionCreate):
return actionCreate, true
case strings.HasSuffix(a, "_update"):
return "write", true
case strings.Contains(a, "_delete"):
return actionDelete, true
case strings.HasSuffix(a, "_manage_role"):
return actionManage, true
case strings.HasSuffix(a, "_add_role_users"), strings.HasSuffix(a, "_remove_role_users"),
strings.Contains(a, "membership"), strings.Contains(a, "_connect"):
return "policy.manage", true
case strings.HasSuffix(a, "_share"), strings.HasSuffix(a, "_unshare"):
return "policy.manage", true
}
return "", false
}
// connectionAction maps Magistrala connection type (1=publish, 2=subscribe).
func connectionAction(t int16) (string, bool) {
switch t {
case 1:
return actionPublish, true
case 2:
return actionSubscribe, true
default:
return "", false
}
}