diff --git a/services/nginx/app/objects/customer_default_driver_template_o.php b/services/nginx/app/objects/customer_default_driver_template_o.php new file mode 100644 index 00000000..4c206b9d --- /dev/null +++ b/services/nginx/app/objects/customer_default_driver_template_o.php @@ -0,0 +1,139 @@ + (int)$this->id, + 'customer_number' => (int)$this->customer_number->value(), + 'template_key' => (string)$this->template_key->value(), + 'created_at' => (string)$this->created_at->value(), + 'updated_at' => (string)$this->updated_at->value(), + ]; + } + + /** + * @throws Exception If the creation of the object fails + */ + public function add(int $customer_number, string $template_key): void + { + $normalized = (new subuser_permission_templates_service())->normalizeTemplateKey($template_key); + if ($normalized === null || $normalized === subuser_permission_templates_service::TEMPLATE_CUSTOM) { + throw new \InvalidArgumentException('Unknown driver access template.'); + } + $tmp_id = self::add_object([ + 'customer_number' => (int)$customer_number, + 'template_key' => $normalized, + ]); + self::select((int)$tmp_id); + self::objectChanged(); + } + + public function update(int $customer_number, string $template_key): void + { + $normalized = (new subuser_permission_templates_service())->normalizeTemplateKey($template_key); + if ($normalized === null || $normalized === subuser_permission_templates_service::TEMPLATE_CUSTOM) { + throw new \InvalidArgumentException('Unknown driver access template.'); + } + if (!$this->doesUserHaveDefaultTemplate($customer_number)) { + $this->add($customer_number, $normalized); + return; + } + $this->selectByCustomerNumber($customer_number); + self::update_object((int)$this->id, [ + 'template_key' => $normalized, + ]); + self::select((int)$this->id); + self::objectChanged(); + } + + public function objectChanged(): void + { + // No cache layer for this object. + } + + public function structure(): void + { + $this->setTable('customer_default_driver_template'); + } + + public function getObjectProperties(): void + { + $this->customer_number = new object_property($this->table, $this->id, 'customer_number', 'int', true); + $this->template_key = new object_property($this->table, $this->id, 'template_key', 'string', true); + $this->created_at = new object_property($this->table, $this->id, 'created_at', 'timestamp', true); + $this->updated_at = new object_property($this->table, $this->id, 'updated_at', 'timestamp', true); + } + + public function getDefaultTemplate(int $customer_number): ?string + { + if (!$this->doesUserHaveDefaultTemplate($customer_number)) { + return null; + } + $this->selectByCustomerNumber($customer_number); + if ($this->exists()) { + return (string)$this->template_key->value(); + } + return null; + } + + public function doesUserHaveDefaultTemplate(int $customer_number): bool + { + return $this->getIdByCustomerNumber($customer_number) !== null; + } + + public function getIdByCustomerNumber(int $customer_number): ?int + { + $matches = self::getFieldsWhere([ + 'customer_number' => (int)$customer_number, + ], [ + 'id' + ]); + if (count($matches) > 0) { + return (int)$matches[0]['id']; + } + return null; + } + + public function selectByCustomerNumber(int $customer_number): ?customer_default_driver_template_o + { + $id = $this->getIdByCustomerNumber($customer_number); + if ($id === null) { + return null; + } + $this->select($id); + if ($this->exists()) { + return $this; + } + throw new Exception('Unable to select customer default driver template for customer number: ' . $customer_number); + } + + public function delete(): void + { + self::requireSelected(); + self::delete_object((int)$this->id); + self::objectChanged(); + } +} diff --git a/services/nginx/app/routes/customerDefaultDriverTemplateRoute.php b/services/nginx/app/routes/customerDefaultDriverTemplateRoute.php new file mode 100644 index 00000000..518fcf0f --- /dev/null +++ b/services/nginx/app/routes/customerDefaultDriverTemplateRoute.php @@ -0,0 +1,169 @@ +get('/customer/default-driver-template', function () { + global $response; + $this->requirePermission('get_customer_default_driver_template'); + $user = (new authentication())->get_user(); + $subuser = (new authentication())->get_subuser(); + $customer_number = $this->resolveCustomerNumberForRead($user, $subuser); + $templateKey = (new customer_default_driver_template_o())->getDefaultTemplate((int)$customer_number); + if ($templateKey === null) { + $response->success([ + 'customer_number' => (int)$customer_number, + 'template_key' => null, + ]); + return; + } + $response->success([ + 'customer_number' => (int)$customer_number, + 'template_key' => $templateKey, + ]); + }, [ + 'get_customer_default_driver_template' => 'Get the default driver access template for a customer (own or, with extended permission, other customers).', + ]); + + $this->put('/customer/default-driver-template', function () { + global $response; + $this->requirePermission('edit_customer_default_driver_template'); + $user = (new authentication())->get_user(); + $subuser = (new authentication())->get_subuser(); + $customer_number = $this->resolveCustomerNumberForWrite($user, $subuser); + + self::requireParameters(['template_key']); + $templateKey = (string)self::getParameter('template_key'); + self::requireType($templateKey, self::type_string()); + self::requireMinLength('template_key', 1); + self::requireMaxLength('template_key', 64); + + $service = new subuser_permission_templates_service(); + $normalized = $service->normalizeTemplateKey($templateKey); + if ($normalized === null || $normalized === subuser_permission_templates_service::TEMPLATE_CUSTOM) { + $response->error('Unknown driver access template.', 400); + } + + $customer = (new users_o())->getUserByCustomerNumber((int)$customer_number); + if (!$customer->exists()) { + $response->error('Customer not found', 400); + } + + $templateObject = new customer_default_driver_template_o(); + $templateObject->update((int)$customer_number, $normalized); + + $actor_id = $user !== false ? (int)$user->id : ($subuser !== false ? (int)$subuser->id : 0); + (new logs_o())->add('customer_default_driver_template', 'global', 1, $actor_id, 'UPDATE_CUSTOMER_DEFAULT_DRIVER_TEMPLATE', 'Default driver template set to ' . $normalized); + + $response->success($templateObject->asArray()); + }, [ + 'edit_customer_default_driver_template' => 'Edit the default driver access template for a customer (own or, with extended permission, other customers).', + ]); + + $this->delete('/customer/default-driver-template', function () { + global $response; + $this->requirePermission('delete_customer_default_driver_template'); + $user = (new authentication())->get_user(); + $subuser = (new authentication())->get_subuser(); + $customer_number = $this->resolveCustomerNumberForWrite($user, $subuser); + + $templateObject = new customer_default_driver_template_o(); + if (!$templateObject->doesUserHaveDefaultTemplate((int)$customer_number)) { + $response->error('This customer does not have a default driver template', 400); + } + $templateObject->selectByCustomerNumber((int)$customer_number); + $templateObject->delete(); + + $actor_id = $user !== false ? (int)$user->id : ($subuser !== false ? (int)$subuser->id : 0); + (new logs_o())->add('customer_default_driver_template', 'global', 1, $actor_id, 'DELETE_CUSTOMER_DEFAULT_DRIVER_TEMPLATE', 'Default driver template removed'); + + $response->success('Default driver template deleted'); + }, [ + 'delete_customer_default_driver_template' => 'Delete the default driver access template for a customer (own or, with extended permission, other customers).', + ]); + } + + /** + * @param users_o|false $user + * @param \objects\subusers_o|false $subuser + */ + private function resolveCustomerNumberForRead($user, $subuser): int + { + global $response; + $customer_number = $this->customerNumberFromRequest(); + if ($subuser !== false) { + $subuserCustomer = (int)$subuser->customer_number_target->value(); + if ($subuserCustomer <= 0 || $subuserCustomer !== $customer_number) { + $response->error('Unauthorized', 401); + } + return $customer_number; + } + if ($user === false) { + $response->error('Invalid session', 400); + } + if ((int)$user->customer_number->value() !== $customer_number) { + $this->requirePermission('get_customer_default_driver_template_other'); + } + return $customer_number; + } + + /** + * @param users_o|false $user + * @param \objects\subusers_o|false $subuser + */ + private function resolveCustomerNumberForWrite($user, $subuser): int + { + global $response; + $customer_number = $this->customerNumberFromRequest(); + if ($subuser !== false) { + $subuserCustomer = (int)$subuser->customer_number_target->value(); + if ($subuserCustomer <= 0 || $subuserCustomer !== $customer_number) { + $response->error('Unauthorized', 401); + } + return $customer_number; + } + if ($user === false) { + $response->error('Invalid session', 400); + } + if ((int)$user->customer_number->value() !== $customer_number) { + $this->requirePermission('edit_customer_default_driver_template_other'); + } + return $customer_number; + } + + private function customerNumberFromRequest(): int + { + global $response; + if (self::isParametersSet(['customer_number'])) { + self::requireParameters(['customer_number']); + $customer_number = (int)self::getParameter('customer_number'); + } else { + $auth = new authentication(); + $user = $auth->get_user(); + $subuser = $auth->get_subuser(); + if ($user !== false) { + $customer_number = (int)$user->customer_number->value(); + } elseif ($subuser !== false) { + $customer_number = (int)$subuser->customer_number_target->value(); + } else { + $response->error('Invalid session', 400); + } + } + if ($customer_number <= 0) { + $response->error('Invalid customer number', 400); + } + return $customer_number; + } +} diff --git a/services/nginx/app/tests/customer/CustomerDefaultDriverTemplateRouteTest.php b/services/nginx/app/tests/customer/CustomerDefaultDriverTemplateRouteTest.php new file mode 100644 index 00000000..bbd63519 --- /dev/null +++ b/services/nginx/app/tests/customer/CustomerDefaultDriverTemplateRouteTest.php @@ -0,0 +1,71 @@ +