Coverage for netrun_rbac \ models.py: 100%
58 statements
« prev ^ index » next coverage.py v7.12.0, created at 2025-11-28 14:16 -0800
« prev ^ index » next coverage.py v7.12.0, created at 2025-11-28 14:16 -0800
1"""
2RBAC Models - Role and Permission definitions
4Extracted from: Intirkast middleware/rbac.py
5Generalized for multi-project reuse
6"""
8from enum import Enum
9from typing import Dict, List
10from pydantic import BaseModel, Field
13class Role(str, Enum):
14 """
15 Role hierarchy for multi-tenant SaaS platforms
17 Hierarchy (lowest to highest):
18 - viewer: Read-only access (view content, analytics)
19 - member: Create/edit own content (schedule posts, generate videos)
20 - admin: Manage team (invite users, edit all content)
21 - owner: Full control (billing, tenant settings, delete tenant)
22 """
24 VIEWER = "viewer"
25 MEMBER = "member"
26 ADMIN = "admin"
27 OWNER = "owner"
30class Permission(str, Enum):
31 """
32 Fine-grained permissions for resource access control
34 Pattern: {resource}:{action}
35 Example: posts:create, users:delete, billing:read
36 """
38 # User management
39 USERS_READ = "users:read"
40 USERS_CREATE = "users:create"
41 USERS_UPDATE = "users:update"
42 USERS_DELETE = "users:delete"
44 # Tenant management
45 TENANT_READ = "tenant:read"
46 TENANT_UPDATE = "tenant:update"
47 TENANT_DELETE = "tenant:delete"
49 # Content management (generic)
50 CONTENT_READ = "content:read"
51 CONTENT_CREATE = "content:create"
52 CONTENT_UPDATE = "content:update"
53 CONTENT_DELETE = "content:delete"
55 # Billing
56 BILLING_READ = "billing:read"
57 BILLING_UPDATE = "billing:update"
59 # Invitations
60 INVITATIONS_CREATE = "invitations:create"
61 INVITATIONS_DELETE = "invitations:delete"
64class RoleHierarchy:
65 """
66 Role hierarchy and permission mapping
68 Provides:
69 - Hierarchical role comparison
70 - Permission-to-role mapping
71 - Role validation utilities
72 """
74 # Role hierarchy (higher number = more permissions)
75 HIERARCHY: Dict[Role, int] = {
76 Role.VIEWER: 0,
77 Role.MEMBER: 1,
78 Role.ADMIN: 2,
79 Role.OWNER: 3,
80 }
82 # Permission mappings (role -> list of permissions)
83 PERMISSIONS: Dict[Role, List[Permission]] = {
84 Role.VIEWER: [
85 Permission.USERS_READ,
86 Permission.TENANT_READ,
87 Permission.CONTENT_READ,
88 Permission.BILLING_READ,
89 ],
90 Role.MEMBER: [
91 # Inherits viewer permissions
92 Permission.USERS_READ,
93 Permission.TENANT_READ,
94 Permission.CONTENT_READ,
95 Permission.BILLING_READ,
96 # Additional member permissions
97 Permission.CONTENT_CREATE,
98 Permission.CONTENT_UPDATE, # Own content only
99 ],
100 Role.ADMIN: [
101 # Inherits member permissions
102 Permission.USERS_READ,
103 Permission.TENANT_READ,
104 Permission.CONTENT_READ,
105 Permission.BILLING_READ,
106 Permission.CONTENT_CREATE,
107 Permission.CONTENT_UPDATE, # All content
108 Permission.CONTENT_DELETE,
109 # Additional admin permissions
110 Permission.USERS_CREATE,
111 Permission.USERS_UPDATE,
112 Permission.INVITATIONS_CREATE,
113 Permission.INVITATIONS_DELETE,
114 ],
115 Role.OWNER: [
116 # All permissions
117 Permission.USERS_READ,
118 Permission.USERS_CREATE,
119 Permission.USERS_UPDATE,
120 Permission.USERS_DELETE,
121 Permission.TENANT_READ,
122 Permission.TENANT_UPDATE,
123 Permission.TENANT_DELETE,
124 Permission.CONTENT_READ,
125 Permission.CONTENT_CREATE,
126 Permission.CONTENT_UPDATE,
127 Permission.CONTENT_DELETE,
128 Permission.BILLING_READ,
129 Permission.BILLING_UPDATE,
130 Permission.INVITATIONS_CREATE,
131 Permission.INVITATIONS_DELETE,
132 ],
133 }
135 @classmethod
136 def has_permission(cls, user_role: str, required_permission: Permission) -> bool:
137 """
138 Check if a role has a specific permission
140 Args:
141 user_role: User's role (string)
142 required_permission: Required permission
144 Returns:
145 True if role has permission
146 """
147 try:
148 role = Role(user_role)
149 except ValueError:
150 return False
152 return required_permission in cls.PERMISSIONS.get(role, [])
154 @classmethod
155 def check_role_permission(cls, user_role: str, required_role: str) -> bool:
156 """
157 Check if user role has sufficient permissions (hierarchical)
159 Args:
160 user_role: User's current role
161 required_role: Minimum required role
163 Returns:
164 True if user has sufficient permissions
165 """
166 try:
167 user_role_enum = Role(user_role)
168 required_role_enum = Role(required_role)
169 except ValueError:
170 return False
172 user_level = cls.HIERARCHY.get(user_role_enum, -1)
173 required_level = cls.HIERARCHY.get(required_role_enum, 999)
175 return user_level >= required_level
177 @classmethod
178 def get_role_level(cls, role: str) -> int:
179 """
180 Get numeric level for a role
182 Args:
183 role: Role name
185 Returns:
186 Numeric level (0-3), or -1 if invalid
187 """
188 try:
189 role_enum = Role(role)
190 return cls.HIERARCHY.get(role_enum, -1)
191 except ValueError:
192 return -1
195class RoleAssignment(BaseModel):
196 """
197 Pydantic model for role assignment
199 Used for API requests/responses
200 """
202 user_id: str = Field(..., description="User ID (UUID)")
203 tenant_id: str = Field(..., description="Tenant ID (UUID)")
204 role: Role = Field(..., description="Assigned role")
205 assigned_by: str | None = Field(None, description="User ID who assigned the role")
207 class Config:
208 json_schema_extra = {
209 "example": {
210 "user_id": "550e8400-e29b-41d4-a716-446655440000",
211 "tenant_id": "660e8400-e29b-41d4-a716-446655440001",
212 "role": "admin",
213 "assigned_by": "770e8400-e29b-41d4-a716-446655440002",
214 }
215 }