Comprehensive user roles and permissions system for the Open Source Site Tracking platform, including role definitions, permission management, and access control.
The user roles system provides comprehensive access control for the Open Source Site Tracking platform. It implements role-based access control (RBAC) with granular permissions, role hierarchy, and flexible role assignment.
# Role model definition
class Role:
id: str
name: str
description: str
permissions: List[str]
level: str
is_system_role: bool
is_default: bool
created_at: datetime
updated_at: datetime
class UserRole:
id: str
user_id: str
role_id: str
resource_id: str
resource_type: str
granted_by: str
granted_at: datetime
expires_at: Optional[datetime]
is_active: bool
class Permission:
id: str
name: str
description: str
resource_type: str
action: str
level: str
created_at: datetime
class RoleLevel:
SYSTEM = "system"
ORGANIZATION = "organization"
PROJECT = "project"
TEAM = "team"
class RoleType:
SYSTEM_ROLE = "system_role"
ORGANIZATION_ROLE = "organization_role"
PROJECT_ROLE = "project_role"
TEAM_ROLE = "team_role"
# Permission management implementation
class PermissionManager:
def __init__(self):
self.permissions_cache = {}
self.roles_cache = {}
async def check_permission(self, user_id, permission, resource_id=None):
"""Check if user has permission"""
# Get user roles
user_roles = await self.get_user_roles(user_id)
# Check each role for permission
for role in user_roles:
if await self.role_has_permission(role, permission, resource_id):
return True
return False
async def role_has_permission(self, role, permission, resource_id=None):
"""Check if role has permission"""
# Get role details
role_details = await self.get_role_details(role.role_id)
# Check direct permissions
if permission in role_details.permissions:
return True
# Check inherited permissions
inherited_permissions = await self.get_inherited_permissions(role_details)
if permission in inherited_permissions:
return True
# Check resource-specific permissions
if resource_id:
resource_permissions = await self.get_resource_permissions(role, resource_id)
if permission in resource_permissions:
return True
return False
async def grant_permission(self, user_id, role_id, resource_id=None, expires_at=None):
"""Grant role to user"""
user_role = UserRole(
user_id=user_id,
role_id=role_id,
resource_id=resource_id,
resource_type=self.get_resource_type(resource_id),
granted_by=self.get_current_user_id(),
granted_at=datetime.utcnow(),
expires_at=expires_at,
is_active=True
)
await self.save_user_role(user_role)
# Clear cache
self.clear_user_cache(user_id)
return user_role
# Role assignment implementation
class RoleAssignmentService:
def __init__(self):
self.permission_manager = PermissionManager()
async def assign_role(self, user_id, role_id, resource_id=None, context=None):
"""Assign role to user"""
# Validate role exists
role = await self.get_role(role_id)
if not role:
raise ValueError(f"Role {role_id} not found")
# Validate user exists
user = await self.get_user(user_id)
if not user:
raise ValueError(f"User {user_id} not found")
# Validate resource access
if resource_id:
if not await self.can_assign_role(user_id, role_id, resource_id):
raise PermissionError("Insufficient permissions to assign role")
# Create user role assignment
user_role = await self.permission_manager.grant_permission(
user_id=user_id,
role_id=role_id,
resource_id=resource_id,
expires_at=context.get('expires_at') if context else None
)
# Send notification
await self.send_role_assignment_notification(user_role, context)
# Log assignment
await self.log_role_assignment(user_role, context)
return user_role
async def revoke_role(self, user_id, role_id, resource_id=None, context=None):
"""Revoke role from user"""
# Find user role assignment
user_role = await self.find_user_role(user_id, role_id, resource_id)
if not user_role:
raise ValueError("Role assignment not found")
# Validate revocation permissions
if not await self.can_revoke_role(user_id, role_id, resource_id):
raise PermissionError("Insufficient permissions to revoke role")
# Deactivate role assignment
user_role.is_active = False
user_role.revoked_at = datetime.utcnow()
user_role.revoked_by = self.get_current_user_id()
await self.save_user_role(user_role)
# Send notification
await self.send_role_revocation_notification(user_role, context)
# Log revocation
await self.log_role_revocation(user_role, context)
return user_role
# Create custom role
POST /api/v1/roles
{
"name": "Custom Analytics Manager",
"description": "Custom role for analytics management",
"permissions": [
"analytics.read",
"analytics.write",
"analytics.export",
"project.read"
],
"level": "project",
"is_default": false
}
Response:
{
"id": "role-123",
"name": "Custom Analytics Manager",
"description": "Custom role for analytics management",
"permissions": [
"analytics.read",
"analytics.write",
"analytics.export",
"project.read"
],
"level": "project",
"is_system_role": false,
"created_at": "2024-01-15T10:30:00Z"
}
# Assign role to user
POST /api/v1/users/{user_id}/roles
{
"role_id": "role-123",
"resource_id": "project-456",
"resource_type": "project",
"expires_at": "2024-12-31T23:59:59Z"
}
Response:
{
"id": "user-role-789",
"user_id": "user-123",
"role_id": "role-123",
"resource_id": "project-456",
"resource_type": "project",
"granted_at": "2024-01-15T10:30:00Z",
"expires_at": "2024-12-31T23:59:59Z",
"is_active": true
}
# Check user permission
GET /api/v1/users/{user_id}/permissions/check?permission=analytics.read&resource_id=project-456
Response:
{
"has_permission": true,
"permission": "analytics.read",
"resource_id": "project-456",
"granted_by": [
{
"role_id": "role-123",
"role_name": "Analytics Manager",
"granted_at": "2024-01-15T10:30:00Z"
}
]
}
# Get user roles
GET /api/v1/users/{user_id}/roles
Response:
{
"roles": [
{
"id": "user-role-789",
"role_id": "role-123",
"role_name": "Analytics Manager",
"resource_id": "project-456",
"resource_type": "project",
"granted_at": "2024-01-15T10:30:00Z",
"expires_at": "2024-12-31T23:59:59Z",
"is_active": true
}
],
"summary": {
"total_roles": 1,
"active_roles": 1,
"expired_roles": 0
}
}