fix: ( bookings ) require explicit tickets:generate to issue reservations

This commit is contained in:
Abubeker Yasin
2026-07-28 12:18:25 +03:00
parent 0d46f3715e
commit ed09bfd8b6
7 changed files with 79 additions and 12 deletions

View File

@@ -11,4 +11,18 @@ export const PassengerStaff = (permission: string | string[]) =>
),
);
/**
* Like {@link PassengerStaff} but the permission must be explicitly granted —
* super admins / org admins get no automatic bypass.
*/
export const PassengerStaffStrict = (permission: string | string[]) =>
applyDecorators(
UseGuards(
JwtGuard,
PassengerPermissionGuard(Array.isArray(permission) ? permission : [permission], {
strict: true,
}),
),
);
export const PassengerAdmin = () => PassengerStaff(PASSENGER_PERMS.admin);

View File

@@ -6,9 +6,25 @@ import {
Type,
UnauthorizedException,
} from '@nestjs/common';
import { hasPassengerPermission } from './passenger-permission.util';
import {
hasPassengerPermission,
hasPassengerPermissionStrict,
} from './passenger-permission.util';
export type PassengerPermissionGuardOptions = {
/**
* When true, super admins and org admins do NOT bypass the check — the
* permission key must be explicitly granted to them like anyone else.
*/
strict?: boolean;
};
export function PassengerPermissionGuard(
permissions: string[],
options: PassengerPermissionGuardOptions = {},
): Type<CanActivate> {
const check = options.strict ? hasPassengerPermissionStrict : hasPassengerPermission;
export function PassengerPermissionGuard(permissions: string[]): Type<CanActivate> {
@Injectable()
class PassengerPermissionsGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
@@ -18,10 +34,11 @@ export function PassengerPermissionGuard(permissions: string[]): Type<CanActivat
if (!permissions?.length) return true;
if (!user) throw new UnauthorizedException('Authentication required');
if (permissions.some((p) => hasPassengerPermission(user, p))) return true;
if (permissions.some((p) => check(user, p))) return true;
throw new ForbiddenException(
`Missing permission. Required one of: ${permissions.join(', ')}`,
`Missing permission. Required one of: ${permissions.join(', ')}` +
(options.strict ? ' (granted explicitly — admin role does not bypass)' : ''),
);
}
}

View File

@@ -65,6 +65,20 @@ export function hasPassengerPermission(
return collectPermissionKeys(user).includes(permissionKey);
}
/**
* Same check as {@link hasPassengerPermission} but WITHOUT the super-admin /
* org-admin bypass — the permission key must be explicitly granted, whether via
* a role or an employee position. Use for actions that must stay auditable to a
* deliberate grant (e.g. ticket generation, which can waive a fare).
*/
export function hasPassengerPermissionStrict(
user: MeLikeUser | null | undefined,
permissionKey: string,
): boolean {
if (!user) return false;
return collectPermissionKeys(user).includes(permissionKey);
}
export function assertPassengerPermission(
user: MeLikeUser | null | undefined,
permissionKey: string,