ProjectMember Model Documentation
Overview​
The ProjectMember model manages team membership and permissions within projects in CODITECT. It defines role-based access control at the project level, tracking who belongs to each project, their roles, and their specific permissions. This model enables fine-grained access control while maintaining a clear audit trail of membership changes.
Model Structure​
Core Fields​
| Field | Type | Description | Constraints |
|---|---|---|---|
tenant_id | UUID | Associated tenant | Foreign key to Tenant |
project_id | UUID | Project identifier | Foreign key to Project |
member_id | UUID | Team member | Foreign key to Member |
role | ProjectRole (Enum) | Member's project role | Required |
joined_at | DateTime | When member joined | Auto-set to current time |
permissions | Vec | Granted permissions | Auto-set based on role |
added_by | UUID | Who added this member | Foreign key to User |
ProjectRole Enum​
enum ProjectRole {
Owner, // Full control, can delete project
Admin, // Manage project and members
Developer, // Create and manage tasks
Viewer, // Read-only access
Tester, // QA and testing role
Designer // Design team member
}
Permission Enum​
enum Permission {
// Task permissions
CreateTask, // Create new tasks
UpdateTask, // Modify existing tasks
DeleteTask, // Remove tasks
AssignTask, // Assign tasks to members
// Project permissions
UpdateProject, // Modify project settings
DeleteProject, // Delete entire project
AddMembers, // Invite new members
RemoveMembers, // Remove members
// View permissions
ViewTasks, // See all tasks
ViewMembers, // See team roster
ViewReports // Access analytics
}
Role-Based Permissions​
Permission Matrix​
| Role | Create Task | Update Task | Delete Task | Assign Task | Update Project | Delete Project | Add Members | Remove Members | View All |
|---|---|---|---|---|---|---|---|---|---|
| Owner | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Admin | ✓ | ✓ | ✓ | ✓ | ✓ | ✗ | ✓ | ✓ | ✓ |
| Developer | ✓ | ✓ | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ | ✓ |
| Tester | ✓ | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✓ |
| Designer | ✓ | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✓ |
| Viewer | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✓ |
Default Permissions by Role​
Owner (11 permissions)​
All permissions - complete project control
Admin (10 permissions)​
All permissions except DeleteProject
Developer (6 permissions)​
CreateTask,UpdateTask,AssignTaskViewTasks,ViewMembers,ViewReports
Tester/Designer (5 permissions)​
CreateTask,UpdateTaskViewTasks,ViewMembers,ViewReports
Viewer (3 permissions)​
ViewTasks,ViewMembers,ViewReports
Example Records​
Project Owner​
{
"tenant_id": "123e4567-e89b-12d3-a456-426614174000",
"project_id": "456e7890-e89b-12d3-a456-426614174000",
"member_id": "789e0123-e89b-12d3-a456-426614174000",
"role": "Owner",
"joined_at": "2025-01-15T10:00:00Z",
"permissions": [
"CreateTask", "UpdateTask", "DeleteTask", "AssignTask",
"UpdateProject", "DeleteProject", "AddMembers", "RemoveMembers",
"ViewTasks", "ViewMembers", "ViewReports"
],
"added_by": "789e0123-e89b-12d3-a456-426614174000"
}
Team Developer​
{
"tenant_id": "123e4567-e89b-12d3-a456-426614174000",
"project_id": "456e7890-e89b-12d3-a456-426614174000",
"member_id": "890e1234-e89b-12d3-a456-426614174000",
"role": "Developer",
"joined_at": "2025-03-01T14:30:00Z",
"permissions": [
"CreateTask", "UpdateTask", "AssignTask",
"ViewTasks", "ViewMembers", "ViewReports"
],
"added_by": "789e0123-e89b-12d3-a456-426614174000"
}
External Reviewer​
{
"tenant_id": "123e4567-e89b-12d3-a456-426614174000",
"project_id": "456e7890-e89b-12d3-a456-426614174000",
"member_id": "901e2345-e89b-12d3-a456-426614174000",
"role": "Viewer",
"joined_at": "2025-08-15T09:00:00Z",
"permissions": ["ViewTasks", "ViewMembers", "ViewReports"],
"added_by": "890e1234-e89b-12d3-a456-426614174000"
}
Database Schema​
Primary Storage Pattern​
Key: /{tenant_id}/project_members/{project_id}/{member_id}
Value: JSON serialized ProjectMember object
Secondary Indexes​
# Members by project
/{tenant_id}/members_by_project/{project_id} -> [member_ids]
# Projects by member
/{tenant_id}/projects_by_member/{member_id} -> [project_ids]
# Members by role
/{tenant_id}/project_members_by_role/{project_id}/{role} -> [member_ids]
# Recent additions
/{tenant_id}/recent_project_members/{YYYY-MM-DD} -> [(project_id, member_id)]
Related Code​
Source Files​
- Model:
/src/models/project_member.rs - Repository:
/src/db/repositories/project_member_repository.rs - Service:
/src/services/project_membership_service.rs - API Handler:
/src/api/handlers/project_member_handlers.rs - Tests:
/src/models/tests/project_member_tests.rs
Key Methods​
impl ProjectMember {
fn new(
tenant_id: Uuid,
project_id: Uuid,
member_id: Uuid,
role: ProjectRole,
added_by: Uuid
) -> Self
fn permissions_for_role(role: &ProjectRole) -> Vec<Permission>
fn has_permission(&self, permission: &Permission) -> bool
fn can_assign_tasks(&self) -> bool
fn can_manage_members(&self) -> bool
}
impl ProjectMemberRepository {
async fn add_member(&self, member: &ProjectMember) -> Result<()>
async fn remove_member(&self, tenant_id: &Uuid, project_id: &Uuid, member_id: &Uuid) -> Result<()>
async fn update_role(&self, tenant_id: &Uuid, project_id: &Uuid, member_id: &Uuid, role: ProjectRole) -> Result<()>
async fn get_project_members(&self, tenant_id: &Uuid, project_id: &Uuid) -> Result<Vec<ProjectMember>>
async fn get_member_projects(&self, tenant_id: &Uuid, member_id: &Uuid) -> Result<Vec<ProjectMember>>
}
API Endpoints​
Member Management​
- POST
/api/projects/{project_id}/members- Add member - GET
/api/projects/{project_id}/members- List members - PUT
/api/projects/{project_id}/members/{member_id}- Update role - DELETE
/api/projects/{project_id}/members/{member_id}- Remove member
Member Queries​
- GET
/api/members/{member_id}/projects- Member's projects - GET
/api/projects/{project_id}/members/by-role/{role}- Members by role - GET
/api/projects/{project_id}/members/{member_id}/permissions- Check permissions
Bulk Operations​
- POST
/api/projects/{project_id}/members/bulk-add- Add multiple members - POST
/api/projects/{project_id}/members/bulk-update- Update multiple roles
Business Rules​
Membership Rules​
- One Owner Required: Every project must have at least one owner
- Owner Transfer: Last owner cannot leave without transferring ownership
- Unique Membership: One member can have only one role per project
- Self-Service: Members cannot change their own role (except owners)
- Cascade Delete: Deleting member removes from all projects
Permission Hierarchy​
- Role-Based: Permissions derived from role
- No Override: Cannot grant permissions beyond role
- Immediate Effect: Permission changes apply immediately
- No Inheritance: Parent project permissions don't cascade
Access Control​
// Only admins and owners can manage members
async fn can_manage_members(
actor_id: Uuid,
project_id: Uuid
) -> Result<bool> {
let membership = get_project_member(project_id, actor_id).await?;
Ok(membership.can_manage_members())
}
// Members can only update tasks they created or are assigned
async fn can_update_task(
member_id: Uuid,
task: &Task
) -> Result<bool> {
let membership = get_project_member(task.project_id, member_id).await?;
if !membership.has_permission(&Permission::UpdateTask) {
return Ok(false);
}
// Additional business logic
Ok(task.creator_id == member_id || task.assignee_id == Some(member_id))
}
Use Cases​
Project Creation​
// Creator automatically becomes owner
let owner_membership = ProjectMember::new(
tenant_id,
new_project_id,
creator_id,
ProjectRole::Owner,
creator_id // self-added
);
Team Onboarding​
// Batch add new team members
let new_members = vec![
(dev1_id, ProjectRole::Developer),
(dev2_id, ProjectRole::Developer),
(qa_id, ProjectRole::Tester),
(designer_id, ProjectRole::Designer),
];
for (member_id, role) in new_members {
let membership = ProjectMember::new(
tenant_id,
project_id,
member_id,
role,
admin_id
);
member_service.add_member(membership).await?;
}
Role Promotion​
// Promote developer to admin
member_service
.update_member_role(
project_id,
developer_id,
ProjectRole::Admin,
promoted_by_id
)
.await?;
Integration Points​
With Member Model​
- Member availability affects assignment
- Member skills influence role assignment
- Member status affects project access
With Project Model​
- Project status affects member operations
- Project deletion removes all memberships
- Project visibility based on membership
With Task Model​
- Task assignment limited to project members
- Task visibility based on membership
- Task permissions derived from project role
Workflows​
Adding Members​
1. Check actor has AddMembers permission
2. Verify member exists and is active
3. Check member not already in project
4. Create ProjectMember record
5. Send notification to new member
6. Log addition in audit trail
Changing Roles​
1. Verify actor can manage members
2. Check target member exists in project
3. Validate new role assignment
4. Update member record
5. Refresh member's permissions
6. Notify member of change
Removing Members​
1. Check actor has RemoveMembers permission
2. Verify not removing last owner
3. Reassign member's tasks (optional)
4. Remove member record
5. Revoke project access
6. Log removal event
Security Considerations​
Permission Checks​
- Always verify membership before granting access
- Check specific permissions, not just role
- Cache permissions for performance
- Invalidate cache on role change
Audit Requirements​
- Log all membership changes
- Track who added/removed members
- Record permission usage
- Monitor unusual access patterns
Data Protection​
- Members see only assigned tasks by default
- Sensitive project data requires explicit permission
- Personal member data protected
- Cross-project isolation enforced
Performance Optimization​
Caching Strategy​
struct MembershipCache {
// Cache member's project list
member_projects: HashMap<Uuid, Vec<ProjectSummary>>,
// Cache project's member list
project_members: HashMap<Uuid, Vec<MemberSummary>>,
// Cache permission lookups
permission_cache: HashMap<(Uuid, Uuid), Vec<Permission>>,
// TTL: 5 minutes
ttl: Duration
}
Query Patterns​
- Batch load project members
- Denormalize member count on project
- Index frequently queried combinations
- Preload permissions for requests
Best Practices​
Role Assignment​
- Start with least privilege (Viewer)
- Promote based on demonstrated need
- Regular role reviews
- Document role changes
- Automate common patterns
Team Management​
- Onboard teams in batches
- Use templates for standard teams
- Regular membership audits
- Clear ownership succession
- Archive inactive memberships
Future Enhancements​
Advanced Features​
- Custom Roles: Project-specific role definitions
- Permission Sets: Reusable permission groups
- Temporary Access: Time-bound memberships
- Role Templates: Standard team structures
Automation​
- Auto-assignment: Based on skills/availability
- Role Suggestions: ML-based recommendations
- Access Reviews: Periodic membership audits
- Succession Planning: Automatic owner transfer
Integration Features​
- Team Sync: Import from HR systems
- SSO Groups: Map external groups to roles
- Approval Workflows: Multi-step member addition
- Cross-Project Teams: Shared team definitions
Analytics​
- Team Velocity: Performance by role mix
- Permission Usage: Actual vs granted
- Collaboration Patterns: Member interactions
- Optimal Team Size: Data-driven recommendations
Last Updated: 2025-08-29 Version: 1.0