feat(scanner): scope login tickets scanning and history by event
This commit is contained in:
@@ -16,6 +16,9 @@ class ScannerMeResource extends JsonResource
|
||||
return [
|
||||
'user' => UserResource::make($this->resource),
|
||||
'tenant' => TenantResource::make($this->tenant),
|
||||
'event' => $this->whenLoaded('event', fn () => $this->event === null ? null : [
|
||||
'id' => $this->event->id, 'title' => $this->event->title,
|
||||
]),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ class UserResource extends JsonResource
|
||||
'rol_codigo' => $this->rol_codigo,
|
||||
'tenant_codigo' => $this->tenant_codigo,
|
||||
'admin_scope' => $this->when($this->rol_codigo === RoleCode::AdminApp->value, $this->admin_scope),
|
||||
'event_id' => $this->when($this->rol_codigo === RoleCode::AdminApp->value, $this->event_id),
|
||||
'event_id' => $this->when(in_array($this->rol_codigo, [RoleCode::AdminApp->value, RoleCode::Scanner->value], true), $this->event_id),
|
||||
'categories' => $this->whenLoaded('scanCategories', fn () => $this->scanCategories
|
||||
->map(fn ($category) => [
|
||||
'id' => $category->id,
|
||||
|
||||
@@ -10,6 +10,7 @@ use App\Domains\Core\Authorization\Enums\PermissionCode;
|
||||
use App\Domains\Core\Authorization\Enums\RoleCode;
|
||||
use App\Shared\Notification\Events\PasswordResetRequested;
|
||||
use Carbon\CarbonImmutable;
|
||||
use Illuminate\Auth\Access\AuthorizationException;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
@@ -206,6 +207,17 @@ class PasswordLoginService
|
||||
return ['outcome' => LoginAttempt::OUTCOME_INVALID_CREDENTIALS, 'user' => $user, 'locked_until' => null];
|
||||
}
|
||||
|
||||
if ($requiredRole === RoleCode::Scanner) {
|
||||
try {
|
||||
app(EventScopeService::class)->eventId($user);
|
||||
} catch (AuthorizationException) {
|
||||
$this->recordAttempt($user, $normalizedEmail, $attemptTenantCode,
|
||||
LoginAttempt::OUTCOME_INVALID_CREDENTIALS, $ipAddress, $userAgent);
|
||||
|
||||
return ['outcome' => LoginAttempt::OUTCOME_INVALID_CREDENTIALS, 'user' => $user, 'locked_until' => null];
|
||||
}
|
||||
}
|
||||
|
||||
$user->forceFill([
|
||||
'failed_login_attempts' => 0,
|
||||
'last_failed_login_at' => null,
|
||||
|
||||
@@ -18,6 +18,7 @@ class ScannerContextService
|
||||
->firstOrFail();
|
||||
|
||||
$user->setRelation('tenant', $tenant);
|
||||
$user->load('event');
|
||||
|
||||
if ($tenant->requiresScannerCategoryValidation()) {
|
||||
$categories = $user->scanCategories()
|
||||
|
||||
@@ -11,6 +11,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
#[Fillable([
|
||||
'tenant_code',
|
||||
'event_id',
|
||||
'scanner_user_id',
|
||||
'ticket_id',
|
||||
'data',
|
||||
@@ -43,6 +44,7 @@ class ScanAttempt extends Model
|
||||
{
|
||||
return [
|
||||
'scanner_user_id' => 'integer',
|
||||
'event_id' => 'integer',
|
||||
'ticket_id' => 'integer',
|
||||
'result' => ScanAttemptResult::class,
|
||||
'created_at' => 'datetime',
|
||||
|
||||
@@ -32,7 +32,7 @@ class ScanAttemptResource extends JsonResource
|
||||
ScanAttemptResult::Processing => 'Error',
|
||||
ScanAttemptResult::Accepted => 'Verificado',
|
||||
ScanAttemptResult::InvalidQr => 'QR no pertenece al evento',
|
||||
ScanAttemptResult::TicketNotFound => 'Error',
|
||||
ScanAttemptResult::TicketNotFound => 'QR no pertenece al evento',
|
||||
ScanAttemptResult::CategoryForbidden => 'Error',
|
||||
ScanAttemptResult::AlreadyScanned => 'Usado',
|
||||
ScanAttemptResult::Expired => 'Vencido',
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Domains\Ticketing\Ticket\Services;
|
||||
|
||||
use App\Domains\Core\Auth\Models\User;
|
||||
use App\Domains\Core\Auth\Services\EventScopeService;
|
||||
use App\Domains\Ticketing\Ticket\Enums\ScanAttemptResult;
|
||||
use App\Domains\Ticketing\Ticket\Models\ScanAttempt;
|
||||
use App\Domains\Ticketing\Ticket\Models\Ticket;
|
||||
@@ -27,6 +28,7 @@ class ScannerTicketService
|
||||
->with('ticket.sourceCatalogItem.category')
|
||||
->where('tenant_code', $scanner->tenant_codigo)
|
||||
->where('scanner_user_id', $scanner->getKey())
|
||||
->when($this->eventId($scanner) !== null, fn (Builder $query) => $query->where('event_id', $this->eventId($scanner)))
|
||||
->when($search !== '', function (Builder $query) use ($search): void {
|
||||
$attemptedAtDate = $this->parseSearchDate($search);
|
||||
|
||||
@@ -60,6 +62,7 @@ class ScannerTicketService
|
||||
->with('ticket.sourceCatalogItem.category')
|
||||
->where('tenant_code', $scanner->tenant_codigo)
|
||||
->where('scanner_user_id', $scanner->getKey())
|
||||
->when($this->eventId($scanner) !== null, fn (Builder $query) => $query->where('event_id', $this->eventId($scanner)))
|
||||
->when($search !== '', function (Builder $query) use ($search): void {
|
||||
$attemptedAtDate = $this->parseSearchDate($search);
|
||||
$attemptedAtDayMonth = $this->parseSearchDayMonth($search);
|
||||
@@ -106,6 +109,7 @@ class ScannerTicketService
|
||||
->with('ticket')
|
||||
->where('tenant_code', $scanner->tenant_codigo)
|
||||
->where('scanner_user_id', $scanner->getKey())
|
||||
->when($this->eventId($scanner) !== null, fn (Builder $query) => $query->where('event_id', $this->eventId($scanner)))
|
||||
->findOrFail($scanAttemptId);
|
||||
|
||||
$scanAttempt->ticket?->loadMissing($this->relations());
|
||||
@@ -113,6 +117,11 @@ class ScannerTicketService
|
||||
return $scanAttempt;
|
||||
}
|
||||
|
||||
private function eventId(User $scanner): ?int
|
||||
{
|
||||
return app(EventScopeService::class)->eventId($scanner);
|
||||
}
|
||||
|
||||
private function parseSearchDate(string $search): ?string
|
||||
{
|
||||
if (preg_match('/^(\d{4})-(\d{2})-(\d{2})$/', $search, $matches) === 1) {
|
||||
@@ -154,7 +163,8 @@ class ScannerTicketService
|
||||
{
|
||||
$query = $this->baseQuery()
|
||||
->where('tenant_code', $scanner->tenant_codigo)
|
||||
->where('ticket', $ticketUuid);
|
||||
->where('ticket', $ticketUuid)
|
||||
->when($this->eventId($scanner) !== null, fn (Builder $query) => $query->where('event_id', $this->eventId($scanner)));
|
||||
|
||||
if ($this->requiresCategoryValidation($scanner)) {
|
||||
$categoryIds = $this->scannerCategoryIds($scanner);
|
||||
@@ -178,6 +188,7 @@ class ScannerTicketService
|
||||
$scanAttempt = ScanAttempt::query()->create([
|
||||
'tenant_code' => $scanner->tenant_codigo,
|
||||
'scanner_user_id' => $scanner->getKey(),
|
||||
'event_id' => $this->eventId($scanner),
|
||||
'data' => $this->serializeScannedData($scannedData),
|
||||
'result' => ScanAttemptResult::Processing,
|
||||
]);
|
||||
@@ -200,6 +211,7 @@ class ScannerTicketService
|
||||
$ticket = $this->baseQuery()
|
||||
->where('tenant_code', $scanner->tenant_codigo)
|
||||
->where('ticket', $scannedData)
|
||||
->when($this->eventId($scanner) !== null, fn (Builder $query) => $query->where('event_id', $this->eventId($scanner)))
|
||||
->lockForUpdate()
|
||||
->firstOrFail();
|
||||
$ticketId = (int) $ticket->getKey();
|
||||
|
||||
Reference in New Issue
Block a user