feat(docs): expand authorization documentation for custom Alpine.js components; include manual protection patterns and implementation guidelines
This commit is contained in:
@@ -227,6 +227,159 @@ public function __construct(
|
||||
- New authorization parameters are optional
|
||||
- Legacy @can/@else patterns still function but are discouraged
|
||||
|
||||
### Custom Component Authorization Patterns
|
||||
|
||||
When dealing with **custom Alpine.js components** or complex UI elements that don't use the standard `x-forms.*` components, manual authorization protection is required since the automatic `canGate` system only applies to enhanced form components.
|
||||
|
||||
#### Common Custom Components Requiring Manual Protection
|
||||
|
||||
**⚠️ Custom Components That Need Manual Authorization:**
|
||||
- Custom dropdowns/selects with Alpine.js
|
||||
- Complex form widgets with JavaScript interactions
|
||||
- Multi-step wizards or dynamic forms
|
||||
- Third-party component integrations
|
||||
- Custom date/time pickers
|
||||
- File upload components with drag-and-drop
|
||||
|
||||
#### Manual Authorization Pattern
|
||||
|
||||
**✅ Proper Manual Authorization:**
|
||||
```html
|
||||
<!-- Custom timezone dropdown example -->
|
||||
<div class="w-full">
|
||||
<div class="flex items-center mb-1">
|
||||
<label for="customComponent">Component Label</label>
|
||||
<x-helper helper="Component description" />
|
||||
</div>
|
||||
@can('update', $resource)
|
||||
<!-- Full interactive component for authorized users -->
|
||||
<div x-data="{
|
||||
open: false,
|
||||
value: '{{ $currentValue }}',
|
||||
options: @js($options),
|
||||
init() { /* Alpine.js initialization */ }
|
||||
}">
|
||||
<input x-model="value" @focus="open = true"
|
||||
wire:model="propertyName" class="w-full input">
|
||||
<div x-show="open">
|
||||
<!-- Interactive dropdown content -->
|
||||
<template x-for="option in options" :key="option">
|
||||
<div @click="value = option; open = false; $wire.submit()"
|
||||
x-text="option"></div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
<!-- Read-only version for unauthorized users -->
|
||||
<div class="relative">
|
||||
<input readonly disabled autocomplete="off"
|
||||
class="w-full input opacity-50 cursor-not-allowed"
|
||||
value="{{ $currentValue ?: 'No value set' }}">
|
||||
<svg class="absolute right-0 mr-2 w-4 h-4 opacity-50">
|
||||
<!-- Disabled icon -->
|
||||
</svg>
|
||||
</div>
|
||||
@endcan
|
||||
</div>
|
||||
```
|
||||
|
||||
#### Implementation Checklist
|
||||
|
||||
When implementing authorization for custom components:
|
||||
|
||||
**🔍 1. Identify Custom Components:**
|
||||
- Look for Alpine.js `x-data` declarations
|
||||
- Find components not using `x-forms.*` prefix
|
||||
- Check for JavaScript-heavy interactions
|
||||
- Review complex form widgets
|
||||
|
||||
**🛡️ 2. Wrap with Authorization:**
|
||||
- Use `@can('gate', $resource)` / `@else` / `@endcan` structure
|
||||
- Provide full functionality in the `@can` block
|
||||
- Create disabled/readonly version in the `@else` block
|
||||
|
||||
**🎨 3. Design Disabled State:**
|
||||
- Apply `readonly disabled` attributes to inputs
|
||||
- Add `opacity-50 cursor-not-allowed` classes for visual feedback
|
||||
- Remove interactive JavaScript behaviors
|
||||
- Show current value or appropriate placeholder
|
||||
|
||||
**🔒 4. Backend Protection:**
|
||||
- Ensure corresponding Livewire methods check authorization
|
||||
- Add `$this->authorize('gate', $resource)` in relevant methods
|
||||
- Validate permissions before processing any changes
|
||||
|
||||
#### Real-World Examples
|
||||
|
||||
**Custom Date Range Picker:**
|
||||
```html
|
||||
@can('update', $application)
|
||||
<div x-data="dateRangePicker()" class="date-picker">
|
||||
<!-- Interactive date picker with calendar -->
|
||||
</div>
|
||||
@else
|
||||
<div class="flex gap-2">
|
||||
<input readonly disabled value="{{ $startDate }}" class="input opacity-50">
|
||||
<input readonly disabled value="{{ $endDate }}" class="input opacity-50">
|
||||
</div>
|
||||
@endcan
|
||||
```
|
||||
|
||||
**Multi-Select Component:**
|
||||
```html
|
||||
@can('update', $server)
|
||||
<div x-data="multiSelect({ options: @js($options) })">
|
||||
<!-- Interactive multi-select with checkboxes -->
|
||||
</div>
|
||||
@else
|
||||
<div class="space-y-2">
|
||||
@foreach($selectedValues as $value)
|
||||
<div class="px-3 py-1 bg-gray-100 rounded text-sm opacity-50">
|
||||
{{ $value }}
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@endcan
|
||||
```
|
||||
|
||||
**File Upload Widget:**
|
||||
```html
|
||||
@can('update', $application)
|
||||
<div x-data="fileUploader()" @drop.prevent="handleDrop">
|
||||
<!-- Drag-and-drop file upload interface -->
|
||||
</div>
|
||||
@else
|
||||
<div class="border-2 border-dashed border-gray-300 rounded-lg p-6 text-center opacity-50">
|
||||
<p class="text-gray-500">File upload restricted</p>
|
||||
@if($currentFile)
|
||||
<p class="text-sm">Current: {{ $currentFile }}</p>
|
||||
@endif
|
||||
</div>
|
||||
@endcan
|
||||
```
|
||||
|
||||
#### Key Principles
|
||||
|
||||
**🎯 Consistency:**
|
||||
- Maintain similar visual styling between enabled/disabled states
|
||||
- Use consistent disabled patterns across the application
|
||||
- Apply the same opacity and cursor styling
|
||||
|
||||
**🔐 Security First:**
|
||||
- Always implement backend authorization checks
|
||||
- Never rely solely on frontend hiding/disabling
|
||||
- Validate permissions on every server action
|
||||
|
||||
**💡 User Experience:**
|
||||
- Show current values in disabled state when appropriate
|
||||
- Provide clear visual feedback about restricted access
|
||||
- Maintain layout stability between states
|
||||
|
||||
**🚀 Performance:**
|
||||
- Minimize Alpine.js initialization for disabled components
|
||||
- Avoid loading unnecessary JavaScript for unauthorized users
|
||||
- Use simple HTML structures for read-only states
|
||||
|
||||
### Team-Based Multi-Tenancy
|
||||
- **[Team.php](mdc:app/Models/Team.php)** - Multi-tenant organization structure (8.9KB, 308 lines)
|
||||
- **[TeamInvitation.php](mdc:app/Models/TeamInvitation.php)** - Secure team collaboration
|
||||
|
||||
Reference in New Issue
Block a user