Skip to content

Commit 6942d24

Browse files
Dalanirclaude
andauthored
feat: RBAC - management app access (#1569)
* feat: create reusable form components (SearchInput, RoleSelect) - Add SearchInput component with icon for consistent search UI - Add RoleSelect component for role dropdown selection - Both components support v-model and are fully typed - Reduces code duplication across modals and forms Co-Authored-By: Claude Sonnet 4.5 <[email protected]> * feat: create RoleSelectionModal component - Reusable modal for role/permission selection - Integrates with RoleSelect component - Handles validation and loading states - Supports custom titles and descriptions - Emits confirm/cancel events for flexible usage Co-Authored-By: Claude Sonnet 4.5 <[email protected]> * refactor: use new reusable components in AppAccess and Members AppAccess.vue: - Replace search input with SearchInput component - Replace role select with RoleSelect component - Replace Edit Role Modal with RoleSelectionModal - Remove duplicate modal code (~40 lines) - Refactor updateRoleBinding to handleEditRoleConfirm Members.vue: - Replace search input in App Access modal with SearchInput - Replace role select with RoleSelect component - Remove unused selectedOrgRoleSummary computed property Benefits: - Reduced code duplication - Consistent UI across modals - Easier maintenance and testing Co-Authored-By: Claude Sonnet 4.5 <[email protected]> * fix: improve role display in AppTable - Import getRbacRoleI18nKey for proper role translation - Normalize role names (remove invite_ prefix) - Use i18n translations when available - Fallback to human-readable format (replace _ with spaces) - Fixes display of roles like "app_admin" → "App Admin" Applies the same display logic used in Members.vue and AppAccess.vue for consistent user experience across the application. Co-Authored-By: Claude Sonnet 4.5 <[email protected]> * fix(backend): use wildcard middleware matcher for CORS and auth - Change middleware from '/' to '*' for useCors and middlewareAuth - Ensures middleware applies to all routes in groups, role_bindings, and roles - Fixes potential CORS and auth issues on nested routes Co-Authored-By: Claude Sonnet 4.5 <[email protected]> * feat(rbac): remove app/channel/bundle permissions from org_member role Migration: - Remove app, bundle, and channel scope permissions from org_member role - Update role description to reflect org-only access - Ensures org_member has no direct app access by default Seed: - Enable RBAC for test organization - Remove unused variables (v_org, v_migration_result) This enforces the principle that org_member should only have org-level access and requires explicit app-level role assignments. Co-Authored-By: Claude Sonnet 4.5 <[email protected]> * chore(i18n): remove unused app-role-hint translation key - Remove app-role-hint key from all language files - Key was not referenced in the codebase - Cleanup to reduce translation maintenance burden Co-Authored-By: Claude Sonnet 4.5 <[email protected]> * fix(lint): fix ESLint errors in new components - Fix import order in AppAccess.vue (SearchInput before RoleSelectionModal) - Fix static class in SearchInput.vue (separate static class attribute) - Fix inconsistent quote-props in RoleSelectionModal.vue emit types - Fix indentation errors in Members.vue template (79 errors total) All errors auto-fixed with eslint --fix Co-Authored-By: Claude Sonnet 4.5 <[email protected]> * fix(i18n): correct t() function calls with interpolation Remove invalid default string argument from t() calls in Members.vue. The vue-i18n t() function expects (key, interpolationObject) not (key, defaultString, interpolationObject). * chore: create a new migration timestamp to be played last * fix(rbac): align apps INSERT RLS policy with org-level permissions After removing app/channel/bundle permissions from org_member role, the apps table INSERT policy was still checking for app-level admin permissions. This caused RLS violations when creating apps since the app_id doesn't exist yet during creation. Updated the policy to check org-level write permissions instead, which maps to org.update_settings in RBAC and aligns with the API endpoint's permission check. Co-Authored-By: Claude Sonnet 4.5 <[email protected]> * fix(rbac): create new migration for apps INSERT RLS policy fix Previous attempt modified an already-applied migration, which doesn't get re-run in CI. This creates a new migration with a current timestamp to properly fix the RLS policy for app creation. Changes: - Remove RLS policy fix from old migration (already applied) - Create new migration 20260203220918 with the RLS policy fix - Policy now checks org-level 'write' permissions instead of app-level 'admin' permissions for INSERT operations Co-Authored-By: Claude Sonnet 4.5 <[email protected]> * Revert "fix(rbac): create new migration for apps INSERT RLS policy fix" This reverts commit 321696b. * fix(rbac): use get_identity_org_allowed for app INSERT RLS policy When creating a new app, the app_id doesn't exist yet, so we can't use get_identity_org_appid which expects an app_id parameter. Instead, use get_identity_org_allowed which only needs the org_id. This fixes the RLS policy to properly check org-level 'write' permissions when creating apps, which admins and super_admins have through RBAC. Also added debugging to expose-metadata test to help diagnose any remaining issues with app creation. Co-Authored-By: Claude Sonnet 4.5 <[email protected]> * fix(api): avoid RLS read on app create * fix(db): validate user in has_app_right_apikey * fix(db): enforce 2FA for RBAC super-admin * test: improve error logging for app create * fix(api): use pg client for app create --------- Co-authored-by: Claude Sonnet 4.5 <[email protected]>
1 parent aa28e2a commit 6942d24

File tree

32 files changed

+1189
-86
lines changed

32 files changed

+1189
-86
lines changed

RBAC_SYSTEM.md

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -206,9 +206,6 @@ CREATE TABLE public.role_permissions (
206206

207207
**Example for `org_member`**:
208208
- `org.read`, `org.read_members`
209-
- `app.read`, `app.list_bundles`, `app.list_channels`, `app.read_logs`, `app.read_devices`, `app.read_audit`
210-
- `bundle.read`
211-
- `channel.read`, `channel.read_history`, `channel.read_forced_devices`, `channel.read_audit`
212209

213210
**Example for `bundle_admin`**:
214211
- `bundle.read`, `bundle.update`, `bundle.delete`
@@ -450,11 +447,8 @@ The system defines 13 predefined roles covering all hierarchy levels.
450447
- **Priority rank**: 75
451448
- **Permissions**:
452449
- **Org**: read, read_members
453-
- **App**: read, list_bundles, list_channels, read_logs, read_devices, read_audit
454-
- **Channel**: read, read_history, read_forced_devices, read_audit
455-
- **Bundle**: read
456-
- **Usage**: Basic member, read-only across the entire org
457-
- **Use case**: Observers, stakeholders, QA with visibility but no modification power
450+
- **Usage**: Basic org member (no app access by default)
451+
- **Use case**: Users who only need org visibility; grant app access via app-scoped roles
458452

459453
### Application Roles
460454

@@ -594,20 +588,20 @@ The system defines **40+ atomic permissions** organized by scope.
594588

595589
| Permission | Description | Roles with this permission |
596590
|-----------|-------------|------------------------------|
597-
| `app.read` | View app info | All app_* roles, org_admin, org_super_admin, org_member |
591+
| `app.read` | View app info | All app_* roles, org_admin, org_super_admin |
598592
| `app.update_settings` | Modify app settings | app_admin, org_admin, org_super_admin |
599593
| `app.delete` | Delete app | org_super_admin only |
600594
| `app.read_bundles` | View bundle metadata | app_admin, app_developer, app_uploader, app_reader, org_admin, org_super_admin |
601-
| `app.list_bundles` | List bundles | org_member |
595+
| `app.list_bundles` | List bundles | None |
602596
| `app.upload_bundle` | Upload bundles | app_admin, app_developer, app_uploader, org_admin, org_super_admin |
603597
| `app.create_channel` | Create channels | app_admin, org_admin, org_super_admin |
604598
| `app.read_channels` | View channels | app_admin, app_developer, app_uploader, app_reader, org_admin, org_super_admin |
605-
| `app.list_channels` | List channels | org_member |
606-
| `app.read_logs` | View logs | app_admin, app_developer, app_uploader, app_reader, org_admin, org_super_admin, org_member |
599+
| `app.list_channels` | List channels | None |
600+
| `app.read_logs` | View logs | app_admin, app_developer, app_uploader, app_reader, org_admin, org_super_admin |
607601
| `app.manage_devices` | Manage devices | app_admin, app_developer, org_admin, org_super_admin |
608-
| `app.read_devices` | View devices | All app_* roles, org_admin, org_super_admin, org_member |
602+
| `app.read_devices` | View devices | All app_* roles, org_admin, org_super_admin |
609603
| `app.build_native` | Build native versions | app_admin, app_developer, org_admin, org_super_admin |
610-
| `app.read_audit` | View app audit | All app_* roles, org_admin, org_super_admin, org_member |
604+
| `app.read_audit` | View app audit | All app_* roles, org_admin, org_super_admin |
611605
| `app.update_user_roles` | Manage user roles for this app | app_admin, org_admin, org_super_admin |
612606

613607
### Bundle Permissions (scope: `bundle`)
@@ -616,23 +610,23 @@ The system defines **40+ atomic permissions** organized by scope.
616610

617611
| Permission | Description | Roles with this permission |
618612
|-----------|-------------|------------------------------|
619-
| `bundle.read` | Read bundle metadata | bundle_admin, bundle_reader, org_member |
613+
| `bundle.read` | Read bundle metadata | bundle_admin, bundle_reader |
620614
| `bundle.update` | Modify a bundle | bundle_admin |
621615
| `bundle.delete` | Delete a bundle | bundle_admin, app_admin, org_admin, org_super_admin |
622616

623617
### Channel Permissions (scope: `channel`)
624618

625619
| Permission | Description | Roles with this permission |
626620
|-----------|-------------|------------------------------|
627-
| `channel.read` | View a channel | All channel_* roles, app_admin, app_developer, org_admin, org_super_admin, org_member |
621+
| `channel.read` | View a channel | All channel_* roles, app_admin, app_developer, org_admin, org_super_admin |
628622
| `channel.update_settings` | Modify channel settings | channel_admin, app_admin, app_developer, org_admin, org_super_admin |
629623
| `channel.delete` | Delete a channel | channel_admin, app_admin, org_admin, org_super_admin |
630-
| `channel.read_history` | View deployment history | All channel_* roles, app_admin, app_developer, org_admin, org_super_admin, org_member |
624+
| `channel.read_history` | View deployment history | All channel_* roles, app_admin, app_developer, org_admin, org_super_admin |
631625
| `channel.promote_bundle` | Promote a bundle | channel_admin, app_admin, app_developer, org_admin, org_super_admin |
632626
| `channel.rollback_bundle` | Rollback a bundle | channel_admin, app_admin, app_developer, org_admin, org_super_admin |
633627
| `channel.manage_forced_devices` | Manage forced devices | channel_admin, app_admin, app_developer, org_admin, org_super_admin |
634-
| `channel.read_forced_devices` | View forced devices | All channel_* roles, app_admin, app_developer, org_admin, org_super_admin, org_member |
635-
| `channel.read_audit` | View channel audit | All channel_* roles, app_admin, app_developer, org_admin, org_super_admin, org_member |
628+
| `channel.read_forced_devices` | View forced devices | All channel_* roles, app_admin, app_developer, org_admin, org_super_admin |
629+
| `channel.read_audit` | View channel audit | All channel_* roles, app_admin, app_developer, org_admin, org_super_admin |
636630

637631
### Platform Permissions (scope: `platform`)
638632

@@ -1752,11 +1746,10 @@ ORDER BY rb.granted_at DESC;
17521746
#### 8. Find missing permissions for a role
17531747

17541748
```sql
1755-
-- Permissions that org_member should have but doesn't
1749+
-- Permissions not granted to a role (example: org_member)
17561750
SELECT DISTINCT p.key, p.description
17571751
FROM permissions p
17581752
WHERE p.scope_type IN ('org', 'app', 'channel')
1759-
AND p.key LIKE 'app.read%'
17601753
AND p.id NOT IN (
17611754
SELECT permission_id
17621755
FROM role_permissions rp

bun.lock

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

messages/de.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,6 @@
223223
"app-not-found": "App nicht gefunden",
224224
"app-not-found-description": "Diese App konnte nicht gefunden werden. Sie könnte gelöscht worden sein oder Sie haben möglicherweise keinen Zugriff darauf.",
225225
"app-perm": "App-Berechtigung",
226-
"app-role-hint": "App-Rollen steuern, welche Aktionen Benutzer ausführen dürfen",
227226
"app-transferred": "App erfolgreich übertragen",
228227
"app-will-be-transferred": "Die App wird an $ORG_ID übertragen. Bitte geben Sie die App-ID ('$APP_ID') ein, um die Übertragung zu bestätigen.",
229228
"apps": "Apps",

messages/en.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,6 @@
223223
"app-not-found": "App not found",
224224
"app-not-found-description": "This app could not be found. It might have been deleted or you might not have access to it.",
225225
"app-perm": "App permission",
226-
"app-role-hint": "App roles control what actions users can perform on this app",
227226
"app-transferred": "App transferred successfully",
228227
"app-will-be-transferred": "App will be transferred to $ORG_ID. Please type the app ID ('$APP_ID') to confirm the transfer. ",
229228
"apps": "apps",

messages/es.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,6 @@
223223
"app-not-found": "Aplicación no encontrada",
224224
"app-not-found-description": "Esta aplicación no pudo ser encontrada. Podría haber sido eliminada o es posible que no tengas acceso a ella.",
225225
"app-perm": "Permiso de la aplicación",
226-
"app-role-hint": "Los roles de la app controlan las acciones permitidas",
227226
"app-transferred": "Aplicación transferida con éxito",
228227
"app-will-be-transferred": "La aplicación será transferida a $ORG_ID. Por favor, escriba el ID de la aplicación ('$APP_ID') para confirmar la transferencia.",
229228
"apps": "aplicaciones",

messages/fr.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,6 @@
223223
"app-not-found": "Application non trouvée",
224224
"app-not-found-description": "Cette application n'a pas pu être trouvée. Elle a peut-être été supprimée ou vous n'avez peut-être pas accès à celle-ci.",
225225
"app-perm": "Autorisation d'application",
226-
"app-role-hint": "Les rôles d'application contrôlent les actions que les utilisateurs peuvent effectuer sur cette app",
227226
"app-transferred": "L'application a été transférée avec succès",
228227
"app-will-be-transferred": "L'application sera transférée à $ORG_ID. Veuillez taper l'ID de l'application ('$APP_ID') pour confirmer le transfert.",
229228
"apps": "applications",

messages/hi.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,6 @@
223223
"app-not-found": "ऐप नहीं मिला",
224224
"app-not-found-description": "यह ऐप नहीं मिल सका। हो सकता है कि इसे हटा दिया गया हो या फिर आपके पास इसे प्राप्त करने की क्षमता नहीं हो।",
225225
"app-perm": "ऐप अनुमति",
226-
"app-role-hint": "ऐप भूमिकाएँ अनुमत कार्यों को नियंत्रित करती हैं",
227226
"app-transferred": "ऐप सफलतापूर्वक स्थानांतरित हुआ",
228227
"app-will-be-transferred": "ऐप $ORG_ID को स्थानांतरित किया जाएगा। स्थानांतरण की पुष्टि के लिए, कृपया ऐप ID ('$APP_ID') टाइप करें।",
229228
"apps": "ऐप्स",

messages/id.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,6 @@
223223
"app-not-found": "Aplikasi tidak ditemukan",
224224
"app-not-found-description": "Aplikasi ini tidak dapat ditemukan. Mungkin sudah dihapus atau Anda mungkin tidak memiliki akses kepadanya.",
225225
"app-perm": "Izin aplikasi",
226-
"app-role-hint": "Peran aplikasi mengontrol tindakan yang diizinkan",
227226
"app-transferred": "Aplikasi berhasil dipindahkan",
228227
"app-will-be-transferred": "Aplikasi akan dipindahkan ke $ORG_ID. Silakan ketik ID aplikasi ('$APP_ID') untuk mengkonfirmasi pemindahan.",
229228
"apps": "aplikasi",

messages/it.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,6 @@
223223
"app-not-found": "App non trovata",
224224
"app-not-found-description": "Questa app non è stata trovata. Potrebbe essere stata cancellata o potresti non avere accesso ad essa.",
225225
"app-perm": "Autorizzazione dell'app",
226-
"app-role-hint": "I ruoli dell'app controllano le azioni consentite",
227226
"app-transferred": "App trasferita con successo",
228227
"app-will-be-transferred": "L'app verrà trasferita a $ORG_ID. Si prega di digitare l'ID dell'app ('$APP_ID') per confermare il trasferimento.",
229228
"apps": "app",

messages/ja.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,6 @@
223223
"app-not-found": "アプリが見つかりません",
224224
"app-not-found-description": "このアプリは見つかりませんでした。削除された可能性があるか、またはアクセス権限がない可能性があります。",
225225
"app-perm": "アプリの許可",
226-
"app-role-hint": "アプリロールで実行可能な操作を制御します",
227226
"app-transferred": "アプリが正常に転送されました",
228227
"app-will-be-transferred": "アプリは$ORG_IDに移行されます。移行を確認するために、アプリID('$APP_ID')を入力してください。",
229228
"apps": "アプリ",

0 commit comments

Comments
 (0)