Skip to main content

UserTenantAssociation Model Documentation

Overview​

The UserTenantAssociation model enables multi-tenant user support in CODITECT, allowing users to be associated with multiple tenants while maintaining different roles and permissions in each. This supports complex organizational scenarios like contractors working across companies, auditors reviewing multiple organizations, or employees transitioning between subsidiaries.

Model Structure​

Core Fields​

FieldTypeDescriptionConstraints
idUUIDUnique association identifierPrimary key, auto-generated
user_idUUIDUser being associatedForeign key to User
tenant_idUUIDTarget tenantForeign key to Tenant
roleUserRoleRole within tenantRequired (Admin, Manager, Developer, User)
permissionsVecAdditional permissionsBeyond role defaults
association_typeAssociationType (Enum)Nature of relationshipRequired
valid_fromDateTimeAssociation start dateRequired, defaults to now
valid_untilDateTime (Optional)Association end dateFor time-bound access
created_byUUIDAdmin who created associationForeign key to User
created_atDateTimeCreation timestampAuto-set
updated_atDateTimeLast modificationAuto-updated
is_activeboolActive statusRequired, default true
notesString (Optional)Association reason/contextFor audit purposes

AssociationType Enum​

enum AssociationType {
Primary, // User's home tenant
Employee, // Regular employment
Contractor, // Time-bound contract work
Auditor, // External audit access
Support, // Technical support access
Guest, // Limited guest access
Custom(String) // Custom relationship type
}

Default Permissions by Type​

Association TypeDefault PermissionsWrite AccessTime-Bound
Primaryread, write, deleteYesNo
Employeeread, writeYesNo
Contractorread, write:assignedYesYes
Auditorread, audit:view, report:generateNoYes
Supportread, support:troubleshoot, logs:viewNoNo
Guestread:limitedNoYes
CustomNone (must specify)DependsDepends

Example Associations​

Primary Employee​

{
"id": "550e8400-e29b-41d4-a716-446655440000",
"user_id": "123e4567-e89b-12d3-a456-426614174000",
"tenant_id": "456e7890-e89b-12d3-a456-426614174000",
"role": "Developer",
"permissions": ["read", "write", "delete"],
"association_type": "Primary",
"valid_from": "2024-01-15T00:00:00Z",
"valid_until": null,
"created_by": "789e0123-e89b-12d3-a456-426614174000",
"created_at": "2024-01-15T00:00:00Z",
"updated_at": "2025-08-29T10:30:00Z",
"is_active": true,
"notes": "Founding team member"
}

External Contractor​

{
"id": "660e8400-e29b-41d4-a716-446655440000",
"user_id": "234e5678-e89b-12d3-a456-426614174000",
"tenant_id": "567e8901-e89b-12d3-a456-426614174000",
"role": "Developer",
"permissions": ["read", "write:assigned", "comment"],
"association_type": "Contractor",
"valid_from": "2025-08-01T00:00:00Z",
"valid_until": "2025-12-31T23:59:59Z",
"created_by": "890e1234-e89b-12d3-a456-426614174000",
"created_at": "2025-07-28T14:30:00Z",
"updated_at": "2025-08-29T09:15:00Z",
"is_active": true,
"notes": "6-month contract for Project Phoenix"
}

Security Auditor​

{
"id": "770e8400-e29b-41d4-a716-446655440000",
"user_id": "345e6789-e89b-12d3-a456-426614174000",
"tenant_id": "678e9012-e89b-12d3-a456-426614174000",
"role": "User",
"permissions": ["read", "audit:view", "report:generate", "compliance:check"],
"association_type": "Auditor",
"valid_from": "2025-09-01T00:00:00Z",
"valid_until": "2025-09-07T23:59:59Z",
"created_by": "901e2345-e89b-12d3-a456-426614174000",
"created_at": "2025-08-25T16:00:00Z",
"updated_at": "2025-08-29T11:45:00Z",
"is_active": true,
"notes": "Annual SOC2 compliance audit - PwC"
}

Supporting Models​

UserTenantSummary​

Provides overview of user's multi-tenant access:

FieldTypeDescription
user_idUUIDUser identifier
primary_tenant_idUUIDPrimary/home tenant
total_associationsusizeTotal tenant count
active_associationsusizeCurrently valid count
association_detailsVecDetailed list

TenantAssociationInfo​

Summary information for each association:

FieldTypeDescription
tenant_idUUIDTenant identifier
tenant_nameStringDisplay name
roleUserRoleUser's role
association_typeAssociationTypeRelationship type
is_activeboolCurrent status
valid_untilDateTime (Optional)Expiration date

Database Schema​

Primary Storage Pattern​

Key: /{tenant_id}/user_tenant_associations/{association_id}
Value: JSON serialized UserTenantAssociation

Secondary Indexes​

# User's associations across all tenants
/global/user_associations/{user_id} -> [association_ids]

# Tenant's user associations
/{tenant_id}/tenant_user_associations -> [association_ids]

# Active associations by type
/{tenant_id}/associations_by_type/{type} -> [association_ids]

# Expiring associations
/global/expiring_associations/{YYYY-MM-DD} -> [association_ids]

# Primary tenant lookup
/global/user_primary_tenant/{user_id} -> tenant_id

Business Logic​

Association Validation​

impl UserTenantAssociation {
pub fn is_valid(&self) -> bool {
// Must be active
if !self.is_active {
return false;
}

// Check time bounds
let now = Utc::now();
if now < self.valid_from {
return false; // Future dated
}

if let Some(valid_until) = self.valid_until {
if now > valid_until {
return false; // Expired
}
}

true
}
}

Permission Resolution​

// Effective permissions = Role permissions + Additional permissions
pub fn effective_permissions(&self) -> Vec<String> {
let mut perms = self.role.permissions(); // Base role permissions
perms.extend(self.permissions.clone()); // Additional permissions
perms.sort();
perms.dedup(); // Remove duplicates
perms
}

API Endpoints​

Association Management​

  • POST /api/users/{user_id}/associations - Create association
  • GET /api/users/{user_id}/associations - List user's associations
  • GET /api/associations/{association_id} - Get association details
  • PUT /api/associations/{association_id} - Update association
  • DELETE /api/associations/{association_id} - Remove association

Tenant User Management​

  • GET /api/tenants/{tenant_id}/users - List tenant users
  • POST /api/tenants/{tenant_id}/invite - Invite user to tenant
  • PUT /api/tenants/{tenant_id}/users/{user_id}/role - Change role

Validation and Switching​

  • GET /api/users/me/tenants - Get accessible tenants
  • POST /api/auth/switch-tenant - Switch active tenant
  • GET /api/associations/validate - Check association validity

Use Cases​

Multi-Company Employee​

// User works for parent company and subsidiary
let parent_assoc = UserTenantAssociation::new(
user_id,
parent_tenant_id,
UserRole::Manager,
AssociationType::Primary,
admin_id
);

let subsidiary_assoc = UserTenantAssociation::new(
user_id,
subsidiary_tenant_id,
UserRole::Developer,
AssociationType::Employee,
admin_id
);

Time-Limited Contractor​

// Contractor with 3-month project access
let mut contractor_assoc = UserTenantAssociation::new(
contractor_id,
client_tenant_id,
UserRole::Developer,
AssociationType::Contractor,
manager_id
);

contractor_assoc.valid_until = Some(Utc::now() + Duration::days(90));
contractor_assoc.permissions = vec![
"project:read:project-123".to_string(),
"project:write:project-123".to_string(),
"task:*:project-123".to_string()
];
contractor_assoc.notes = Some("React developer for mobile app project".to_string());

Compliance Auditor​

// Annual audit with read-only access
let auditor_assoc = UserTenantAssociation::new(
auditor_id,
tenant_id,
UserRole::User, // Minimal role
AssociationType::Auditor,
compliance_officer_id
);

// Override with specific audit permissions
auditor_assoc.permissions = vec![
"audit:logs:read".to_string(),
"compliance:reports:generate".to_string(),
"financial:records:view".to_string(),
"security:events:analyze".to_string()
];

Security Patterns​

Tenant Switching​

// Validate user can access requested tenant
async fn switch_tenant(user_id: Uuid, target_tenant_id: Uuid) -> Result<()> {
let association = get_user_tenant_association(user_id, target_tenant_id).await?;

if !association.is_valid() {
return Err(Unauthorized("Invalid or expired association"));
}

// Update session with new tenant context
update_session_tenant(user_id, target_tenant_id).await?;
Ok(())
}

Permission Checking​

// Check permission in tenant context
async fn check_permission(
user_id: Uuid,
tenant_id: Uuid,
required_permission: &str
) -> Result<bool> {
let association = get_user_tenant_association(user_id, tenant_id).await?;

if !association.is_valid() {
return Ok(false);
}

let permissions = association.effective_permissions();
Ok(permissions.contains(&required_permission.to_string()))
}

Workflows​

Invitation Flow​

1. Admin initiates invitation
2. System creates pending association
3. Email sent to invitee
4. User accepts invitation
5. Association activated
6. User granted access

Expiration Handling​

1. Daily job checks expiring associations
2. Warning sent 7 days before expiry
3. Final notice 1 day before
4. Association deactivated on expiry
5. Access automatically revoked
6. Audit log entry created

Role Change Process​

1. Admin requests role change
2. Validate admin has permission
3. Check business rules
4. Update association record
5. Refresh user permissions
6. Log role change event

Performance Considerations​

Caching Strategy​

  • Cache user's active associations
  • Cache primary tenant lookup
  • Invalidate on any association change
  • TTL: 5 minutes for active sessions

Query Optimization​

  • Index on user_id for fast lookup
  • Composite index on (tenant_id, is_active)
  • Separate index for expiring associations
  • Denormalize primary tenant for speed

Compliance & Audit​

Tracking Requirements​

  • Log all association changes
  • Record who approved access
  • Track permission usage
  • Monitor unusual patterns
  • Regular access reviews

Data Retention​

  • Active associations: Indefinite
  • Expired associations: 7 years
  • Audit logs: 7 years
  • Permission usage: 90 days

Integration Points​

With User Model​

  • User deletion cascades to associations
  • Primary tenant stored on user
  • Profile syncs across tenants

With Tenant Model​

  • Tenant deletion removes associations
  • Tenant settings affect permissions
  • Billing based on active users

With RBAC Model​

  • Role permissions baseline
  • Additional permissions overlay
  • Dynamic permission calculation

Best Practices​

Association Management​

  1. Always set expiration for contractors
  2. Document reason in notes field
  3. Review associations quarterly
  4. Automate contractor offboarding
  5. Use least privilege principle

Security Guidelines​

  1. Require approval for cross-tenant access
  2. Log all tenant switches
  3. Alert on unusual access patterns
  4. Regular permission audits
  5. Enforce separation of duties

Future Enhancements​

Advanced Features​

  1. Delegation Support: Temporary permission delegation
  2. Approval Workflows: Multi-step association approval
  3. Access Reviews: Automated periodic reviews
  4. Smart Expiration: ML-based duration suggestions

Integration Improvements​

  1. SSO Integration: Federated identity support
  2. SCIM Support: Automated provisioning
  3. Directory Sync: LDAP/AD integration
  4. OAuth Scopes: Fine-grained API access

Compliance Features​

  1. Access Certification: Periodic attestation
  2. Segregation of Duties: Rule enforcement
  3. Privacy Controls: Cross-tenant data isolation
  4. Audit Reports: Compliance reporting

Last Updated: 2025-08-29 Version: 1.0