Add user role management: introduce UserRole enum for role definitions, implement role-based access control in various resources and pages, and enhance authorization logic in the PanelProvider for improved security and user experience.

This commit is contained in:
2025-07-29 15:33:36 +05:00
parent c7e01f404d
commit a1826ae53c
18 changed files with 226 additions and 0 deletions

View File

@@ -3,6 +3,7 @@
namespace App\Filament\Pages; namespace App\Filament\Pages;
use App\Settings\AboutSettings; use App\Settings\AboutSettings;
use App\Models\UserRole;
use Filament\Forms\Components\FileUpload; use Filament\Forms\Components\FileUpload;
use Filament\Forms\Components\Grid; use Filament\Forms\Components\Grid;
use Filament\Forms\Components\Repeater; use Filament\Forms\Components\Repeater;
@@ -245,4 +246,9 @@ class AboutPageSettings extends SettingsPage
{ {
return 'Manage the content sections of the About Us page.'; return 'Manage the content sections of the About Us page.';
} }
public static function canView(): bool
{
return auth()->user()->role === UserRole::ADMIN || auth()->user()->role === UserRole::MANAGER;
}
} }

View File

@@ -3,6 +3,7 @@
namespace App\Filament\Pages; namespace App\Filament\Pages;
use App\Settings\ContactSettings; use App\Settings\ContactSettings;
use App\Models\UserRole;
use Filament\Forms\Components\Section; use Filament\Forms\Components\Section;
use Filament\Forms\Components\Textarea; use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput; use Filament\Forms\Components\TextInput;
@@ -84,4 +85,9 @@ class ContactPageSettings extends SettingsPage
{ {
return 'Manage the contact form details, contact information, and map embed.'; return 'Manage the contact form details, contact information, and map embed.';
} }
public static function canView(): bool
{
return auth()->user()->role === UserRole::ADMIN || auth()->user()->role === UserRole::MANAGER;
}
} }

View File

@@ -3,6 +3,7 @@
namespace App\Filament\Pages; namespace App\Filament\Pages;
use App\Settings\HomeSettings; use App\Settings\HomeSettings;
use App\Models\UserRole;
use Filament\Forms\Components\FileUpload; use Filament\Forms\Components\FileUpload;
use Filament\Forms\Components\Grid; use Filament\Forms\Components\Grid;
use Filament\Forms\Components\Repeater; use Filament\Forms\Components\Repeater;
@@ -292,4 +293,9 @@ class HomePageSettings extends SettingsPage
{ {
return 'Manage the homepage hero section, background video, and call-to-action content.'; return 'Manage the homepage hero section, background video, and call-to-action content.';
} }
public static function canView(): bool
{
return auth()->user()->role === UserRole::ADMIN || auth()->user()->role === UserRole::MANAGER;
}
} }

View File

@@ -3,6 +3,7 @@
namespace App\Filament\Pages; namespace App\Filament\Pages;
use App\Settings\SiteSettings; use App\Settings\SiteSettings;
use App\Models\UserRole;
use Filament\Forms; use Filament\Forms;
use Filament\Forms\Form; use Filament\Forms\Form;
use Filament\Pages\SettingsPage; use Filament\Pages\SettingsPage;
@@ -168,4 +169,9 @@ class ManageSite extends SettingsPage
{ {
return 'Manage your website\'s general configuration'; return 'Manage your website\'s general configuration';
} }
public static function canView(): bool
{
return auth()->user()->role === UserRole::ADMIN || auth()->user()->role === UserRole::MANAGER;
}
} }

View File

@@ -3,6 +3,7 @@
namespace App\Filament\Pages; namespace App\Filament\Pages;
use App\Settings\SiteSocialSettings; use App\Settings\SiteSocialSettings;
use App\Models\UserRole;
use Filament\Forms; use Filament\Forms;
use Filament\Forms\Form; use Filament\Forms\Form;
use Filament\Pages\SettingsPage; use Filament\Pages\SettingsPage;
@@ -77,4 +78,9 @@ class ManageSiteSocialSettings extends SettingsPage
{ {
return 'Manage your social media profiles and sharing options'; return 'Manage your social media profiles and sharing options';
} }
public static function canView(): bool
{
return auth()->user()->role === UserRole::ADMIN || auth()->user()->role === UserRole::MANAGER;
}
} }

View File

@@ -5,6 +5,7 @@ namespace App\Filament\Resources;
use App\Filament\Resources\ApplicationResource\Pages; use App\Filament\Resources\ApplicationResource\Pages;
use App\Models\Application; use App\Models\Application;
use App\Models\Career; use App\Models\Career;
use App\Models\UserRole;
use Filament\Forms; use Filament\Forms;
use Filament\Forms\Form; use Filament\Forms\Form;
use Filament\Resources\Resource; use Filament\Resources\Resource;
@@ -104,4 +105,9 @@ class ApplicationResource extends Resource
'edit' => Pages\EditApplication::route('/{record}/edit'), 'edit' => Pages\EditApplication::route('/{record}/edit'),
]; ];
} }
public static function canViewAny(): bool
{
return auth()->user()->role === UserRole::ADMIN || auth()->user()->role === UserRole::MANAGER;
}
} }

View File

@@ -13,6 +13,7 @@ use Filament\Resources\Resource;
use Filament\Tables; use Filament\Tables;
use Filament\Tables\Columns\ImageColumn; use Filament\Tables\Columns\ImageColumn;
use Filament\Tables\Table; use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
class AuthorResource extends Resource class AuthorResource extends Resource
{ {
@@ -91,4 +92,34 @@ class AuthorResource extends Resource
'edit' => Pages\EditAuthor::route('/{record}/edit'), 'edit' => Pages\EditAuthor::route('/{record}/edit'),
]; ];
} }
public static function canViewAny(): bool
{
return auth()->user()->can('view-authors');
}
public static function canCreate(): bool
{
return auth()->user()->can('view-authors');
}
public static function canEdit(mixed $record): bool
{
return auth()->user()->can('view-authors');
}
public static function canDelete(mixed $record): bool
{
return auth()->user()->can('view-authors');
}
public static function canDeleteAny(): bool
{
return auth()->user()->can('view-authors');
}
public static function getEloquentQuery(): Builder
{
return parent::getEloquentQuery()->withoutGlobalScopes();
}
} }

View File

@@ -4,6 +4,7 @@ namespace App\Filament\Resources;
use App\Filament\Resources\BrandResource\Pages; use App\Filament\Resources\BrandResource\Pages;
use App\Models\Brand; use App\Models\Brand;
use App\Models\UserRole;
use Filament\Forms\Components\FileUpload; use Filament\Forms\Components\FileUpload;
use Filament\Forms\Components\TextInput; use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle; use Filament\Forms\Components\Toggle;
@@ -79,4 +80,9 @@ class BrandResource extends Resource
'index' => Pages\ManageBrands::route('/'), 'index' => Pages\ManageBrands::route('/'),
]; ];
} }
public static function canViewAny(): bool
{
return auth()->user()->role === UserRole::ADMIN || auth()->user()->role === UserRole::MANAGER;
}
} }

View File

@@ -5,6 +5,7 @@ namespace App\Filament\Resources;
use App\Filament\Resources\CareerResource\Pages; use App\Filament\Resources\CareerResource\Pages;
use App\Filament\Resources\CareerResource\RelationManagers; use App\Filament\Resources\CareerResource\RelationManagers;
use App\Models\Career; use App\Models\Career;
use App\Models\UserRole;
use Filament\Forms; use Filament\Forms;
use Filament\Forms\Components\Repeater; use Filament\Forms\Components\Repeater;
use Filament\Forms\Components\Textarea; use Filament\Forms\Components\Textarea;
@@ -117,4 +118,9 @@ class CareerResource extends Resource
'edit' => Pages\EditCareer::route('/{record}/edit'), 'edit' => Pages\EditCareer::route('/{record}/edit'),
]; ];
} }
public static function canViewAny(): bool
{
return auth()->user()->role === UserRole::ADMIN || auth()->user()->role === UserRole::MANAGER;
}
} }

View File

@@ -4,6 +4,7 @@ namespace App\Filament\Resources;
use App\Filament\Resources\CommentResource\Pages; use App\Filament\Resources\CommentResource\Pages;
use App\Models\Comment; use App\Models\Comment;
use App\Models\UserRole;
use Filament\Forms; use Filament\Forms;
use Filament\Forms\Components\RichEditor; use Filament\Forms\Components\RichEditor;
use Filament\Forms\Components\Select; use Filament\Forms\Components\Select;
@@ -93,4 +94,9 @@ class CommentResource extends Resource
'edit' => Pages\EditComment::route('/{record}/edit'), 'edit' => Pages\EditComment::route('/{record}/edit'),
]; ];
} }
public static function canViewAny(): bool
{
return auth()->user()->role === UserRole::ADMIN || auth()->user()->role === UserRole::MANAGER;
}
} }

View File

@@ -5,6 +5,7 @@ namespace App\Filament\Resources;
use App\Filament\Resources\InternshipResource\Pages; use App\Filament\Resources\InternshipResource\Pages;
use App\Filament\Resources\InternshipResource\RelationManagers; use App\Filament\Resources\InternshipResource\RelationManagers;
use App\Models\Internship; use App\Models\Internship;
use App\Models\UserRole;
use Filament\Forms; use Filament\Forms;
use Filament\Forms\Components\Repeater; use Filament\Forms\Components\Repeater;
use Filament\Forms\Components\Textarea; use Filament\Forms\Components\Textarea;
@@ -120,4 +121,9 @@ class InternshipResource extends Resource
'edit' => Pages\EditInternship::route('/{record}/edit'), 'edit' => Pages\EditInternship::route('/{record}/edit'),
]; ];
} }
public static function canViewAny(): bool
{
return auth()->user()->role === UserRole::ADMIN || auth()->user()->role === UserRole::MANAGER;
}
} }

View File

@@ -17,6 +17,8 @@ use Filament\Tables;
use Filament\Tables\Columns\ImageColumn; use Filament\Tables\Columns\ImageColumn;
use Filament\Tables\Table; use Filament\Tables\Table;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Auth;
class NewsResource extends Resource class NewsResource extends Resource
{ {
@@ -123,4 +125,34 @@ class NewsResource extends Resource
'edit' => Pages\EditNews::route('/{record}/edit'), 'edit' => Pages\EditNews::route('/{record}/edit'),
]; ];
} }
public static function canViewAny(): bool
{
return auth()->user()->can('manage-news-and-success');
}
public static function canCreate(): bool
{
return auth()->user()->can('manage-news-and-success');
}
public static function canEdit(mixed $record): bool
{
return auth()->user()->can('manage-news-and-success');
}
public static function canDelete(mixed $record): bool
{
return auth()->user()->can('manage-news-and-success');
}
public static function canDeleteAny(): bool
{
return auth()->user()->can('manage-news-and-success');
}
public static function getEloquentQuery(): Builder
{
return parent::getEloquentQuery()->withoutGlobalScopes();
}
} }

View File

@@ -4,6 +4,7 @@ namespace App\Filament\Resources;
use App\Filament\Resources\SolutionResource\Pages; use App\Filament\Resources\SolutionResource\Pages;
use App\Models\Solution; use App\Models\Solution;
use App\Models\UserRole;
use Filament\Forms; use Filament\Forms;
use Filament\Forms\Form; use Filament\Forms\Form;
use Filament\Resources\Resource; use Filament\Resources\Resource;
@@ -129,4 +130,9 @@ class SolutionResource extends Resource
'edit' => Pages\EditSolution::route('/{record}/edit'), 'edit' => Pages\EditSolution::route('/{record}/edit'),
]; ];
} }
public static function canViewAny(): bool
{
return auth()->user()->role === UserRole::ADMIN || auth()->user()->role === UserRole::MANAGER;
}
} }

View File

@@ -15,6 +15,7 @@ use Filament\Tables;
use Filament\Tables\Columns\ImageColumn; use Filament\Tables\Columns\ImageColumn;
use Filament\Tables\Table; use Filament\Tables\Table;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Illuminate\Database\Eloquent\Builder;
class SuccessResource extends Resource class SuccessResource extends Resource
{ {
@@ -112,4 +113,34 @@ class SuccessResource extends Resource
'edit' => Pages\EditSuccess::route('/{record}/edit'), 'edit' => Pages\EditSuccess::route('/{record}/edit'),
]; ];
} }
public static function canViewAny(): bool
{
return auth()->user()->can('manage-news-and-success');
}
public static function canCreate(): bool
{
return auth()->user()->can('manage-news-and-success');
}
public static function canEdit(mixed $record): bool
{
return auth()->user()->can('manage-news-and-success');
}
public static function canDelete(mixed $record): bool
{
return auth()->user()->can('manage-news-and-success');
}
public static function canDeleteAny(): bool
{
return auth()->user()->can('manage-news-and-success');
}
public static function getEloquentQuery(): Builder
{
return parent::getEloquentQuery()->withoutGlobalScopes();
}
} }

View File

@@ -5,6 +5,7 @@ namespace App\Filament\Resources;
use App\Filament\Resources\TeamMemberResource\Pages; use App\Filament\Resources\TeamMemberResource\Pages;
use App\Filament\Resources\TeamMemberResource\RelationManagers; use App\Filament\Resources\TeamMemberResource\RelationManagers;
use App\Models\TeamMember; use App\Models\TeamMember;
use App\Models\UserRole;
use Filament\Forms\Components\FileUpload; use Filament\Forms\Components\FileUpload;
use Filament\Forms\Components\Textarea; use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput; use Filament\Forms\Components\TextInput;
@@ -91,4 +92,9 @@ class TeamMemberResource extends Resource
'edit' => Pages\EditTeamMember::route('/{record}/edit'), 'edit' => Pages\EditTeamMember::route('/{record}/edit'),
]; ];
} }
public static function canViewAny(): bool
{
return auth()->user()->role === UserRole::ADMIN || auth()->user()->role === UserRole::MANAGER;
}
} }

View File

@@ -2,6 +2,13 @@
namespace App\Models; namespace App\Models;
enum UserRole: string
{
case ADMIN = 'admin';
case MANAGER = 'manager';
case NEWS_WRITER = 'news_writer';
}
// use Illuminate\Contracts\Auth\MustVerifyEmail; // use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Foundation\Auth\User as Authenticatable;
@@ -21,6 +28,7 @@ class User extends Authenticatable
'name', 'name',
'email', 'email',
'password', 'password',
'role',
]; ];
/** /**
@@ -43,6 +51,7 @@ class User extends Authenticatable
return [ return [
'email_verified_at' => 'datetime', 'email_verified_at' => 'datetime',
'password' => 'hashed', 'password' => 'hashed',
'role' => UserRole::class,
]; ];
} }
} }

View File

@@ -20,6 +20,9 @@ use Illuminate\Routing\Middleware\SubstituteBindings;
use Illuminate\Session\Middleware\AuthenticateSession; use Illuminate\Session\Middleware\AuthenticateSession;
use Illuminate\Session\Middleware\StartSession; use Illuminate\Session\Middleware\StartSession;
use Illuminate\View\Middleware\ShareErrorsFromSession; use Illuminate\View\Middleware\ShareErrorsFromSession;
use App\Models\User;
use App\Models\UserRole;
use Illuminate\Support\Facades\Gate;
class PanelPanelProvider extends PanelProvider class PanelPanelProvider extends PanelProvider
{ {
@@ -30,6 +33,7 @@ class PanelPanelProvider extends PanelProvider
->id('panel') ->id('panel')
->path('panel') ->path('panel')
->login() ->login()
->profile()
->colors([ ->colors([
'primary' => Color::Amber, 'primary' => Color::Amber,
]) ])
@@ -73,4 +77,23 @@ class PanelPanelProvider extends PanelProvider
ApplicationResource::class, ApplicationResource::class,
]); ]);
} }
public function boot(): void
{
Gate::before(function (User $user, string $ability) {
return $user->role === UserRole::ADMIN ? true : null;
});
Gate::define('view-activity-logs', function (User $user) {
return $user->role === UserRole::ADMIN;
});
Gate::define('manage-news-and-success', function (User $user) {
return $user->role === UserRole::NEWS_WRITER || $user->role === UserRole::ADMIN || $user->role === UserRole::MANAGER;
});
Gate::define('view-authors', function (User $user) {
return $user->role === UserRole::NEWS_WRITER || $user->role === UserRole::ADMIN || $user->role === UserRole::MANAGER;
});
}
} }

View File

@@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->string('role')->default('news_writer')->nullable();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('role');
});
}
};