Status: β
COMPLETE
Started: 2025-11-08
Completed: 2025-11-08
Owner: fulgidus
Progress: 24/24 tasks complete (100%)
Implementation of Role-Based Access Control (RBAC) for Heimdall with the introduction of Constellations - logical groupings of WebSDR stations that can be owned, shared, and managed by users with different permission levels.
Decision: Use constellation_shares table with permission field (βreadβ|βeditβ)
Rationale:
Decision: Hybrid approach
common/auth/rbac.pyRationale:
common/auth/rbac.py (DRY)Decision: Dual approach
Rationale:
| Resource | USER | OPERATOR | ADMIN |
|---|---|---|---|
| Constellations | View assigned (read) | CRUD owned/shared (edit) | Full access |
| Sources | View public/shared | CRUD owned/shared | Full access |
| Models | View shared | CRUD owned/shared | Full access |
| Sessions | Start on assigned constellations | Full control on accessible | Full control |
| WebSDRs | View all (global) | View all + assign to constellations | Full access |
| System Settings | β | β | β |
constellationsRepresents a logical grouping of WebSDR stations.
CREATE TABLE constellations (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
name VARCHAR(255) NOT NULL,
description TEXT,
owner_id VARCHAR(255) NOT NULL, -- Keycloak user ID (sub claim)
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_constellations_owner ON constellations(owner_id);
constellation_membersMany-to-many relationship: Constellations β WebSDR Stations.
CREATE TABLE constellation_members (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
constellation_id UUID NOT NULL REFERENCES constellations(id) ON DELETE CASCADE,
websdr_station_id UUID NOT NULL REFERENCES websdr_stations(id) ON DELETE CASCADE,
added_at TIMESTAMPTZ DEFAULT NOW(),
added_by VARCHAR(255), -- User who added this SDR
UNIQUE(constellation_id, websdr_station_id)
);
CREATE INDEX idx_constellation_members_constellation ON constellation_members(constellation_id);
CREATE INDEX idx_constellation_members_websdr ON constellation_members(websdr_station_id);
constellation_sharesUser access permissions for constellations.
CREATE TABLE constellation_shares (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
constellation_id UUID NOT NULL REFERENCES constellations(id) ON DELETE CASCADE,
user_id VARCHAR(255) NOT NULL, -- Keycloak user ID
permission VARCHAR(20) NOT NULL CHECK (permission IN ('read', 'edit')),
shared_by VARCHAR(255) NOT NULL, -- User who created the share
shared_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(constellation_id, user_id)
);
CREATE INDEX idx_constellation_shares_constellation ON constellation_shares(constellation_id);
CREATE INDEX idx_constellation_shares_user ON constellation_shares(user_id);
source_sharesUser access permissions for known sources.
CREATE TABLE source_shares (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
source_id UUID NOT NULL REFERENCES known_sources(id) ON DELETE CASCADE,
user_id VARCHAR(255) NOT NULL,
permission VARCHAR(20) NOT NULL CHECK (permission IN ('read', 'edit')),
shared_by VARCHAR(255) NOT NULL,
shared_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(source_id, user_id)
);
CREATE INDEX idx_source_shares_source ON source_shares(source_id);
CREATE INDEX idx_source_shares_user ON source_shares(user_id);
model_sharesUser access permissions for ML models.
CREATE TABLE model_shares (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
model_id UUID NOT NULL REFERENCES models(id) ON DELETE CASCADE,
user_id VARCHAR(255) NOT NULL,
permission VARCHAR(20) NOT NULL CHECK (permission IN ('read', 'edit')),
shared_by VARCHAR(255) NOT NULL,
shared_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(model_id, user_id)
);
CREATE INDEX idx_model_shares_model ON model_shares(model_id);
CREATE INDEX idx_model_shares_user ON model_shares(user_id);
known_sourcesAdd ownership and public visibility.
ALTER TABLE known_sources
ADD COLUMN owner_id VARCHAR(255),
ADD COLUMN is_public BOOLEAN DEFAULT false;
CREATE INDEX idx_known_sources_owner ON known_sources(owner_id);
CREATE INDEX idx_known_sources_public ON known_sources(is_public);
modelsAdd ownership and description.
ALTER TABLE models
ADD COLUMN owner_id VARCHAR(255),
ADD COLUMN description TEXT;
CREATE INDEX idx_models_owner ON models(owner_id);
recording_sessionsLink sessions to constellations.
ALTER TABLE recording_sessions
ADD COLUMN constellation_id UUID REFERENCES constellations(id);
CREATE INDEX idx_recording_sessions_constellation ON recording_sessions(constellation_id);
Goal: Create database schema and migration scripts
04-add-rbac-schema.sql
db/migrations/04-add-rbac-schema.sqldb/migrations/05-migrate-existing-data.sqlGoal: Create SQLAlchemy ORM models for new tables
Constellation modelConstellationMember modelConstellationShare modelservices/backend/src/models/constellation.pyowner_id to KnownSource modelis_public to KnownSource modelservices/backend/src/models/session.pySourceShare modelModelShare modelservices/backend/src/models/shares.pyconstellation_id foreign keyConstellationservices/backend/src/models/session.pyGoal: Create reusable permission checking functions
can_view_constellation(db, user_id, constellation_id, is_admin)can_edit_constellation(db, user_id, constellation_id, is_admin)can_delete_constellation(db, user_id, constellation_id, is_admin)can_view_source(db, user_id, source_id, is_admin)can_edit_source(db, user_id, source_id, is_admin)can_view_model(db, user_id, model_id, is_admin)can_edit_model(db, user_id, model_id, is_admin)get_user_constellations(db, user_id, is_admin)services/common/auth/rbac.py, services/common/auth/__init__.pyis_user propertyfrom_token_data() to properly detect all rolesservices/common/auth/models.pyGoal: Create REST API for constellation management
GET /api/v1/constellations - List accessible constellationsPOST /api/v1/constellations - Create constellation (operator+)GET /api/v1/constellations/{id} - Get constellation detailsPUT /api/v1/constellations/{id} - Update constellationDELETE /api/v1/constellations/{id} - Delete constellationPOST /api/v1/constellations/{id}/members - Add WebSDRDELETE /api/v1/constellations/{id}/members/{websdr_id} - Remove WebSDRservices/backend/src/routers/constellations.pyGET /api/v1/constellations/{id}/shares - List sharesPOST /api/v1/constellations/{id}/shares - Share with userPUT /api/v1/constellations/{id}/shares/{user_id} - Update permissionDELETE /api/v1/constellations/{id}/shares/{user_id} - Remove shareservices/backend/src/routers/constellations.pyGoal: Add RBAC checks to existing endpoints
services/backend/src/routers/sessions.pyservices/backend/src/routers/sources.py (new)services/backend/src/routers/models.py (new)Goal: Create authentication hooks and utilities
extractUserRole() function implementing hierarchyUser interface with 'operator' role type and roles: string[] arrayfrontend/src/store/authStore.tsisAdmin: boolean - true only for adminisOperator: boolean - true for operator OR adminisUser: boolean - true for all authenticated usersuser: User | nullfrontend/src/hooks/useAuth.tscanView(), canEdit(), canDelete(), canShare(), canCreate()canViewConstellation(), canEditConstellation(), canDeleteConstellation()canViewSource(), canEditSource(), canDeleteSource()canViewModel(), canEditModel(), canDeleteModel()canAccessSystemSettings(), canStartTraining(), canGenerateSynthetic()frontend/src/hooks/usePermissions.tsfrontend/src/components/auth/RequireRole.tsx<RequireRole role="..."> guardsfrontend/src/App.tsxGoal: Create user-facing pages and components
frontend/src/pages/Constellations.tsx, frontend/src/components/constellations/ConstellationCard.tsx, frontend/src/components/constellations/ConstellationForm.tsx, frontend/src/services/api/constellations.tsfrontend/src/components/sharing/ShareModal.tsx, frontend/src/components/sharing/UserSearch.tsx, frontend/src/components/sharing/ShareList.tsx, frontend/src/components/sharing/index.tsfrontend/src/pages/Constellations.tsxfrontend/src/pages/Dashboard.tsxfrontend/src/pages/Training/components/SyntheticTab/SyntheticTab.tsxfrontend/src/pages/Training/components/ModelsTab/ModelsTab.tsxfrontend/src/pages/Training/components/ModelsTab/ModelCard.tsxfrontend/src/pages/Training/components/JobsTab/JobsTab.tsxfrontend/src/pages/SourcesManagement.tsxfrontend/src/pages/Settings.tsxfrontend/src/pages/WebSDRManagement.tsxfrontend/src/pages/SessionHistory.tsxfrontend/src/components/widgets/QuickActionsWidget.tsxGoal: Ensure correctness and reliability
can_view_constellation() with different rolescan_edit_constellation() with ownership/sharingservices/common/auth/tests/test_rbac.pyservices/backend/tests/integration/test_constellations_rbac.pyGoal: Document RBAC system for developers and users
docs/RBAC.md) β
docs/API.md Constellations section) β
docs/ARCHITECTURE.md, docs/RBAC.md, docs/API.mdheimdall/
βββ db/
β βββ migrations/
β βββ 04-add-rbac-schema.sql [Task 1]
β βββ 05-migrate-existing-data.sql [Task 13]
βββ services/
β βββ common/
β β βββ auth/
β β βββ models.py [Task 7 - update]
β β βββ rbac.py [Task 6 - new]
β β βββ tests/
β β βββ test_rbac.py [Task 21]
β βββ backend/
β βββ src/
β βββ models/
β β βββ constellation.py [Task 2 - new]
β β βββ shares.py [Task 4 - new]
β β βββ db.py [Task 3 - update]
β β βββ session.py [Task 5 - update]
β βββ routers/
β β βββ constellations.py [Task 8, 9 - new]
β β βββ sources.py [Task 11 - new]
β β βββ models.py [Task 12 - new]
β β βββ sessions.py [Task 10 - update]
β βββ tests/
β βββ test_constellations.py [Task 22]
βββ frontend/
βββ src/
βββ store/
β βββ authStore.ts [Task 16 - update]
βββ hooks/
β βββ useAuth.ts [Task 14 - new]
β βββ usePermissions.ts [Task 14 - new]
βββ components/
β βββ auth/
β β βββ RequireRole.tsx [Task 15 - new]
β βββ sharing/
β βββ ShareModal.tsx [Task 19 - new]
βββ pages/
βββ Constellations.tsx [Task 17 - new]
βββ Dashboard.tsx [Task 18 - update]
04-add-rbac-schema.sql05-migrate-existing-data.sql04-add-rbac-schema.sql (2025-11-08)05-migrate-existing-data.sql (2025-11-08)None - All tasks complete!
None
All 24 tasks completed successfully! β RBAC implementation ready for production deployment.
Question: What is the Keycloak user ID (sub claim) for the default admin user?
Impact: Need this for data migration (Task 13)
Resolution: Check Keycloak realm export or query Keycloak API
Confirmed: All WebSDR stations remain globally visible (not owned).
Users with operator+ role can assign any WebSDR to their constellations.
Strategy: Create a default βGlobalβ constellation containing all existing SDRs.
Assign it to admin user. This preserves existing workflows.
If you have questions about this implementation:
Last Updated: 2025-11-08 by fulgidus
Status: β
COMPLETE - Ready for production deployment
Next Steps: Follow deployment checklist above to deploy RBAC to production