Commit 8cb4030
authored
Add granular role-based permissions and super admin support (#41)
* Migrate from legacy role-based to granular permission-based access control
- Replace legacy Role type (admin/manager/agent) with permission-based system
- Add Permission, CustomRole, and RolePermission models for granular RBAC
- Update User model to use RoleID referencing CustomRole instead of Role string
- Add TeamRole type for team membership roles (manager/agent within teams)
- Implement HasPermission cache with Redis support for efficient permission checks
- Update all handlers to use permission-based checks (HasPermission)
- Add roles.go handler for CustomRole CRUD operations
- Seed system roles (admin, manager, agent) per organization with default permissions
- Update JWT claims to include RoleID instead of legacy Role
- Remove route-level role checking in main.go (now handled by handlers)
- Update SSOProvider to use DefaultRoleName string for auto-created users
- Update all tests to work with new permission system
* Add super admin support for cross-organization access
- Add IsSuperAdmin field to User model for platform-wide admin access
- Include IsSuperAdmin in JWT claims and middleware context
- Update HasPermission to automatically grant all permissions to super admins
- Add ScopeToOrg helper to conditionally bypass org filtering for super admins
- Update key handlers (contacts, users, roles, teams) to use org scoping helper
- Make default admin user a super admin during initial setup
* Add roles and permissions management UI
- Create RolesView with full CRUD for custom roles
- Add reusable PermissionMatrix component for permission selection
- Add roles and permissions API services
- Create roles Pinia store with permission grouping
- Update UsersView to use role selection from roles store
- Ensure permissions are seeded in migrations for existing installations
* Fix lint issues and add proper ESLint configuration
Backend (Go):
- Fix file.Close() error not checked in campaigns.go and templates.go
- Fix capitalized error strings in flows.go
- Convert if/else to switch statement in flows.go
- Simplify embedded field access in cache.go
- Export GetRolePermissionsCached for reuse
Frontend:
- Add .eslintrc.cjs with proper Vue 3 + TypeScript config
- Add .gitignore for frontend directory
- Fix duplicate keyframes in tailwind.config.cjs (radix -> reka)
- Fix side effects in computed function in ApiMockDialog.vue
* Add tests for roles and permissions
Backend (Go):
- Add comprehensive unit tests for roles CRUD operations
- Add tests for permission listing and role constraints
- Update test database utilities to include roles tables
Frontend (E2E):
- Add Playwright E2E tests for roles management
- Test role creation, editing, deletion
- Test permission selection in role dialog
- Test system role restrictions
- Simplify test scripts (remove Vitest, use E2E only)
* Fix roles tests and delete role handler bug
- Fix testApp to include Redis connection for permission caching
- Fix auth_test to create actual role before assigning to user
- Fix roles_test to use existing permissions instead of creating duplicates
- Fix DeleteRole handler to check correct column (role_id instead of custom_role_id)
* Fix role object handling and E2E test issues
Backend:
- Preload Role in Login handler so role object is returned
- Populate user.Role in Register response
- Fix backend tests to create proper roles for test users
Frontend:
- Update User interface to use role object instead of string
- Fix all role checks to use role?.name instead of role
- Update auth store userRole computed to return role.name
- Fix websocket.ts role comparisons
E2E Tests:
- Update playwright config to use Vite dev server port 3000
- Fix roles.spec.ts locators to be more specific
- Fix LoginPage to wait for network idle after login
- Update global-setup to include organization_name
* Add test:e2e script for CI workflow
* Fix CI test race condition with sequential package execution
Add -p 1 flag to go test to run packages sequentially. This prevents
race conditions where multiple test packages share the same test
database and one package's cleanup truncates data while another
package is inserting.
* Enable and fix E2E global-setup to properly create test users
- Enable globalSetup in playwright.config.ts (was commented out)
- Update global-setup to properly create test users with correct roles:
1. Register admin@test.com (creates org with admin role)
2. Use admin token to fetch role IDs (manager, agent)
3. Create manager@test.com and agent@test.com via users API
with their respective roles in the same organization
* Use default superadmin to create test users in global-setup
Login as the default superadmin (admin@admin.com) created by migrations
to create test users. This ensures proper permissions for user creation.
* Add super admin support with organization management
Backend:
- Add is_super_admin field to UserRequest/UserResponse
- Allow super admins to set/modify super admin status on users
- Add ListOrganizations endpoint (super admin only)
- Add GetCurrentOrganization endpoint
- ScopeToOrg already bypasses org filtering for super admins
Frontend:
- Add is_super_admin to User interfaces in auth and users stores
- Add super admin toggle in user form (visible only to super admins)
- Add organizations API service
- Show super admin badge in users table
* Add organization switcher for super admins
Backend:
- Update getOrgIDFromContext to allow super admins to override org via X-Organization-ID header
- Add X-Organization-ID to CORS allowed headers
Frontend:
- Add organizations store to manage org list and selection
- Add OrganizationSwitcher component in sidebar (visible only to super admins)
- Update API interceptor to include X-Organization-ID header when org is selected
- Integrate organization switcher into AppLayout
Super admins can now:
- View all organizations in dropdown
- Switch between organizations to manage their data
- Select "All Organizations" to see data across all orgs
* Fix organization switcher visibility and add loading states
* Add error display and console logging for organizations API debugging
* Fix organization filtering for super admins
- getOrgIDFromContext now returns uuid.Nil when super admin views "all orgs"
- ScopeToOrg/ScopedQuery only bypass filtering when orgID is uuid.Nil
- When super admin selects a specific org, data is filtered to that org
- Add getOrgIDForCreate helper for create operations (always returns valid org)
* Remove 'All Organizations' option - always require specific org selection
* Remove unused getOrgIDForCreate function
* Fix: Remove unused function and add Redis nil checks for tests
* Fix users search test to use specific email to avoid multiple matches
* Implement granular permissions system with real-time updates
Backend:
- Add permission checks to chatbot flow handlers
- Cache role permissions in Redis with automatic invalidation
- Broadcast permission updates via WebSocket when roles change
- Add user-targeted WebSocket broadcasts for permission notifications
- Load permissions from cache in login and /me endpoints
Frontend:
- Replace legacy role-based routing with permission-based access
- Add navigation filtering based on user permissions
- Auto-redirect to first accessible page on login
- Show parent menus if user has any child permission
- Refresh user permissions via WebSocket without page reload
- Improve PermissionMatrix UX: collapse by default, auto-expand selected,
sort selected groups to top
- Remove debug console.log statements
Router changes:
- All routes now use permission meta instead of roles
- getFirstAccessibleRoute() finds best landing page for user
* Fix role_permissions table empty for existing setups
Add SeedSystemRolesForAllOrgs() call in migration flow to fix existing
organizations where system roles exist but have no permissions linked.
This is idempotent - skips roles that already have permissions.
* Add migration for existing users' roles and super admin
- MigrateExistingUserRoles: Maps old role column (admin/manager/agent)
to new role_id for existing users. Safe for fresh installs - checks
if old column exists before proceeding.
- Set admin@admin.com as super admin if exists
- All functions are idempotent - safe to run multiple times
* Remove "all organizations" view from backend
- getOrgIDFromContext: Falls back to user's org instead of returning
uuid.Nil when super admin has no X-Organization-ID header
- ScopedQuery/ScopeToOrg: Always filter by organization, never return
unfiltered queries
This aligns backend with frontend which removed the "All Organizations"
option from the organization switcher.
* Add E2E tests for custom role permissions
- Add ApiHelper class for reusable API operations in tests
- Add permission-based UI tests that:
- Create custom roles with specific permissions
- Create users with those roles
- Verify sidebar menu visibility based on permissions
- Verify page access/redirect based on permissions
- Compare admin vs limited role access
- Add role fixtures to helpers
10 new tests covering permission-based access control.
* Add test-results to gitignore
* Refetch data on organization change in settings views
Add watcher for organizationsStore.selectedOrgId in:
- UsersView
- RolesView
- TeamsView
- TemplatesView
- AccountsView
- WebhooksView
This ensures data is refreshed when super admin switches organizations.
* Fix getOrganizationID to check X-Organization-ID header
The getOrganizationID function now checks for the X-Organization-ID
header override, allowing super admins to switch organizations and
have all API endpoints respect the selected org.
This fixes the issue where user list wasn't updating when switching
organizations because the function only read from JWT context.
* Restrict X-Organization-ID header to super admins only
Security fix: Only super admins can use the X-Organization-ID header
to switch organizations. Regular users and API key users are locked
to their own organization.
This prevents API key users from accessing other organizations by
sending the header.
* Add E2E tests for organization switching
- Test super admin can see organization switcher
- Test switching organization updates users list
- Test regular user cannot see organization switcher
- Test API respects X-Organization-ID header for super admin only
- Test organization data isolation between orgs
* Improve organization switch E2E tests with data isolation
- Add register() helper to create new organizations
- Add getOrganizations() and getUsersWithOrgHeader() helpers
- Create test organizations dynamically instead of requiring pre-existing ones
- Test users from one org are not visible in another org
- Test regular user cannot use X-Organization-ID header to access other orgs
* Trigger CI for organization-switch tests
* Add documentation for granular roles and permissions
- Update README with new permissions feature description
- Fix configuration docs link to use hosted URL
- Add roles-permissions.mdx feature guide covering:
- System roles (admin, manager, agent)
- Custom roles creation
- Permission matrix
- Super admin organization switching
- UI behavior based on permissions
- Add roles.mdx API reference with all endpoints
- Update users.mdx to reflect role_id based system
- Add new docs to sidebar navigation1 parent 2f8c651 commit 8cb4030
File tree
82 files changed
+7801
-975
lines changed- .github/workflows
- cmd/whatomate
- docs
- src/content/docs
- api-reference
- features
- frontend
- e2e
- helpers
- pages
- tests/settings
- src
- components
- chatbot/flow-preview
- layout
- roles
- router
- services
- stores
- views
- analytics
- auth
- chatbot
- chat
- profile
- settings
- internal
- database
- handlers
- middleware
- models
- websocket
- worker
- test
- fixtures/models
- testutil
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
82 files changed
+7801
-975
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
56 | 56 | | |
57 | 57 | | |
58 | 58 | | |
59 | | - | |
| 59 | + | |
| 60 | + | |
60 | 61 | | |
61 | 62 | | |
62 | 63 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
53 | 53 | | |
54 | 54 | | |
55 | 55 | | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
11 | 11 | | |
12 | 12 | | |
13 | 13 | | |
14 | | - | |
15 | | - | |
| 14 | + | |
| 15 | + | |
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
| |||
94 | 94 | | |
95 | 95 | | |
96 | 96 | | |
97 | | - | |
| 97 | + | |
98 | 98 | | |
99 | 99 | | |
100 | 100 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
17 | | - | |
18 | 17 | | |
19 | 18 | | |
20 | 19 | | |
| |||
432 | 431 | | |
433 | 432 | | |
434 | 433 | | |
435 | | - | |
436 | | - | |
437 | | - | |
438 | | - | |
439 | | - | |
440 | | - | |
441 | | - | |
442 | | - | |
443 | | - | |
444 | | - | |
445 | | - | |
446 | | - | |
447 | | - | |
448 | | - | |
449 | | - | |
450 | | - | |
451 | | - | |
452 | | - | |
453 | | - | |
454 | | - | |
455 | | - | |
456 | | - | |
457 | | - | |
458 | | - | |
459 | | - | |
460 | | - | |
461 | | - | |
462 | | - | |
463 | | - | |
464 | | - | |
465 | | - | |
466 | | - | |
467 | | - | |
468 | | - | |
469 | | - | |
470 | | - | |
471 | | - | |
472 | | - | |
473 | | - | |
474 | | - | |
475 | | - | |
476 | | - | |
477 | | - | |
478 | | - | |
479 | | - | |
480 | | - | |
481 | | - | |
482 | | - | |
483 | | - | |
484 | | - | |
485 | | - | |
486 | | - | |
487 | | - | |
488 | | - | |
489 | | - | |
490 | | - | |
491 | | - | |
492 | | - | |
493 | | - | |
494 | | - | |
495 | | - | |
496 | | - | |
| 434 | + | |
| 435 | + | |
497 | 436 | | |
498 | 437 | | |
499 | 438 | | |
| |||
510 | 449 | | |
511 | 450 | | |
512 | 451 | | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
| 455 | + | |
| 456 | + | |
| 457 | + | |
| 458 | + | |
| 459 | + | |
513 | 460 | | |
514 | 461 | | |
515 | 462 | | |
| |||
649 | 596 | | |
650 | 597 | | |
651 | 598 | | |
| 599 | + | |
| 600 | + | |
| 601 | + | |
| 602 | + | |
652 | 603 | | |
653 | 604 | | |
654 | 605 | | |
| |||
714 | 665 | | |
715 | 666 | | |
716 | 667 | | |
717 | | - | |
| 668 | + | |
718 | 669 | | |
719 | 670 | | |
720 | 671 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
24 | 24 | | |
25 | 25 | | |
26 | 26 | | |
| 27 | + | |
27 | 28 | | |
28 | 29 | | |
29 | 30 | | |
| |||
38 | 39 | | |
39 | 40 | | |
40 | 41 | | |
| 42 | + | |
41 | 43 | | |
42 | 44 | | |
43 | 45 | | |
| |||
0 commit comments