|
| 1 | +# RBAC Service |
| 2 | + |
| 3 | +Role-Based Access Control (RBAC) service for Semaphore CI/CD platform. |
| 4 | + |
| 5 | +## Database Schema |
| 6 | + |
| 7 | +### Core RBAC System |
| 8 | + |
| 9 | +```mermaid |
| 10 | +erDiagram |
| 11 | + scopes ||--o{ permissions : has |
| 12 | + scopes ||--o{ rbac_roles : has |
| 13 | +
|
| 14 | + permissions ||--o{ role_permission_bindings : "" |
| 15 | + rbac_roles ||--o{ role_permission_bindings : "" |
| 16 | +
|
| 17 | + rbac_roles ||--o{ role_inheritance : inheriting |
| 18 | + rbac_roles ||--o{ role_inheritance : inherited |
| 19 | +
|
| 20 | + rbac_roles ||--o{ subject_role_bindings : "" |
| 21 | + subjects ||--o{ subject_role_bindings : "" |
| 22 | +
|
| 23 | + scopes { |
| 24 | + int id |
| 25 | + string scope_name |
| 26 | + } |
| 27 | +
|
| 28 | + permissions { |
| 29 | + int id |
| 30 | + string name |
| 31 | + int scope_id |
| 32 | + string description |
| 33 | + } |
| 34 | +
|
| 35 | + rbac_roles { |
| 36 | + int id |
| 37 | + string name |
| 38 | + uuid org_id |
| 39 | + int scope_id |
| 40 | + string description |
| 41 | + boolean editable |
| 42 | + } |
| 43 | +
|
| 44 | + role_permission_bindings { |
| 45 | + int permission_id |
| 46 | + int rbac_role_id |
| 47 | + } |
| 48 | +
|
| 49 | + role_inheritance { |
| 50 | + int inheriting_role_id |
| 51 | + int inherited_role_id |
| 52 | + } |
| 53 | +
|
| 54 | + subject_role_bindings { |
| 55 | + int id |
| 56 | + int role_id |
| 57 | + uuid org_id |
| 58 | + uuid project_id |
| 59 | + int subject_id |
| 60 | + string binding_source |
| 61 | + } |
| 62 | +
|
| 63 | + subjects { |
| 64 | + int id |
| 65 | + string name |
| 66 | + string type |
| 67 | + } |
| 68 | +
|
| 69 | + rbac_roles ||--o{ org_role_to_proj_role_mappings : org_role |
| 70 | + rbac_roles ||--o{ org_role_to_proj_role_mappings : proj_role |
| 71 | +
|
| 72 | + org_role_to_proj_role_mappings { |
| 73 | + int org_role_id |
| 74 | + int proj_role_id |
| 75 | + } |
| 76 | +``` |
| 77 | + |
| 78 | +### Subject System (Users & Groups) |
| 79 | + |
| 80 | +```mermaid |
| 81 | +erDiagram |
| 82 | + subjects ||--o| rbac_users : is_type |
| 83 | + subjects ||--o| groups : is_type |
| 84 | +
|
| 85 | + rbac_users ||--o{ user_group_bindings : "" |
| 86 | + groups ||--o{ user_group_bindings : "" |
| 87 | +
|
| 88 | + subjects { |
| 89 | + int id |
| 90 | + string name |
| 91 | + string type |
| 92 | + } |
| 93 | +
|
| 94 | + rbac_users { |
| 95 | + int id |
| 96 | + string email |
| 97 | + string name |
| 98 | + } |
| 99 | +
|
| 100 | + groups { |
| 101 | + int id |
| 102 | + uuid org_id |
| 103 | + uuid creator_id |
| 104 | + string description |
| 105 | + } |
| 106 | +
|
| 107 | + user_group_bindings { |
| 108 | + int user_id |
| 109 | + int group_id |
| 110 | + } |
| 111 | +``` |
| 112 | + |
| 113 | +### Identity Provider Integration |
| 114 | + |
| 115 | +```mermaid |
| 116 | +erDiagram |
| 117 | + rbac_users ||--o{ oidc_sessions : "" |
| 118 | + rbac_users ||--o{ oidc_users : "" |
| 119 | +
|
| 120 | + rbac_users { |
| 121 | + int id |
| 122 | + string email |
| 123 | + string name |
| 124 | + } |
| 125 | +
|
| 126 | + oidc_sessions { |
| 127 | + uuid id |
| 128 | + int user_id |
| 129 | + bytea refresh_token_enc |
| 130 | + bytea id_token_enc |
| 131 | + timestamp expires_at |
| 132 | + } |
| 133 | +
|
| 134 | + oidc_users { |
| 135 | + uuid id |
| 136 | + int user_id |
| 137 | + string oidc_user_id |
| 138 | + } |
| 139 | +
|
| 140 | + okta_integrations { |
| 141 | + int id |
| 142 | + uuid org_id |
| 143 | + uuid creator_id |
| 144 | + string saml_issuer |
| 145 | + boolean jit_provisioning_enabled |
| 146 | + } |
| 147 | +
|
| 148 | + okta_users { |
| 149 | + int id |
| 150 | + uuid integration_id |
| 151 | + uuid org_id |
| 152 | + uuid user_id |
| 153 | + string email |
| 154 | + } |
| 155 | +
|
| 156 | + saml_jit_users { |
| 157 | + int id |
| 158 | + uuid integration_id |
| 159 | + uuid org_id |
| 160 | + uuid user_id |
| 161 | + string email |
| 162 | + } |
| 163 | +
|
| 164 | + idp_group_mapping { |
| 165 | + int id |
| 166 | + uuid organization_id |
| 167 | + uuid default_role_id |
| 168 | + array role_mapping |
| 169 | + array group_mapping |
| 170 | + } |
| 171 | +``` |
| 172 | + |
| 173 | +### Legacy Tables |
| 174 | + |
| 175 | +```mermaid |
| 176 | +erDiagram |
| 177 | + projects ||--o{ collaborators : "" |
| 178 | + projects ||--o{ project_members : "" |
| 179 | + users ||--o{ project_members : "" |
| 180 | + users ||--o{ roles : "" |
| 181 | +
|
| 182 | + projects { |
| 183 | + uuid id |
| 184 | + uuid project_id |
| 185 | + string repo_name |
| 186 | + uuid org_id |
| 187 | + string provider |
| 188 | + } |
| 189 | +
|
| 190 | + collaborators { |
| 191 | + uuid id |
| 192 | + uuid project_id |
| 193 | + string github_username |
| 194 | + string github_uid |
| 195 | + boolean admin |
| 196 | + boolean push |
| 197 | + boolean pull |
| 198 | + } |
| 199 | +
|
| 200 | + users { |
| 201 | + uuid id |
| 202 | + uuid user_id |
| 203 | + string github_uid |
| 204 | + string provider |
| 205 | + } |
| 206 | +
|
| 207 | + project_members { |
| 208 | + uuid id |
| 209 | + uuid project_id |
| 210 | + uuid user_id |
| 211 | + } |
| 212 | +
|
| 213 | + roles { |
| 214 | + uuid id |
| 215 | + uuid user_id |
| 216 | + uuid org_id |
| 217 | + string name |
| 218 | + } |
| 219 | +``` |
| 220 | + |
| 221 | +### Key-Value Stores & Audit |
| 222 | + |
| 223 | +```mermaid |
| 224 | +erDiagram |
| 225 | + user_permissions_key_value_store { |
| 226 | + string key |
| 227 | + text value |
| 228 | + } |
| 229 | +
|
| 230 | + project_access_key_value_store { |
| 231 | + string key |
| 232 | + text value |
| 233 | + } |
| 234 | +
|
| 235 | + global_permissions_audit_log { |
| 236 | + int id |
| 237 | + string key |
| 238 | + text old_value |
| 239 | + text new_value |
| 240 | + string query_operation |
| 241 | + boolean notified |
| 242 | + } |
| 243 | +``` |
| 244 | + |
| 245 | +### Background Job Tables |
| 246 | + |
| 247 | +```mermaid |
| 248 | +erDiagram |
| 249 | + collaborator_refresh_requests { |
| 250 | + uuid id |
| 251 | + uuid org_id |
| 252 | + string state |
| 253 | + uuid requester_user_id |
| 254 | + } |
| 255 | +
|
| 256 | + rbac_refresh_project_access_requests { |
| 257 | + uuid id |
| 258 | + string state |
| 259 | + uuid org_id |
| 260 | + int user_id |
| 261 | + } |
| 262 | +
|
| 263 | + rbac_refresh_all_permissions_requests { |
| 264 | + int id |
| 265 | + string state |
| 266 | + int organizations_updated |
| 267 | + int retries |
| 268 | + } |
| 269 | +
|
| 270 | + group_management_request { |
| 271 | + int id |
| 272 | + string state |
| 273 | + uuid user_id |
| 274 | + uuid group_id |
| 275 | + string action |
| 276 | + } |
| 277 | +``` |
| 278 | + |
| 279 | +## Schema Notes |
| 280 | + |
| 281 | +### RBAC Architecture |
| 282 | +- **Scopes** categorize permissions (org-level, project-level) |
| 283 | +- **Permissions** are individual access rights within scopes |
| 284 | +- **Roles** bundle multiple permissions together |
| 285 | +- **Role Inheritance** allows roles to inherit permissions from other roles |
| 286 | +- **Org-to-Project Mappings** automatically map organization roles to project roles |
| 287 | + |
| 288 | +### Subject System (Polymorphic Design) |
| 289 | +- **Subjects** is a base table for both users and groups |
| 290 | +- **rbac_users** inherits from subjects (1:1 relationship) |
| 291 | +- **groups** inherits from subjects (1:1 relationship) |
| 292 | +- **subject_role_bindings** assigns roles to any subject with source tracking |
| 293 | + |
| 294 | +### Binding Sources |
| 295 | +The `subject_role_bindings.binding_source` enum tracks where role assignments originate: |
| 296 | +- `github` - From GitHub collaborator permissions |
| 297 | +- `bitbucket` - From Bitbucket collaborator permissions |
| 298 | +- `gitlab` - From GitLab collaborator permissions |
| 299 | +- `manually_assigned` - Manually assigned by admin |
| 300 | +- `okta` - From Okta/SCIM integration |
| 301 | +- `inherited_from_org_role` - Inherited from organization role |
| 302 | +- `saml_jit` - From SAML just-in-time provisioning |
| 303 | + |
| 304 | +### Repository Access Mapping |
| 305 | +- Maps legacy repository permissions (admin/push/pull) to RBAC roles |
| 306 | +- One mapping per organization |
| 307 | +- References three different roles for each access level |
| 308 | + |
| 309 | +### Identity Providers |
| 310 | +- **OIDC**: OpenID Connect sessions and user mappings |
| 311 | +- **Okta**: SAML/SCIM integration with JIT provisioning support |
| 312 | +- **SAML JIT**: Just-in-time user provisioning via SAML |
| 313 | + |
| 314 | +### Audit System |
| 315 | +- Database trigger on `user_permissions_key_value_store` automatically logs changes |
| 316 | +- Tracks old/new values for permission changes |
| 317 | +- Notified flag for tracking alert status |
0 commit comments