Coverage for netrun / rbac / models.py: 100%

58 statements  

« prev     ^ index     » next       coverage.py v7.12.0, created at 2025-12-18 22:20 +0000

1""" 

2RBAC Models - Role and Permission definitions 

3 

4Extracted from: Intirkast middleware/rbac.py 

5Generalized for multi-project reuse 

6""" 

7 

8from enum import Enum 

9from typing import Dict, List 

10from pydantic import BaseModel, Field 

11 

12 

13class Role(str, Enum): 

14 """ 

15 Role hierarchy for multi-tenant SaaS platforms 

16 

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 """ 

23 

24 VIEWER = "viewer" 

25 MEMBER = "member" 

26 ADMIN = "admin" 

27 OWNER = "owner" 

28 

29 

30class Permission(str, Enum): 

31 """ 

32 Fine-grained permissions for resource access control 

33 

34 Pattern: {resource}:{action} 

35 Example: posts:create, users:delete, billing:read 

36 """ 

37 

38 # User management 

39 USERS_READ = "users:read" 

40 USERS_CREATE = "users:create" 

41 USERS_UPDATE = "users:update" 

42 USERS_DELETE = "users:delete" 

43 

44 # Tenant management 

45 TENANT_READ = "tenant:read" 

46 TENANT_UPDATE = "tenant:update" 

47 TENANT_DELETE = "tenant:delete" 

48 

49 # Content management (generic) 

50 CONTENT_READ = "content:read" 

51 CONTENT_CREATE = "content:create" 

52 CONTENT_UPDATE = "content:update" 

53 CONTENT_DELETE = "content:delete" 

54 

55 # Billing 

56 BILLING_READ = "billing:read" 

57 BILLING_UPDATE = "billing:update" 

58 

59 # Invitations 

60 INVITATIONS_CREATE = "invitations:create" 

61 INVITATIONS_DELETE = "invitations:delete" 

62 

63 

64class RoleHierarchy: 

65 """ 

66 Role hierarchy and permission mapping 

67 

68 Provides: 

69 - Hierarchical role comparison 

70 - Permission-to-role mapping 

71 - Role validation utilities 

72 """ 

73 

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 } 

81 

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 } 

134 

135 @classmethod 

136 def has_permission(cls, user_role: str, required_permission: Permission) -> bool: 

137 """ 

138 Check if a role has a specific permission 

139 

140 Args: 

141 user_role: User's role (string) 

142 required_permission: Required permission 

143 

144 Returns: 

145 True if role has permission 

146 """ 

147 try: 

148 role = Role(user_role) 

149 except ValueError: 

150 return False 

151 

152 return required_permission in cls.PERMISSIONS.get(role, []) 

153 

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) 

158 

159 Args: 

160 user_role: User's current role 

161 required_role: Minimum required role 

162 

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 

171 

172 user_level = cls.HIERARCHY.get(user_role_enum, -1) 

173 required_level = cls.HIERARCHY.get(required_role_enum, 999) 

174 

175 return user_level >= required_level 

176 

177 @classmethod 

178 def get_role_level(cls, role: str) -> int: 

179 """ 

180 Get numeric level for a role 

181 

182 Args: 

183 role: Role name 

184 

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 

193 

194 

195class RoleAssignment(BaseModel): 

196 """ 

197 Pydantic model for role assignment 

198 

199 Used for API requests/responses 

200 """ 

201 

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") 

206 

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 }