Organization Management
Complete guide to multi-tenant organization management in the S-MA-C-H solution.
Overview
S-MA-C-H implements organization-based multi-tenancy to support multiple museums, institutions, and organizations using the same platform while keeping their data completely isolated.
What is an Organization?
An organization represents a distinct entity using S-MA-C-H:
- Museums (e.g., Bayeux Museum, British Museum, Louvre)
- Conservation companies
- Transport specialists
- The S-MA-C-H organization itself (super-admin access)
Key Features
✅ Complete Data Isolation
- Each organization has its own Collections, Transports, Dataloggers, Sensors, and Crates
- Users can only access data from their authorized organizations
- Organization boundaries enforced at API level
✅ Flexible User Assignment
- Users can belong to one or multiple organizations
- Single-org users: Simplified workflow (auto-selection)
- Multi-org users: Choose which organization they're working with
✅ Super Admin Access
- S-MA-C-H organization users have super-admin role
- Can access data from any organization
- Can perform cross-organization queries
✅ Secure by Default
- Organization validation at authentication layer (Keycloak)
- Organization enforcement at API layer (Spring Boot)
- Automatic filtering at data layer (MongoDB)
Architecture
Organization-Scoped Resources
| Resource | Organization Field | Example |
|---|---|---|
| Collection | organization | Bayeux Museum collections |
| Transport | organization | Bayeux transports |
| Datalogger | organization | Bayeux dataloggers |
| Sensor | organization | Bayeux sensors |
| Crate | organization | Bayeux crates |
| Artwork | via collection | Artworks in Bayeux collections |
| Destination | via transport | Destinations in Bayeux transports |
Constraints & Limitations
Important Constraints
Cross-Organization Restrictions:
- ❌ Cannot share Collections between organizations
- ❌ Cannot share Transports between organizations
- ❌ Cannot share Dataloggers between organizations
- ❌ Users cannot access resources from organizations they don't belong to
Allowed Operations:
- ✅ Users can belong to multiple organizations
- ✅ Super-admins can access all organizations
- ✅ Organizations can be added/removed from users in Keycloak
Keycloak Configuration
How to configure organizations and users in Keycloak (id.smach.science).
Prerequisites
- Keycloak admin access to S-MA-C-H realm
- URL:
https://id.smach.science/admin - Realm:
smach
Organization Structure in Keycloak
Organizations are managed using Keycloak's Organization feature (Keycloak 26+):
Realm: smach
├── Organizations
│ ├── bayeux (Alias: bayeux, Name: Bayeux Museum)
│ ├── louvre (Alias: louvre, Name: Musée du Louvre)
│ ├── british-museum (Alias: british-museum, Name: British Museum)
│ └── smach (Alias: smach, Name: S-MA-C-H)
├── Roles
│ ├── super-admin (S-MA-C-H experts only)
│ ├── administrator (organization admins)
│ └── viewer (read-only users)
└── Users
├── user@bayeux.com (organizations: [bayeux], role: viewer)
├── admin@louvre.fr (organizations: [louvre], role: administrator)
├── expert@smach.science (organizations: [smach], role: super-admin)
└── admin@bayeux.com (organizations: [bayeux], role: administrator)Creating a New Organization
Step 1: Create Organization in Keycloak
- Log in to Keycloak Admin Console:
https://id.smach.science/admin - Select smach realm
- Navigate to Organizations (left menu)
- Click Create Organization
- Fill in details:
- Alias:
bayeux(lowercase identifier - used in JWT and API) - Name:
Bayeux Museum(display name) - Description:
Bayeux Tapestry Museum - Normandy, France
- Alias:
- Click Save
Keycloak Terminology
- Alias: The technical identifier (e.g.,
bayeux)- Used in JWT token
organizationclaim - Used in API
X-Organizationheader - Must be lowercase, alphanumeric with hyphens
- Used in JWT token
- Name: The human-readable display name (e.g.,
Bayeux Museum)- Shown in Keycloak admin UI
- Can contain spaces, accents, uppercase
Step 2: Configure Organization
- In the organization settings:
- Enabled: ON
- Default Organization: OFF (only enable for S-MA-C-H org if needed)
Organization Alias Format
Use lowercase, alphanumeric identifiers with hyphens:
- ✅ Good:
bayeux,british-museum,louvre-paris - ❌ Bad:
Bayeux Museum,Musée du Louvre,british_museum
The Alias is what appears in:
- JWT token:
{ "organization": ["bayeux"] } - API header:
X-Organization: bayeux - Database:
{ "organization": "bayeux" }
Creating the First Organization User (Administrator)
Step 1: Create User
- Navigate to Users in Keycloak
- Click Add User
- Fill in details:
- Username:
admin@bayeux.com - Email:
admin@bayeux.com - First Name:
Jean - Last Name:
Dupont - Email Verified: ON
- Username:
- Click Save
Step 2: Set Password
- Go to Credentials tab
- Click Set Password
- Enter password
- Temporary: OFF (user won't be forced to change)
- Click Save
Step 3: Assign to Organization
- Go to Organizations tab (in the user details)
- Click Join Organization
- Select
bayeuxfrom the dropdown - Click Join
Organization Claim in JWT
Once assigned, the user's JWT token will contain the organization Alias:
{
"sub": "52673b28-ee1f-4d34-bb70-85137d4de6bd",
"email": "admin@bayeux.com",
"organization": ["bayeux"], // ← Organization Alias
"realm_access": {
"roles": [
"default-roles-smach",
"administrator", // ← User's role
"offline_access",
"uma_authorization"
]
},
"preferred_username": "admin@bayeux.com",
"given_name": "Jean",
"family_name": "Dupont"
}Step 4: Assign Role
- Go to Role Mappings tab
- Click Assign Role
- Filter by Realm roles
- Select
administrator - Click Assign
Adding Users to Existing Organizations
For Single Organization:
- Open user in Keycloak
- Go to Organizations tab
- Click Join Organization
- Select organization (e.g.,
louvre) - Click Join
- Assign appropriate role (administrator, viewer)
For Multiple Organizations (S-MA-C-H super-admins):
- Open user in Keycloak
- Go to Organizations tab
- Typically only assign to smach organization
- Assign
super-adminrole in Role Mappings - Super-admins automatically have access to all organizations
Multi-Organization Access
S-MA-C-H super-admins belong to the smach organization but can access all organizations' data. Regular administrators and viewers belong to their specific organization(s) only.
JWT token example (super-admin):
{
"sub": "52673b28-ee1f-4d34-bb70-85137d4de6bd",
"email": "super.admin@smach.science",
"organization": ["smach"],
"realm_access": {
"roles": [
"default-roles-smach",
"super-admin", // ← Super-admin role
"offline_access",
"uma_authorization"
]
},
"preferred_username": "super.admin@smach.science",
"name": "Super Admin"
}Creating Super Admin Users (S-MA-C-H Staff)
Super admins have access to all organizations without explicit assignment.
- Create user (e.g.,
expert@smach.science) - Set password
- Assign to smach organization
- Assign super-admin role
- Do NOT assign to other organizations (not needed)
Result:
- Can access data from any organization
- Can perform cross-organization queries
- Special privileges in the system
Removing User from Organization
- Open user in Keycloak
- Go to Organizations tab
- Find the organization in the list
- Click Leave button
- Confirm
Data Access Revoked Immediately
When a user is removed from an organization, they immediately lose access to that organization's data. Active sessions remain valid until JWT expires (typically 5-15 minutes).
User Management Best Practices
✅ DO:
- Use email addresses as usernames for consistency
- Verify email addresses before activating accounts
- Use meaningful Aliases (e.g.,
bayeux, notorg1) - Use descriptive Names (e.g.,
Bayeux Museum) - Assign minimum required organizations
- Review organization memberships regularly
❌ DON'T:
- Create duplicate organizations with different casing in Alias
- Use spaces or special characters in Alias
- Assign super-admin role to non-S-MA-C-H staff
- Share credentials between users
- Leave test organizations/users in production
Backend API (Spring Boot)
How the backend API handles organization context and enforces multi-tenancy.
API Documentation
Production API Base URL: https://api.smach.science
OpenAPI Documentation: Swagger UI
Interactive API Documentation
For complete API endpoint documentation, request/response schemas, and interactive testing, visit the Swagger UI.
You can test API calls directly from the browser with your authentication token!
Organization Flow
Request Organization Sources
The API determines organization context from (in order):
1. X-Organization Header (Recommended)
GET /collections
Host: api.smach.science
Authorization: Bearer {token}
X-Organization: bayeux2. URL Path (Future Support)
GET /orgs/bayeux/collections
Host: api.smach.science
Authorization: Bearer {token}3. Auto-Detection (Single-org users)
GET /collections
Host: api.smach.science
Authorization: Bearer {single-org-user-token}
# API automatically uses user's only organizationUser Access Rules
| User Type | Organizations in JWT | X-Organization Header | API Behavior |
|---|---|---|---|
| Single-org | ["bayeux"] | Not required | ✅ Auto-uses bayeux |
| Single-org | ["bayeux"] | bayeux | ✅ Validates and uses bayeux |
| Single-org | ["bayeux"] | louvre | ❌ 403 Forbidden |
| Multi-org | ["bayeux", "louvre"] | Not provided | ❌ 400 Bad Request (must specify) |
| Multi-org | ["bayeux", "louvre"] | bayeux | ✅ Uses bayeux |
| Multi-org | ["bayeux", "louvre"] | british-museum | ❌ 403 Forbidden (not in user's orgs) |
| Super-admin | ["smach"] | Not provided | ✅ No filter (all orgs) |
| Super-admin | ["smach"] | bayeux | ✅ Filters to bayeux |
API Request Examples
Single-organization user:
# No header needed - uses user's organization
curl https://api.smach.science/collections \
-H "Authorization: Bearer {token}"Multi-organization user:
# Must specify organization
curl https://api.smach.science/collections \
-H "Authorization: Bearer {token}" \
-H "X-Organization: bayeux"Super-admin (all organizations):
# No header - returns from all organizations
curl https://api.smach.science/collections \
-H "Authorization: Bearer {super-admin-token}"Super-admin (specific organization):
# With header - filters to specific organization
curl https://api.smach.science/collections \
-H "Authorization: Bearer {super-admin-token}" \
-H "X-Organization: louvre"Creating Resources
When creating organization-scoped resources, the organization field is automatically populated:
curl -X POST https://api.smach.science/collections \
-H "Authorization: Bearer {token}" \
-H "X-Organization: bayeux" \
-H "Content-Type: application/json" \
-d '{
"reference": "B3",
"name": "BAYEUX MUSEUM",
"summary": "Main collection"
// No "organization" field needed!
}'API Response:
{
"id": "507f1f77bcf86cd799439011",
"reference": "B3",
"organization": "bayeux", // ← Auto-populated from header
"name": "BAYEUX MUSEUM",
"summary": "Main collection"
}Organization Validation
If you include organization in the request body, it must match the context organization (from header or auto-detection). Otherwise:
{
"status": 400,
"message": "Organization mismatch: expected bayeux, got louvre"
}Error Responses
Missing Organization (Multi-org user):
{
"status": 400,
"error": "Bad Request",
"message": "X-Organization header required (user has multiple organizations: bayeux, louvre)"
}Access Denied:
{
"status": 403,
"error": "Forbidden",
"message": "Access denied to organization: british-museum"
}Organization Mismatch:
{
"status": 400,
"error": "Bad Request",
"message": "Organization mismatch: expected bayeux, got louvre"
}Technical Implementation
Implementation Overview (For Developers)
WebFilter Layer:
- Extracts organization from request (header or path)
- Validates user has access to the organization
- Stores organization in Reactor Context
- Handles single-org auto-detection
- Returns appropriate errors
Service Layer:
- Builds MongoDB queries
- Applies organization filter via
OrganizationContext.filter(query) - Validates organization on save operations
Data Layer:
- MongoDB queries automatically include organization criteria
- Format:
{ organization: "bayeux" }or{ organization: { $in: ["bayeux", "louvre"] } }
Security:
- Three-layer validation:
- Keycloak: Organizations in JWT (using organization Alias)
- WebFilter: Request organization validation
- Data Layer: Query-level filtering
Frontend (Angular) - Coming Soon
Frontend organization management will be documented once the backend implementation is complete.
Planned features:
- Organization selector component for multi-org users
- HTTP interceptor to add
X-Organizationheader - Organization context service
- Automatic organization persistence (localStorage/session)
- Organization-specific routing
Summary
| Component | Responsibility |
|---|---|
| Keycloak | Organization creation (Alias + Name), user assignment, JWT token generation |
| Backend API | Organization validation, query filtering, data isolation |
| Frontend | Organization selection, header management, UX |
Organization Identifier Flow:
Keycloak Organization Alias
↓
JWT "organization" claim
↓
X-Organization header
↓
Backend validation
↓
MongoDB organization field