first init
This commit is contained in:
@@ -0,0 +1,252 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Auth\Entities;
|
||||
|
||||
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Illuminate\Database\Eloquent\Concerns\HasUuids;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Laravel\Sanctum\HasApiTokens;
|
||||
use Spatie\Activitylog\LogOptions;
|
||||
use Spatie\Activitylog\Traits\LogsActivity;
|
||||
use Spatie\Permission\Contracts\Permission;
|
||||
use Spatie\Permission\PermissionRegistrar;
|
||||
use Spatie\Permission\Traits\HasPermissions;
|
||||
use Spatie\Permission\Traits\HasRoles;
|
||||
use App\Services\ActiveRoleService;
|
||||
use App\Traits\HasVisibility;
|
||||
use Lab404\Impersonate\Models\Impersonate;
|
||||
use Modules\Role\Entities\Role;
|
||||
|
||||
class User extends Authenticatable
|
||||
{
|
||||
/** @use HasFactory<\Modules\Auth\Database\Factories\UserFactory> */
|
||||
use HasApiTokens, HasFactory, HasPermissions, HasRoles, HasUuids, Impersonate, LogsActivity, Notifiable, SoftDeletes, HasVisibility;
|
||||
|
||||
protected $table = 'users';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var list<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'email',
|
||||
'password',
|
||||
'ic_number',
|
||||
'position',
|
||||
'phone_number',
|
||||
'image_url',
|
||||
'status',
|
||||
'two_factor_secret',
|
||||
'two_factor_recovery_codes',
|
||||
'two_factor_confirmed_at',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be hidden for serialization.
|
||||
*
|
||||
* @var list<string>
|
||||
*/
|
||||
protected $hidden = [
|
||||
'password',
|
||||
'remember_token',
|
||||
'two_factor_secret',
|
||||
'two_factor_recovery_codes',
|
||||
'two_factor_confirmed_at',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the attributes that should be cast.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'email_verified_at' => 'datetime',
|
||||
'password' => 'hashed',
|
||||
'status' => 'string',
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Role used for permission checks in the current session (Sanctum token).
|
||||
*/
|
||||
public function activeRole(): ?Role
|
||||
{
|
||||
return ActiveRoleService::getActiveRole($this);
|
||||
}
|
||||
|
||||
public function hasPermissionAction($action): bool
|
||||
{
|
||||
$role = $this->activeRole();
|
||||
|
||||
return $role && $role->permission_actions &&
|
||||
in_array($action, $role->permission_actions, true);
|
||||
}
|
||||
|
||||
public function getActivityLogOptions(): LogOptions
|
||||
{
|
||||
return LogOptions::defaults()
|
||||
->logAll()
|
||||
->logOnlyDirty()
|
||||
->dontSubmitEmptyLogs()
|
||||
->setDescriptionForEvent(fn (string $eventName) => "User {$this->name} was {$eventName}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Override hasPermissionTo to bypass all permission checks for DEVELOPER role
|
||||
*/
|
||||
public function hasPermissionTo($permission, $guardName = null): bool
|
||||
{
|
||||
$activeRole = $this->activeRole();
|
||||
|
||||
if ($activeRole?->name === 'DEVELOPER') {
|
||||
return true;
|
||||
}
|
||||
|
||||
static $isCheckingPermission = false;
|
||||
|
||||
if ($isCheckingPermission) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$isCheckingPermission = true;
|
||||
|
||||
try {
|
||||
$permissionClass = app(PermissionRegistrar::class)->getPermissionClass();
|
||||
|
||||
if (is_string($permission)) {
|
||||
$permission = $permissionClass::findByName($permission, $guardName ?? 'api');
|
||||
}
|
||||
|
||||
if (is_int($permission)) {
|
||||
$permission = $permissionClass::findById($permission, $guardName ?? 'api');
|
||||
}
|
||||
|
||||
if (! $permission instanceof Permission) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->permissions->contains('id', $permission->id)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (! $activeRole) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$activeRole->loadMissing('permissions');
|
||||
|
||||
return $activeRole->permissions->contains('id', $permission->id);
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
} finally {
|
||||
$isCheckingPermission = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope to exclude current user unless they have developer role
|
||||
*/
|
||||
public function scopeExcludeCurrentUserUnlessDeveloper($query, $currentUser = null)
|
||||
{
|
||||
$currentUser = $currentUser ?? auth()->user();
|
||||
|
||||
if ($currentUser && ! $currentUser->hasRole('DEVELOPER')) {
|
||||
return $query->where('id', '!=', $currentUser->id);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope to exclude users with DEVELOPER role unless current user is DEVELOPER
|
||||
*/
|
||||
public function scopeExcludeDevelopersUnlessDeveloper($query, $currentUser = null)
|
||||
{
|
||||
$currentUser = $currentUser ?? auth()->user();
|
||||
|
||||
if ($currentUser && ! $currentUser->hasRole('DEVELOPER')) {
|
||||
return $query->whereDoesntHave('roles', function ($q) {
|
||||
$q->where('name', 'DEVELOPER');
|
||||
});
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the user can impersonate another user
|
||||
*/
|
||||
public function canImpersonate(): bool
|
||||
{
|
||||
// Check permission instead of hardcoded roles
|
||||
return $this->hasPermissionTo('menyamar pengguna');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the user can be impersonated
|
||||
*/
|
||||
public function canBeImpersonated(): bool
|
||||
{
|
||||
// DEVELOPER cannot be impersonated by anyone
|
||||
if ($this->hasRole('DEVELOPER')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the current user has permission to impersonate
|
||||
$currentUser = auth()->user();
|
||||
if (!$currentUser) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Users with permission can impersonate other users (except DEVELOPER)
|
||||
return $currentUser->hasPermissionTo('menyamar pengguna');
|
||||
}
|
||||
|
||||
public function hasVerifiedEmail(): bool
|
||||
{
|
||||
return $this->email_verified_at !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user can login based on their status
|
||||
*/
|
||||
public function canLogin(): bool
|
||||
{
|
||||
return $this->status === 'active';
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether credentials are valid for issuing a session (includes verified pending users).
|
||||
*/
|
||||
public function canAuthenticate(): bool
|
||||
{
|
||||
if ($this->canLogin()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->hasVerifiedEmail() && $this->status === 'pending';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the login restriction message based on user status
|
||||
*/
|
||||
public function getLoginRestrictionMessage(): ?string
|
||||
{
|
||||
switch ($this->status) {
|
||||
case 'pending':
|
||||
return 'Akaun anda sedang menunggu pengaktifan dari pentadbir sistem. Sila hubungi pentadbir sistem.';
|
||||
case 'inactive':
|
||||
return 'Akaun anda tidak aktif. Sila hubungi pentadbir sistem.';
|
||||
default:
|
||||
return 'Akaun anda tidak dapat mengakses sistem. Sila hubungi pentadbir sistem.';
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user