User Organization Membership
Data Entity
Description
Junction record linking a user to an organization with a specific role, status, and tenure. Supports multi-organization membership (e.g. NHF members in up to 5 local associations) and drives RBAC scoping, module visibility, and Bufdir reporting boundaries.
Data Structure
| Name | Type | Description | Constraints |
|---|---|---|---|
id |
uuid |
Primary key | PKrequiredunique |
user_id |
uuid |
Foreign key to users table | required |
organization_id |
uuid |
Foreign key to organizations table | required |
role |
enum |
The user's functional role within this organization | required |
status |
enum |
Membership lifecycle state | required |
is_primary_organization |
boolean |
Whether this is the user's primary organization. Used to resolve default context when user belongs to multiple organizations. | required |
invited_at |
datetime |
Timestamp when the invitation was sent by an org admin | - |
accepted_at |
datetime |
Timestamp when the user accepted the invitation | - |
deactivated_at |
datetime |
Timestamp when the membership was deactivated | - |
paused_at |
datetime |
Timestamp when the membership was paused (peer mentor pause function) | - |
paused_until |
datetime |
Optional scheduled resume date set at pause time | - |
pause_reason |
text |
Optional free-text reason for pause, visible to coordinator | - |
deactivation_reason |
text |
Reason for deactivation recorded by org admin | - |
invited_by_user_id |
uuid |
User ID of the org admin who sent the invitation | - |
local_association_label |
string |
Human-readable label for the specific local association within the organization (e.g. 'Oslo Lokallag'). Supports NHF's 1400-lokallag structure. | - |
sub_organization_id |
uuid |
Optional reference to a child node in org_hierarchy for multi-tier organizations (e.g. NHF region or lokallag) | - |
metadata |
json |
Extensible key-value bag for org-specific membership attributes without schema changes | - |
created_at |
datetime |
Row creation timestamp | required |
updated_at |
datetime |
Row last-updated timestamp | required |
Database Indexes
idx_uom_user_org
Columns: user_id, organization_id
idx_uom_user_id
Columns: user_id
idx_uom_org_id
Columns: organization_id
idx_uom_org_role_status
Columns: organization_id, role, status
idx_uom_status
Columns: status
Validation Rules
user_id_must_exist
error
Validation failed
organization_id_must_exist
error
Validation failed
role_must_be_valid_enum
error
Validation failed
status_transition_must_be_valid
error
Validation failed
pause_timestamps_consistent
error
Validation failed
deactivation_timestamp_consistent
error
Validation failed
invited_by_must_be_org_admin
error
Validation failed
sub_organization_belongs_to_organization
error
Validation failed
Business Rules
one_membership_per_user_per_org
A user may hold at most one membership record per organization. Attempting to create a duplicate (user_id + organization_id) must be rejected.
exactly_one_primary_organization
Each user must have exactly one membership flagged is_primary_organization=true at all times. When a new primary is set, the previous primary is automatically cleared.
global_admin_no_org_context
Users with role=global_admin must not have is_primary_organization=true or an organization_id scoped to a specific tenant org. Global admins authenticate without org context and gain time-bounded org access only via support_access_grants.
coordinator_and_peer_mentor_mobile_only
Memberships with role=peer_mentor or role=coordinator grant access to the Mobile App only. These roles must not be granted admin portal login. Org admin surfaces as coordinator on mobile.
pause_requires_coordinator_notification
When status transitions to paused, the user's coordinator(s) in the same organization must receive a notification.
auto_pause_on_certification_expiry
When a peer mentor's certification expires, the system automatically sets status=paused and records paused_at. Coordinator is notified.
deactivated_membership_excludes_from_reports
Deactivated memberships (status=deactivated) must be excluded from Bufdir reports, activity aggregations, and coordinator team views. Norse Test Organization data is always excluded from Bufdir exports regardless of status.
max_five_org_memberships_per_user
A single user may belong to at most 5 organizations simultaneously (NHF multi-lokallag constraint). Reject create if the user already has 5 active or paused memberships.
invited_status_expires
Memberships in status=invited that have not been accepted within 30 days should be flagged as expired. The org admin must be notified and may re-invite.