@@ -30,6 +30,22 @@ A comprehensive, production-ready permissions system with role-based access cont
3030- ⚡ ** Zero Config Start** - Works out of the box with sensible defaults
3131- 🔧 ** Fully Typed** - Complete TypeScript support
3232
33+ ## UI Overview
34+
35+ Payload Gatekeeper provides a clean and intuitive interface for managing roles and permissions:
36+
37+ ### Roles Management
38+ ![ Roles Collection] ( docs/roles-collection-with-default-roles.png )
39+ * The Roles collection showing system default roles - fully manageable through the UI*
40+
41+ ### User Role Assignment
42+ ![ User Creation with Role Selection] ( docs/users-collection-create-new-user-select-roles.png )
43+ * Assigning roles when creating new users - with searchable dropdown*
44+
45+ ### Users with Roles
46+ ![ Users Collection with Roles] ( docs/users-collection-with-roles.png )
47+ * Users overview showing their assigned roles*
48+
3349## Installation
3450
3551``` bash
@@ -55,13 +71,22 @@ export default buildConfig({
5571 collections: {
5672 ' users' : {
5773 enhance: true ,
74+ autoAssignFirstUser: true ,
5875 }
59- }
76+ },
77+ // Exclude collections from permission system entirely
78+ excludeCollections: [' special-config' ] // These use their own access control
6079 })
6180 ]
6281})
6382```
6483
84+ ### First User Setup
85+ When ` autoAssignFirstUser ` is enabled, the first user automatically receives the super_admin role:
86+
87+ ![ First User Creation] ( docs/create-first-user.png )
88+ * The first user gets super admin privileges automatically*
89+
6590## Configuration
6691
6792### Full Configuration Example
@@ -143,11 +168,8 @@ export default buildConfig({
143168 // Optional: Exclude collections from permission system
144169 excludeCollections: [' public-pages' ],
145170
146- // Optional: Enable audit logging
147- enableAuditLog: false ,
148-
149171 // Environment-based options
150- seedingMode : false , // Set to true during seeding
172+ skipPermissionChecks : false , // Set to true during seeding/migration
151173 syncRolesOnInit: process .env .SYNC_ROLES === ' true' ,
152174
153175 // UI customization
@@ -160,6 +182,41 @@ export default buildConfig({
160182
161183## Core Concepts
162184
185+ ### Public Access
186+
187+ Non-authenticated users automatically have configurable public access:
188+
189+ ``` typescript
190+ // Default behavior - public can read all non-auth collections
191+ gatekeeperPlugin ({})
192+
193+ // Custom public permissions
194+ gatekeeperPlugin ({
195+ publicRolePermissions: [
196+ ' *.read' , // Read all collections
197+ ' comments.create' , // Can create comments
198+ ' reactions.create' // Can add reactions
199+ ]
200+ })
201+
202+ // Completely private system
203+ gatekeeperPlugin ({
204+ disablePublicRole: true // No public access at all
205+ })
206+ ```
207+
208+ ** Important:** Auth-enabled collections (users, admins) are ALWAYS protected from public access, regardless of public permissions.
209+
210+ ### Role Management
211+
212+ The plugin automatically creates a Roles collection where you can manage all roles through the UI:
213+
214+ ![ Creating Editor Role] ( docs/roles-collection-create-new-role-editor.png )
215+ * Creating a new editor role with specific permissions*
216+
217+ ![ Roles with Custom Editor] ( docs/roles-collection-with-custom-role-editor.png )
218+ * Roles collection showing both system and custom roles*
219+
163220### Permission Patterns
164221
165222The plugin supports various permission patterns:
@@ -175,13 +232,16 @@ The plugin supports various permission patterns:
175232Protected roles cannot be deleted and have restricted field updates. Only users with ` * ` permission can modify protected roles.
176233
177234``` typescript
178- {
235+ const superAdminRole = {
179236 name: ' super_admin' ,
180237 permissions: [' *' ],
181238 protected: true , // Cannot be deleted, limited updates
182239}
183240```
184241
242+ ![ Super Admin Role Details] ( docs/role-details-super-admin.png )
243+ * Protected super admin role showing the lock indicator and full permissions*
244+
185245### UI Visibility vs CRUD Operations
186246
187247The plugin separates UI visibility from CRUD operations:
@@ -405,14 +465,18 @@ gatekeeperPlugin({
405465})
406466```
407467
408- ### Seeding First Admin User
468+ ### Seeding Users
469+
470+ #### Simple: First Admin User (with autoAssignFirstUser)
471+
472+ When ` autoAssignFirstUser: true ` is configured, you don't need to search for roles - the first user automatically gets super_admin:
409473
410474``` typescript
411475// seed-admin.ts
412476import { getPayload } from ' payload'
413477import config from ' ./payload.config'
414478
415- async function seedAdmin () {
479+ async function seedFirstAdmin () {
416480 const payload = await getPayload ({ config })
417481
418482 try {
@@ -426,38 +490,81 @@ async function seedAdmin() {
426490 return
427491 }
428492
429- // Find the super_admin role (created by plugin on first start)
430- const superAdminRole = await payload .find ({
431- collection: ' roles' ,
432- where: {
433- name: { equals: ' super_admin' },
434- },
435- })
436-
437- if (superAdminRole .docs .length === 0 ) {
438- throw new Error (' Super admin role not found! Start the application first.' )
439- }
440-
441- // Create the first admin user
493+ // Create the first admin user - automatically gets super_admin role!
442494 await payload .create ({
443495 collection: ' users' ,
444496 data: {
445497 email: ' admin@example.com' ,
446498 password: ' SecurePassword123!' ,
447- role: superAdminRole .docs [0 ].id ,
448- // ... other fields
499+ // No need to set role - autoAssignFirstUser handles it
449500 },
450501 })
451502
452- console .log (' ✅ Admin user created successfully ' )
503+ console .log (' ✅ First admin user created with super_admin role ' )
453504 } catch (error ) {
454505 console .error (' Error seeding admin:' , error )
455506 }
456507
457508 process .exit (0 )
458509}
459510
460- seedAdmin ()
511+ seedFirstAdmin ()
512+ ```
513+
514+ #### Advanced: Multiple Users with Specific Roles
515+
516+ Only search for roles when you need to create additional users with specific roles:
517+
518+ ``` typescript
519+ // seed-users.ts
520+ async function seedUsers() {
521+ const payload = await getPayload ({ config })
522+
523+ try {
524+ // Find specific roles for additional users
525+ const editorRole = await payload .find ({
526+ collection: ' roles' ,
527+ where: { name: { equals: ' editor' } },
528+ limit: 1 ,
529+ })
530+
531+ const viewerRole = await payload .find ({
532+ collection: ' roles' ,
533+ where: { name: { equals: ' viewer' } },
534+ limit: 1 ,
535+ })
536+
537+ // Create editor user
538+ if (editorRole .docs .length > 0 ) {
539+ await payload .create ({
540+ collection: ' users' ,
541+ data: {
542+ email: ' editor@example.com' ,
543+ password: ' EditorPass123!' ,
544+ role: editorRole .docs [0 ].id ,
545+ },
546+ })
547+ }
548+
549+ // Create viewer user
550+ if (viewerRole .docs .length > 0 ) {
551+ await payload .create ({
552+ collection: ' users' ,
553+ data: {
554+ email: ' viewer@example.com' ,
555+ password: ' ViewerPass123!' ,
556+ role: viewerRole .docs [0 ].id ,
557+ },
558+ })
559+ }
560+
561+ console .log (' ✅ Additional users created' )
562+ } catch (error ) {
563+ console .error (' Error seeding users:' , error )
564+ }
565+ }
566+
567+ seedUsers ()
461568```
462569
463570### Custom Permission Checks in Your Code
@@ -494,8 +601,9 @@ async function myCustomEndpoint(req: PayloadRequest) {
494601| ` collections ` | ` object ` | Collection-specific configuration | ` {} ` |
495602| ` systemRoles ` | ` array ` | Roles to create/sync on init | ` [] ` |
496603| ` excludeCollections ` | ` string[] ` | Collections to exclude from permission system | ` [] ` |
497- | ` enableAuditLog ` | ` boolean ` | Enable audit logging | ` false ` |
498- | ` seedingMode ` | ` boolean ` | Skip permission checks during seeding | ` false ` |
604+ | ` disablePublicRole ` | ` boolean ` | Disable public access for non-authenticated users | ` false ` |
605+ | ` publicRolePermissions ` | ` string[] ` | Custom permissions for public users | ` ['*.read'] ` |
606+ | ` skipPermissionChecks ` | ` boolean \| (() => boolean) ` | Skip permission checks (for seeding/migration) | ` false ` |
499607| ` syncRolesOnInit ` | ` boolean ` | Force role sync on every init | ` false ` |
500608| ` rolesGroup ` | ` string ` | UI group name for Roles collection | ` 'System' ` |
501609| ` rolesSlug ` | ` string ` | Custom slug for Roles collection | ` 'roles' ` |
@@ -615,16 +723,32 @@ const Products: CollectionConfig = {
615723// Both must pass for access to be granted
616724```
617725
618- ### Environment Variables
726+ ### Using Environment Variables
619727
620- ``` bash
621- # Skip permission checks during seeding (configure in plugin options)
622- npm run seed
728+ The plugin doesn't read environment variables directly. You need to configure them in your plugin options:
729+
730+ ``` typescript
731+ // payload.config.ts
732+ gatekeeperPlugin ({
733+ // Use environment variables in your config
734+ syncRolesOnInit: process .env .SYNC_ROLES === ' true' ,
735+ skipPermissionChecks: process .env .SKIP_PERMISSIONS === ' true' ,
736+
737+ // Or use a function for dynamic control
738+ skipPermissionChecks : () => process .env .NODE_ENV === ' seed' ,
739+ })
740+ ```
623741
742+ Then run your application with environment variables:
743+
744+ ``` bash
624745# Force role synchronization
625746SYNC_ROLES=true npm run dev
626747
627- # Development mode (auto-syncs roles)
748+ # Skip permissions during seeding
749+ NODE_ENV=seed npm run seed
750+
751+ # Development mode (auto-syncs roles when NODE_ENV=development)
628752NODE_ENV=development npm run dev
629753```
630754
@@ -668,8 +792,6 @@ const role: Role = {
668792
669793## Testing
670794
671- The plugin has comprehensive test coverage with a blackbox testing approach:
672-
673795``` bash
674796# Run all tests
675797npm test
@@ -782,16 +904,6 @@ Contributions are welcome! Please read our contributing guidelines before submit
782904
783905For issues, questions, or suggestions, please open an issue on GitHub.
784906
785- # # Roadmap
786-
787- - [ ] Global permissions UI component
788- - [ ] Audit log persistence
789- - [ ] Permission templates
790- - [ ] Dynamic permission generation from custom fields
791- - [ ] GraphQL support
792- - [ ] Permission caching layer
793- - [ ] Role hierarchy support
794-
795907# # Acknowledgments
796908
797909Built with ❤️ for the [Payload CMS](https://payloadcms.com/) community.
0 commit comments