Skip to main content

Memberships

The Membership entity represents the relationship between users and organizations, defining roles and access levels within specific contexts. It is a crucial component of Wrkbelt's multi-tenant architecture and role-based access control (RBAC) system.

Schema Definition

type Membership = {
// Base Properties
_id: string; // MongoDB ObjectId
user_id: string; // Required, Reference to User
roles: string[]; // Required, Array of Role ObjectIds
context_id: string; // Required, Reference to Organization
context_type: MembershipType; // Required, Currently only 'organization'
is_default: boolean; // Optional, Default: false

// Timestamps
createdAt: Date; // Auto-generated
updatedAt: Date; // Auto-updated
} & BaseEntity;

Field Descriptions

Base Properties

FieldTypeRequiredDefaultDescription
_idObjectIdYes-Unique identifier for the membership
user_idObjectIdYes-Reference to the associated user
rolesObjectId[]Yes-Array of role references defining user permissions
context_idObjectIdYes-Reference to the organization this membership belongs to
context_typeMembershipTypeYes-Type of membership context (currently only 'organization')
is_defaultbooleanNofalseIndicates if this is the user's default membership

Timestamps

FieldTypeDescription
createdAtDateAutomatically set when the membership is created
updatedAtDateAutomatically updated when the membership changes

Type Variations

MembershipPopulated

A fully populated version with resolved references:

type MembershipPopulated = Omit<Membership, 'roles' | 'user_id'> & {
roles: RolePopulated[]; // Full role objects with permissions
user_id: User; // Full user object
};

MembershipLeanWithRoleNames

A lightweight version with just role names:

type MembershipLeanWithRoleNames = {
roles: Pick<Role, 'name'>[];
};

Relationships

Primary Relationships

  • Membership → User (Many-to-One)

    • Each membership belongs to exactly one user
    • Users can have multiple memberships
    • Referenced via user_id field
  • Membership → Roles (Many-to-Many)

    • Each membership can have multiple roles
    • Roles can be assigned to multiple memberships
    • Referenced via roles array field
  • Membership → Organization (Many-to-One)

    • Each membership belongs to exactly one organization
    • Organizations can have multiple memberships
    • Referenced via context_id field with context_type: 'organization'

Context Types

Currently, the only supported context type is:

enum MembershipType {
Organization = 'organization'
}

This design allows for future expansion to support other context types while maintaining proper type safety.

Validation

  • user_id: Required ObjectId reference to User
  • roles: Required array of ObjectId references to Role
  • context_id: Required ObjectId reference to Organization
  • context_type: Required enum value (currently only 'organization')
  • is_default: Optional boolean, defaults to false

Security Considerations

  1. Access Control

    • Memberships define user access within organizations
    • Role assignments determine available permissions
    • Default membership affects initial context selection
  2. Multi-tenancy

    • Memberships enforce proper tenant isolation
    • Users can only access resources within their membership context
    • Organizations can only manage their own memberships
  3. Role-Based Security

    • Roles must be valid for the organization context
    • Permission checks are performed through role assignments
    • Role changes require proper authorization

Usage Examples

Creating a Basic Membership

const membership = {
user_id: "user123",
roles: ["role456"],
context_id: "org789",
context_type: MembershipType.Organization,
is_default: true
};

Common Operations

  1. Default Membership Management

    // Only one membership can be default per user
    await Membership.updateMany(
    { user_id: userId },
    { is_default: false }
    );
    await Membership.updateOne(
    { _id: membershipId },
    { is_default: true }
    );
  2. Role Assignment

    // Add roles to membership
    await Membership.updateOne(
    { _id: membershipId },
    { $addToSet: { roles: newRoleId } }
    );