diff --git a/services/nginx/app/classes/subuser_permission_templates_service.php b/services/nginx/app/classes/subuser_permission_templates_service.php index 6abbe49d..12a84ff2 100644 --- a/services/nginx/app/classes/subuser_permission_templates_service.php +++ b/services/nginx/app/classes/subuser_permission_templates_service.php @@ -178,6 +178,23 @@ class subuser_permission_templates_service ]; } + /** + * @return array{enabled:bool,permissions:array}|null + */ + public function expandTemplateForWritePayload(?string $templateKey): ?array + { + $key = $this->normalizeTemplateKey($templateKey); + if ($key === self::TEMPLATE_CUSTOM) { + return null; + } + + if ($key === null) { + throw new \InvalidArgumentException('Unknown driver access template.'); + } + + return $this->expandTemplate($key); + } + public function normalizeTemplateKey(?string $templateKey): ?string { if ($templateKey === null) { diff --git a/services/nginx/app/routes/subusersRoute.php b/services/nginx/app/routes/subusersRoute.php index 651915f2..65c723c2 100644 --- a/services/nginx/app/routes/subusersRoute.php +++ b/services/nginx/app/routes/subusersRoute.php @@ -140,7 +140,7 @@ class subusersRoute $templateKey = (string)self::getParameter('permission_template_key'); try { - return (new subuser_permission_templates_service())->expandTemplate($templateKey); + return (new subuser_permission_templates_service())->expandTemplateForWritePayload($templateKey); } catch (\InvalidArgumentException $exception) { $response->error($exception->getMessage(), 400); } diff --git a/services/nginx/app/tests/Unit/Subusers/SubuserPermissionTemplatesTest.php b/services/nginx/app/tests/Unit/Subusers/SubuserPermissionTemplatesTest.php index e04960ce..4945c7f7 100644 --- a/services/nginx/app/tests/Unit/Subusers/SubuserPermissionTemplatesTest.php +++ b/services/nginx/app/tests/Unit/Subusers/SubuserPermissionTemplatesTest.php @@ -44,3 +44,25 @@ it('expands and classifies practical driver access templates', function (): void ->and($service->classify(['VEHICLES_LIST'], true)) ->toBe(subuser_permission_templates_service::TEMPLATE_CUSTOM); }); + +it('treats custom as a raw permissions marker in write payloads', function (): void { + $service = new subuser_permission_templates_service(); + $driver = $service->expandTemplateForWritePayload(subuser_permission_templates_service::TEMPLATE_DRIVER); + + expect($service->expandTemplateForWritePayload(subuser_permission_templates_service::TEMPLATE_CUSTOM)) + ->toBeNull() + ->and($driver['enabled']) + ->toBeTrue() + ->and($driver['permissions']) + ->toBe([ + 'VEHICLES_LIST', + 'SELFSERVE_LIST', + 'SELFSERVE_ADD', + 'BOOKINGS_LIST', + 'BOOKINGS_ADD', + 'ORDERS_LIST', + ]); + + expect(fn () => $service->expandTemplateForWritePayload('missing-template')) + ->toThrow(\InvalidArgumentException::class, 'Unknown driver access template.'); +}); diff --git a/services/nginx/app/tests/Unit/Subusers/SubusersRouteManagementContractTest.php b/services/nginx/app/tests/Unit/Subusers/SubusersRouteManagementContractTest.php index faa3fde1..fd4801ca 100644 --- a/services/nginx/app/tests/Unit/Subusers/SubusersRouteManagementContractTest.php +++ b/services/nginx/app/tests/Unit/Subusers/SubusersRouteManagementContractTest.php @@ -70,6 +70,16 @@ it('links grant disable operations to SUBUSERS_DELETE for own-customer managers' expect($normalized)->toContain("requireManagedCustomerScope(subusers_permission_node_key::SUBUSERS_DELETE, \$targetCustomer);"); }); +it('parses custom grant writes through raw permissions instead of template expansion', function (): void { + $routeFile = app_path('routes/subusersRoute.php'); + expect(is_file($routeFile))->toBeTrue(); + + $code = (string)file_get_contents($routeFile); + $normalized = preg_replace('/\s+/', ' ', $code); + + expect($normalized)->toContain('expandTemplateForWritePayload($templateKey)'); +}); + it('prevents own-customer managers from editing driver-owned account profiles', function (): void { $routeFile = app_path('routes/subusersRoute.php'); expect(is_file($routeFile))->toBeTrue();