252 lines
7.3 KiB
Markdown
252 lines
7.3 KiB
Markdown
# Authorization System Implementation Summary
|
|
|
|
## ✅ Implementation Complete
|
|
|
|
A comprehensive polymorphic record sharing and authorization system has been implemented with CASL, Objection.js, and NestJS.
|
|
|
|
## What Was Built
|
|
|
|
### Backend (NestJS + Objection.js + CASL)
|
|
|
|
#### 1. Database Layer
|
|
- ✅ Migration for authorization tables (`20250128000001_add_authorization_system.js`)
|
|
- ✅ Updated Prisma schema with new models
|
|
- ✅ Objection.js models: `ObjectField`, `RoleRule`, `RecordShare`
|
|
- ✅ Updated existing models with new relations
|
|
|
|
#### 2. Authorization Core
|
|
- ✅ `AbilityFactory` - Builds CASL abilities from 3 layers (global, role, share)
|
|
- ✅ Query scoping utilities for SQL-level authorization
|
|
- ✅ Guards and decorators (`AbilitiesGuard`, `@CheckAbility()`, `@CurrentUser()`)
|
|
- ✅ Middleware for attaching abilities to requests
|
|
|
|
#### 3. API Endpoints
|
|
- ✅ **ShareController** - CRUD for record shares
|
|
- POST /shares - Create share
|
|
- GET /shares/record/:objectDefinitionId/:recordId - List shares
|
|
- GET /shares/granted - Shares granted by user
|
|
- GET /shares/received - Shares received by user
|
|
- PATCH /shares/:id - Update share
|
|
- DELETE /shares/:id - Revoke share
|
|
|
|
- ✅ **RoleController** - Role management
|
|
- Standard CRUD for roles
|
|
- RoleRuleController for CASL rules
|
|
|
|
- ✅ **ObjectAccessController** - Object-level permissions
|
|
- GET/PUT /setup/objects/:apiName/access
|
|
- POST /setup/objects/:apiName/fields/:fieldKey/permissions
|
|
- PUT /setup/objects/:apiName/field-permissions
|
|
|
|
### Frontend (Nuxt 3 + Vue 3)
|
|
|
|
#### 4. Object Management Enhancement
|
|
- ✅ Added "Access & Permissions" tab to object setup page
|
|
- ✅ `ObjectAccessSettings.vue` component:
|
|
- Configure access model (public/owner/mixed)
|
|
- Set public CRUD permissions
|
|
- Configure owner field
|
|
- Set field-level read/write permissions
|
|
|
|
#### 5. Role Management
|
|
- ✅ New page: `/setup/roles`
|
|
- ✅ `RolePermissionsEditor.vue` component:
|
|
- Configure CRUD permissions per object
|
|
- Apply conditions (e.g., own records only)
|
|
- Visual permission matrix
|
|
|
|
#### 6. Record Sharing
|
|
- ✅ `RecordShareDialog.vue` component:
|
|
- List current shares
|
|
- Add new shares with permissions
|
|
- Field-level scoping
|
|
- Expiration dates
|
|
- Revoke shares
|
|
|
|
## Key Features
|
|
|
|
### 🌍 Global Object Policies
|
|
- Public/private access models
|
|
- Default CRUD permissions per object
|
|
- Configurable owner field
|
|
- Field-level default permissions
|
|
|
|
### 👥 Role-Based Access
|
|
- CASL rules stored in database
|
|
- Per-object permissions
|
|
- Condition-based rules (e.g., ownerId matching)
|
|
- Multiple actions per rule
|
|
|
|
### 🔗 Per-Record Sharing
|
|
- Polymorphic design (works with any object type)
|
|
- Grant read/update access to specific users
|
|
- Optional field-level scoping
|
|
- Expiration and revocation support
|
|
- Track who granted each share
|
|
|
|
### 🔒 SQL Query Scoping
|
|
- Critical for list endpoints
|
|
- Ensures users only see authorized records
|
|
- Combines ownership + sharing logic
|
|
- Works with public access flags
|
|
|
|
## File Structure
|
|
|
|
```
|
|
backend/
|
|
├── migrations/tenant/
|
|
│ └── 20250128000001_add_authorization_system.js
|
|
├── src/
|
|
│ ├── auth/
|
|
│ │ ├── ability.factory.ts (CASL ability builder)
|
|
│ │ ├── query-scope.util.ts (SQL scoping utilities)
|
|
│ │ ├── guards/
|
|
│ │ │ └── abilities.guard.ts
|
|
│ │ ├── decorators/
|
|
│ │ │ ├── auth.decorators.ts
|
|
│ │ │ └── check-ability.decorator.ts
|
|
│ │ └── middleware/
|
|
│ │ └── ability.middleware.ts
|
|
│ ├── models/
|
|
│ │ ├── object-field.model.ts
|
|
│ │ ├── role-rule.model.ts
|
|
│ │ └── record-share.model.ts
|
|
│ ├── rbac/
|
|
│ │ ├── share.controller.ts
|
|
│ │ └── role.controller.ts
|
|
│ └── object/
|
|
│ └── object-access.controller.ts
|
|
|
|
frontend/
|
|
├── components/
|
|
│ ├── ObjectAccessSettings.vue
|
|
│ ├── RecordShareDialog.vue
|
|
│ └── RolePermissionsEditor.vue
|
|
└── pages/
|
|
├── setup/
|
|
│ ├── objects/[apiName].vue (enhanced with access tab)
|
|
│ └── roles.vue
|
|
└── ...
|
|
|
|
docs/
|
|
└── AUTHORIZATION_SYSTEM.md (comprehensive documentation)
|
|
```
|
|
|
|
## Next Steps
|
|
|
|
### 1. Run the Migration
|
|
```bash
|
|
cd backend
|
|
npm run migrate:latest
|
|
```
|
|
|
|
### 2. Initialize Existing Objects
|
|
Set default access models for existing object definitions:
|
|
```sql
|
|
UPDATE object_definitions
|
|
SET
|
|
access_model = 'owner',
|
|
public_read = false,
|
|
public_create = false,
|
|
public_update = false,
|
|
public_delete = false,
|
|
owner_field = 'ownerId'
|
|
WHERE access_model IS NULL;
|
|
```
|
|
|
|
### 3. Apply Query Scoping
|
|
Update existing controllers to use query scoping:
|
|
|
|
```typescript
|
|
import { applyReadScope } from '@/auth/query-scope.util';
|
|
|
|
// In your list endpoint
|
|
async findAll(@CurrentUser() user: User) {
|
|
const objectDef = await ObjectDefinition.query(this.knex)
|
|
.findOne({ apiName: 'YourObject' });
|
|
|
|
let query = YourModel.query(this.knex);
|
|
query = applyReadScope(query, user, objectDef, this.knex);
|
|
|
|
return query;
|
|
}
|
|
```
|
|
|
|
### 4. Add Route Protection
|
|
Use guards on sensitive endpoints:
|
|
|
|
```typescript
|
|
@UseGuards(JwtAuthGuard, AbilitiesGuard)
|
|
@CheckAbility({ action: 'update', subject: 'Post' })
|
|
async update(@Body() data: any) {
|
|
// Only users with 'update' permission on 'Post' can access
|
|
}
|
|
```
|
|
|
|
### 5. Frontend Integration
|
|
Add sharing button to record detail pages:
|
|
|
|
```vue
|
|
<template>
|
|
<div>
|
|
<!-- Your record details -->
|
|
<Button @click="showShareDialog = true">
|
|
<Share class="w-4 h-4 mr-2" />
|
|
Share
|
|
</Button>
|
|
|
|
<RecordShareDialog
|
|
:open="showShareDialog"
|
|
:object-definition-id="objectDefinition.id"
|
|
:record-id="record.id"
|
|
:fields="objectDefinition.fields"
|
|
@close="showShareDialog = false"
|
|
/>
|
|
</div>
|
|
</template>
|
|
```
|
|
|
|
## Testing Checklist
|
|
|
|
- [ ] Run database migration successfully
|
|
- [ ] Create a test role with permissions
|
|
- [ ] Configure object access settings via UI
|
|
- [ ] Share a record with another user
|
|
- [ ] Verify shared record appears in grantee's list
|
|
- [ ] Verify query scoping filters unauthorized records
|
|
- [ ] Test field-level permissions
|
|
- [ ] Test share expiration
|
|
- [ ] Test share revocation
|
|
- [ ] Test role-based access with conditions
|
|
|
|
## Performance Considerations
|
|
|
|
1. **Index Usage**: The migration creates proper indexes on foreign keys and commonly queried columns
|
|
2. **Query Scoping**: Uses SQL EXISTS subqueries for efficient filtering
|
|
3. **Ability Caching**: Consider caching abilities per request (already done via middleware)
|
|
4. **Batch Loading**: When checking multiple records, batch the share lookups
|
|
|
|
## Security Notes
|
|
|
|
⚠️ **Important**: Always use SQL query scoping for list endpoints. Never fetch all records and filter in application code.
|
|
|
|
✅ **Best Practices**:
|
|
- Share creation requires ownership verification
|
|
- Only grantors can update/revoke shares
|
|
- Expired/revoked shares are excluded from queries
|
|
- Field-level permissions are enforced on write operations
|
|
|
|
## Documentation
|
|
|
|
Full documentation available in:
|
|
- [AUTHORIZATION_SYSTEM.md](./AUTHORIZATION_SYSTEM.md) - Comprehensive guide
|
|
- Inline code comments in all new files
|
|
- JSDoc comments on key functions
|
|
|
|
## Support
|
|
|
|
For questions or issues:
|
|
1. Check the documentation in `docs/AUTHORIZATION_SYSTEM.md`
|
|
2. Review example usage in the controllers
|
|
3. Examine the test cases (when added)
|