Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
90.00% |
207 / 230 |
|
63.16% |
24 / 38 |
CRAP | |
0.00% |
0 / 1 |
| AbstractIdentifyMethod | |
90.00% |
207 / 230 |
|
63.16% |
24 / 38 |
87.56 | |
0.00% |
0 / 1 |
| __construct | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
| getId | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
| getName | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| getFriendlyName | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| setFriendlyName | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| setCodeSentByUser | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| cleanEntity | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
| setEntity | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| getEntity | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| signatureMethodsToArray | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| getAvailableSignatureMethods | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| getEmptyInstanceOfSignatureMethodByName | |
75.00% |
6 / 8 |
|
0.00% |
0 / 1 |
3.14 | |||
| getSignatureMethods | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| getSettings | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
| getDefaultSettings | |
100.00% |
13 / 13 |
|
100.00% |
1 / 1 |
3 | |||
| notify | |
100.00% |
11 / 11 |
|
100.00% |
1 / 1 |
2 | |||
| willNotifyUser | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| validateToRequest | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| validateToCreateAccount | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| validateToIdentify | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| validateToSign | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| throwIfFileNotFound | |
81.25% |
26 / 32 |
|
0.00% |
0 / 1 |
6.24 | |||
| throwIfMaximumValidityExpired | |
100.00% |
12 / 12 |
|
100.00% |
1 / 1 |
3 | |||
| throwIfInvalidToken | |
85.71% |
6 / 7 |
|
0.00% |
0 / 1 |
6.10 | |||
| renewSession | |
80.00% |
4 / 5 |
|
0.00% |
0 / 1 |
2.03 | |||
| updateIdentifiedAt | |
83.33% |
5 / 6 |
|
0.00% |
0 / 1 |
3.04 | |||
| throwIfRenewalIntervalExpired | |
94.44% |
34 / 36 |
|
0.00% |
0 / 1 |
5.00 | |||
| getRenewAction | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
| getRuntimeConfigInt | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
| throwIfAlreadySigned | |
100.00% |
12 / 12 |
|
100.00% |
1 / 1 |
3 | |||
| getSettingsFromDatabase | |
89.47% |
17 / 19 |
|
0.00% |
0 / 1 |
3.01 | |||
| overrideImmutable | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| loadSavedSettings | |
95.24% |
20 / 21 |
|
0.00% |
0 / 1 |
10 | |||
| applyDefault | |
87.50% |
7 / 8 |
|
0.00% |
0 / 1 |
5.05 | |||
| save | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
| delete | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| removeKeysThatDontExists | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
| validateToRenew | |
33.33% |
1 / 3 |
|
0.00% |
0 / 1 |
1.30 | |||
| 1 | <?php |
| 2 | |
| 3 | declare(strict_types=1); |
| 4 | /** |
| 5 | * SPDX-FileCopyrightText: 2020-2024 LibreCode coop and contributors |
| 6 | * SPDX-License-Identifier: AGPL-3.0-or-later |
| 7 | */ |
| 8 | |
| 9 | namespace OCA\Libresign\Service\IdentifyMethod; |
| 10 | |
| 11 | use DateTime; |
| 12 | use InvalidArgumentException; |
| 13 | use OC\AppFramework\Http as AppFrameworkHttp; |
| 14 | use OCA\Libresign\Db\IdentifyMethod; |
| 15 | use OCA\Libresign\Enum\FileStatus; |
| 16 | use OCA\Libresign\Enum\IdentifyMethodRequirement; |
| 17 | use OCA\Libresign\Events\SendSignNotificationEvent; |
| 18 | use OCA\Libresign\Exception\LibresignException; |
| 19 | use OCA\Libresign\Helper\JSActions; |
| 20 | use OCA\Libresign\Service\IdentifyMethod\SignatureMethod\AbstractSignatureMethod; |
| 21 | use OCA\Libresign\Service\Policy\Provider\ExpirationRules\ExpirationRulesPolicy; |
| 22 | use OCA\Libresign\Service\SessionService; |
| 23 | use OCA\Libresign\Vendor\Wobeto\EmailBlur\Blur; |
| 24 | use OCP\Files\NotFoundException; |
| 25 | use OCP\IUser; |
| 26 | |
| 27 | abstract class AbstractIdentifyMethod implements IIdentifyMethod { |
| 28 | /** |
| 29 | * Semantic error code of the invalid verification code error: the closest |
| 30 | * HTTP status with one extra digit (401 -> 4010), so it is not mistaken |
| 31 | * for an HTTP status code. |
| 32 | */ |
| 33 | public const int CODE_INVALID_TOKEN = 4010; |
| 34 | |
| 35 | protected IdentifyMethod $entity; |
| 36 | protected string $name; |
| 37 | protected string $friendlyName; |
| 38 | protected ?IUser $user = null; |
| 39 | protected string $codeSentByUser = ''; |
| 40 | protected array $settings = []; |
| 41 | protected bool $willNotify = true; |
| 42 | /** |
| 43 | * @var string[] |
| 44 | */ |
| 45 | public array $availableSignatureMethods = []; |
| 46 | protected string $defaultSignatureMethod = ''; |
| 47 | /** |
| 48 | * @var AbstractSignatureMethod[] |
| 49 | */ |
| 50 | protected array $signatureMethods = []; |
| 51 | public function __construct( |
| 52 | protected IdentifyService $identifyService, |
| 53 | ) { |
| 54 | $className = (new \ReflectionClass($this))->getShortName(); |
| 55 | $this->name = lcfirst($className); |
| 56 | $this->cleanEntity(); |
| 57 | } |
| 58 | |
| 59 | #[\Override] |
| 60 | public static function getId(): string { |
| 61 | $id = lcfirst(substr(strrchr(static::class, '\\'), 1)); |
| 62 | return $id; |
| 63 | } |
| 64 | |
| 65 | #[\Override] |
| 66 | public function getName(): string { |
| 67 | return $this->name; |
| 68 | } |
| 69 | |
| 70 | #[\Override] |
| 71 | public function getFriendlyName(): string { |
| 72 | return $this->friendlyName; |
| 73 | } |
| 74 | |
| 75 | #[\Override] |
| 76 | public function setFriendlyName(string $friendlyName): void { |
| 77 | $this->friendlyName = $friendlyName; |
| 78 | } |
| 79 | |
| 80 | #[\Override] |
| 81 | public function setCodeSentByUser(string $code): void { |
| 82 | $this->codeSentByUser = $code; |
| 83 | } |
| 84 | |
| 85 | #[\Override] |
| 86 | public function cleanEntity(): void { |
| 87 | $this->entity = new IdentifyMethod(); |
| 88 | $this->entity->setIdentifierKey($this->name); |
| 89 | } |
| 90 | |
| 91 | #[\Override] |
| 92 | public function setEntity(IdentifyMethod $entity): void { |
| 93 | $this->entity = $entity; |
| 94 | } |
| 95 | |
| 96 | #[\Override] |
| 97 | public function getEntity(): IdentifyMethod { |
| 98 | return $this->entity; |
| 99 | } |
| 100 | |
| 101 | #[\Override] |
| 102 | public function signatureMethodsToArray(): array { |
| 103 | return array_map(fn (AbstractSignatureMethod $method) => $method->toArray(), $this->signatureMethods); |
| 104 | } |
| 105 | |
| 106 | public function getAvailableSignatureMethods(): array { |
| 107 | return $this->availableSignatureMethods; |
| 108 | } |
| 109 | |
| 110 | #[\Override] |
| 111 | public function getEmptyInstanceOfSignatureMethodByName(string $name): AbstractSignatureMethod { |
| 112 | if (!in_array($name, $this->getAvailableSignatureMethods())) { |
| 113 | throw new InvalidArgumentException(sprintf('%s is not a valid signature method of identify method %s', $name, $this->getName())); |
| 114 | } |
| 115 | $className = 'OCA\Libresign\Service\IdentifyMethod\\SignatureMethod\\' . ucfirst($name); |
| 116 | if (!class_exists($className)) { |
| 117 | throw new InvalidArgumentException('Invalid signature method. Set at identify method the list of available signature methdos with right values.'); |
| 118 | } |
| 119 | /** @var AbstractSignatureMethod */ |
| 120 | $signatureMethod = clone \OCP\Server::get($className); |
| 121 | $signatureMethod->cleanEntity(); |
| 122 | return $signatureMethod; |
| 123 | } |
| 124 | |
| 125 | /** |
| 126 | * @return AbstractSignatureMethod[] |
| 127 | */ |
| 128 | #[\Override] |
| 129 | public function getSignatureMethods(): array { |
| 130 | return $this->signatureMethods; |
| 131 | } |
| 132 | |
| 133 | #[\Override] |
| 134 | public function getSettings(): array { |
| 135 | $this->getSettingsFromDatabase(); |
| 136 | return $this->settings; |
| 137 | } |
| 138 | |
| 139 | #[\Override] |
| 140 | public function getDefaultSettings(): array { |
| 141 | $this->signatureMethods = []; |
| 142 | foreach ($this->availableSignatureMethods as $signatureMethodName) { |
| 143 | $signatureMethod = $this->getEmptyInstanceOfSignatureMethodByName($signatureMethodName); |
| 144 | if ($signatureMethodName === $this->defaultSignatureMethod) { |
| 145 | $signatureMethod->enable(); |
| 146 | } |
| 147 | $this->signatureMethods[$signatureMethodName] = $signatureMethod; |
| 148 | } |
| 149 | |
| 150 | return [ |
| 151 | 'name' => $this->name, |
| 152 | 'friendly_name' => $this->getFriendlyName(), |
| 153 | 'enabled' => true, |
| 154 | 'requirement' => IdentifyMethodRequirement::REQUIRED->value, |
| 155 | 'signatureMethods' => $this->signatureMethodsToArray(), |
| 156 | ]; |
| 157 | } |
| 158 | |
| 159 | #[\Override] |
| 160 | public function notify(?string $deliveryEventId = null): bool { |
| 161 | if (!$this->willNotify) { |
| 162 | return false; |
| 163 | } |
| 164 | $signRequest = $this->identifyService->getSignRequestMapper()->getById($this->getEntity()->getSignRequestId()); |
| 165 | $libresignFile = $this->identifyService->getFileMapper()->getById($signRequest->getFileId()); |
| 166 | $this->identifyService->getEventDispatcher()->dispatchTyped(new SendSignNotificationEvent( |
| 167 | $signRequest, |
| 168 | $libresignFile, |
| 169 | $this, |
| 170 | $deliveryEventId, |
| 171 | )); |
| 172 | return true; |
| 173 | } |
| 174 | |
| 175 | #[\Override] |
| 176 | public function willNotifyUser(bool $willNotify): void { |
| 177 | $this->willNotify = $willNotify; |
| 178 | } |
| 179 | |
| 180 | #[\Override] |
| 181 | public function validateToRequest(): void { |
| 182 | } |
| 183 | |
| 184 | #[\Override] |
| 185 | public function validateToCreateAccount(string $value): void { |
| 186 | } |
| 187 | |
| 188 | #[\Override] |
| 189 | public function validateToIdentify(): void { |
| 190 | } |
| 191 | |
| 192 | #[\Override] |
| 193 | public function validateToSign(): void { |
| 194 | } |
| 195 | |
| 196 | protected function throwIfFileNotFound(): void { |
| 197 | $signRequest = $this->identifyService->getSignRequestMapper()->getById($this->getEntity()->getSignRequestId()); |
| 198 | $fileEntity = $this->identifyService->getFileMapper()->getById($signRequest->getFileId()); |
| 199 | |
| 200 | $filesToCheck = []; |
| 201 | |
| 202 | if ($fileEntity->getNodeType() === 'envelope') { |
| 203 | $children = $this->identifyService->getFileMapper()->getChildrenFiles($fileEntity->getId()); |
| 204 | foreach ($children as $child) { |
| 205 | $filesToCheck[] = [ |
| 206 | 'uuid' => $child->getUuid(), |
| 207 | 'nodeId' => $child->getNodeId(), |
| 208 | ]; |
| 209 | } |
| 210 | } else { |
| 211 | $filesToCheck[] = [ |
| 212 | 'uuid' => $fileEntity->getUuid(), |
| 213 | 'nodeId' => $fileEntity->getNodeId(), |
| 214 | ]; |
| 215 | } |
| 216 | |
| 217 | foreach ($filesToCheck as $fileInfo) { |
| 218 | if (!is_int($fileInfo['nodeId'])) { |
| 219 | throw new LibresignException(json_encode([ |
| 220 | 'action' => JSActions::ACTION_DO_NOTHING, |
| 221 | // TRANSLATORS Error shown during signer identification when the related document cannot be found. |
| 222 | 'errors' => [['message' => $this->identifyService->getL10n()->t('File not found')]], |
| 223 | ]), AppFrameworkHttp::STATUS_NOT_FOUND); |
| 224 | } |
| 225 | |
| 226 | $storageUserId = $this->identifyService->getFileMapper() |
| 227 | ->getStorageUserIdByUuid($fileInfo['uuid']); |
| 228 | $folderService = $this->identifyService->getFolderService(); |
| 229 | $previousUserId = $folderService->getUserId(); |
| 230 | $folderService->setUserId($storageUserId); |
| 231 | try { |
| 232 | $folderService->getFileByNodeId($fileInfo['nodeId']); |
| 233 | } catch (NotFoundException) { |
| 234 | throw new LibresignException(json_encode([ |
| 235 | 'action' => JSActions::ACTION_DO_NOTHING, |
| 236 | // TRANSLATORS Error shown during signer identification when the related document cannot be found. |
| 237 | 'errors' => [['message' => $this->identifyService->getL10n()->t('File not found')]], |
| 238 | ]), AppFrameworkHttp::STATUS_NOT_FOUND); |
| 239 | } finally { |
| 240 | $folderService->setUserId($previousUserId); |
| 241 | } |
| 242 | } |
| 243 | } |
| 244 | |
| 245 | protected function throwIfMaximumValidityExpired(): void { |
| 246 | $maximumValidity = $this->getRuntimeConfigInt(ExpirationRulesPolicy::KEY_MAXIMUM_VALIDITY, SessionService::NO_MAXIMUM_VALIDITY); |
| 247 | if ($maximumValidity <= 0) { |
| 248 | return; |
| 249 | } |
| 250 | $signRequest = $this->identifyService->getSignRequestMapper()->getById($this->getEntity()->getSignRequestId()); |
| 251 | $now = $this->identifyService->getTimeFactory()->getDateTime(); |
| 252 | $expirationDate = (clone $signRequest->getCreatedAt()) |
| 253 | ->add(new \DateInterval('PT' . $maximumValidity . 'S')); |
| 254 | if ($expirationDate < $now) { |
| 255 | throw new LibresignException(json_encode([ |
| 256 | 'action' => JSActions::ACTION_DO_NOTHING, |
| 257 | // TRANSLATORS Error shown when the signing request has reached its maximum validity and can no longer be renewed. |
| 258 | 'errors' => [['message' => $this->identifyService->getL10n()->t('This signing request has expired and can no longer be used.')]], |
| 259 | ])); |
| 260 | } |
| 261 | } |
| 262 | |
| 263 | protected function throwIfInvalidToken(): void { |
| 264 | $code = $this->getEntity()->getCode(); |
| 265 | if ($code === null || $code === '') { |
| 266 | if ($this->codeSentByUser !== null) { |
| 267 | // TRANSLATORS Error shown when the verification code entered by the signer is incorrect. |
| 268 | throw new LibresignException($this->identifyService->getL10n()->t('Invalid code.'), self::CODE_INVALID_TOKEN); |
| 269 | } |
| 270 | return; |
| 271 | } |
| 272 | if (empty($this->codeSentByUser) || !$this->identifyService->getHasher()->verify($this->codeSentByUser, $code)) { |
| 273 | // TRANSLATORS Error shown when the verification code entered by the signer is incorrect. |
| 274 | throw new LibresignException($this->identifyService->getL10n()->t('Invalid code.'), self::CODE_INVALID_TOKEN); |
| 275 | } |
| 276 | } |
| 277 | |
| 278 | protected function renewSession(): void { |
| 279 | $this->identifyService->getSessionService()->setIdentifyMethodId($this->getEntity()->getId()); |
| 280 | $renewalInterval = $this->getRuntimeConfigInt(ExpirationRulesPolicy::KEY_RENEWAL_INTERVAL, SessionService::NO_RENEWAL_INTERVAL); |
| 281 | if ($renewalInterval <= 0) { |
| 282 | return; |
| 283 | } |
| 284 | $this->identifyService->getSessionService()->resetDurationOfSignPage($renewalInterval); |
| 285 | } |
| 286 | |
| 287 | protected function updateIdentifiedAt(): void { |
| 288 | if ($this->getEntity()->getCode() && !$this->getEntity()->getIdentifiedAtDate()) { |
| 289 | return; |
| 290 | } |
| 291 | $this->getEntity()->setIdentifiedAtDate($this->identifyService->getTimeFactory()->getDateTime()); |
| 292 | $this->willNotify = false; |
| 293 | $this->identifyService->save($this->getEntity()); |
| 294 | $this->notify(); |
| 295 | } |
| 296 | |
| 297 | protected function throwIfRenewalIntervalExpired(): void { |
| 298 | $renewalInterval = $this->getRuntimeConfigInt(ExpirationRulesPolicy::KEY_RENEWAL_INTERVAL, SessionService::NO_RENEWAL_INTERVAL); |
| 299 | if ($renewalInterval <= 0) { |
| 300 | return; |
| 301 | } |
| 302 | $signRequest = $this->identifyService->getSignRequestMapper()->getById($this->getEntity()->getSignRequestId()); |
| 303 | $startTime = $this->identifyService->getSessionService()->getSignStartTime(); |
| 304 | if ($startTime > 0) { |
| 305 | $startTime = new DateTime('@' . $startTime, new \DateTimeZone('UTC')); |
| 306 | } else { |
| 307 | $startTime = null; |
| 308 | } |
| 309 | $createdAt = $signRequest->getCreatedAt(); |
| 310 | $lastAttempt = $this->getEntity()->getLastAttemptDate(); |
| 311 | $identifiedAt = $this->getEntity()->getIdentifiedAtDate(); |
| 312 | $lastActionDate = max( |
| 313 | $startTime, |
| 314 | $createdAt, |
| 315 | $lastAttempt, |
| 316 | $identifiedAt, |
| 317 | ); |
| 318 | $now = $this->identifyService->getTimeFactory()->getDateTime(); |
| 319 | $endRenewal = (clone $lastActionDate) |
| 320 | ->add(new \DateInterval('PT' . $renewalInterval . 'S')); |
| 321 | if ($endRenewal < $now) { |
| 322 | if ($this->getName() === 'email') { |
| 323 | $blur = new Blur($this->getEntity()->getIdentifierValue()); |
| 324 | throw new LibresignException(json_encode([ |
| 325 | 'action' => $this->getRenewAction(), |
| 326 | // TRANSLATORS title that is displayed at screen to notify the signer that the link to sign the document expired |
| 327 | 'title' => $this->identifyService->getL10n()->t('Link expired'), |
| 328 | 'body' => $this->identifyService->getL10n()->t( |
| 329 | "The link to sign the document has expired.\n" |
| 330 | . 'We will send a new link to the email %1$s.' . "\n" |
| 331 | . 'Click below to receive the new link and be able to sign the document.', |
| 332 | [$blur->make()] |
| 333 | ), |
| 334 | 'uuid' => $signRequest->getUuid(), |
| 335 | // TRANSLATORS Button to renew the link to sign the document. Renew is the action to generate a new sign link when the link expired. |
| 336 | 'renewButton' => $this->identifyService->getL10n()->t('Renew'), |
| 337 | ])); |
| 338 | } |
| 339 | $this->validateToRenew($this->user); |
| 340 | } |
| 341 | } |
| 342 | |
| 343 | private function getRenewAction(): int { |
| 344 | return match ($this->name) { |
| 345 | 'email' => JSActions::ACTION_RENEW_EMAIL, |
| 346 | default => throw new InvalidArgumentException('Invalid identify method name'), |
| 347 | }; |
| 348 | } |
| 349 | |
| 350 | private function getRuntimeConfigInt(string $key, int $default): int { |
| 351 | $resolved = $this->identifyService->getPolicyService()->resolve($key); |
| 352 | $value = $resolved->getEffectiveValue(); |
| 353 | return $value !== null ? (int)$value : $default; |
| 354 | } |
| 355 | |
| 356 | protected function throwIfAlreadySigned(): void { |
| 357 | $signRequest = $this->identifyService->getSignRequestMapper()->getById($this->getEntity()->getSignRequestId()); |
| 358 | $fileEntity = $this->identifyService->getFileMapper()->getById($signRequest->getFileId()); |
| 359 | if ($fileEntity->getStatus() === FileStatus::SIGNED->value |
| 360 | || $signRequest->getSigned() |
| 361 | ) { |
| 362 | throw new LibresignException(json_encode([ |
| 363 | 'action' => JSActions::ACTION_REDIRECT, |
| 364 | // TRANSLATORS Error shown when the signer tries to sign a document that is already signed. |
| 365 | 'errors' => [['message' => $this->identifyService->getL10n()->t('File already signed.')]], |
| 366 | 'redirect' => $this->identifyService->getUrlGenerator()->linkToRoute( |
| 367 | 'libresign.page.validationFilePublic', |
| 368 | ['uuid' => $signRequest->getUuid()] |
| 369 | ), |
| 370 | ])); |
| 371 | } |
| 372 | } |
| 373 | |
| 374 | protected function getSettingsFromDatabase(array $default = [], array $immutable = []): array { |
| 375 | if ($this->settings) { |
| 376 | return $this->settings; |
| 377 | } |
| 378 | $this->loadSavedSettings(); |
| 379 | $default = array_merge( |
| 380 | [ |
| 381 | 'name' => $this->name, |
| 382 | 'friendly_name' => $this->getFriendlyName(), |
| 383 | 'enabled' => true, |
| 384 | 'requirement' => IdentifyMethodRequirement::REQUIRED->value, |
| 385 | 'signatureMethods' => $this->signatureMethodsToArray(), |
| 386 | ], |
| 387 | $default |
| 388 | ); |
| 389 | $this->removeKeysThatDontExists($default); |
| 390 | $this->overrideImmutable($immutable); |
| 391 | $this->settings = $this->applyDefault($this->settings, $default); |
| 392 | if (!isset($this->settings['requirement'])) { |
| 393 | $this->settings['requirement'] = IdentifyMethodRequirement::REQUIRED->value; |
| 394 | } |
| 395 | return $this->settings; |
| 396 | } |
| 397 | |
| 398 | private function overrideImmutable(array $immutable): void { |
| 399 | $this->settings = array_merge($this->settings, $immutable); |
| 400 | } |
| 401 | |
| 402 | private function loadSavedSettings(): void { |
| 403 | $config = $this->identifyService->getSavedSettings(); |
| 404 | $this->settings = array_reduce($config, function ($carry, $config) { |
| 405 | if ($config['name'] === $this->name) { |
| 406 | return $config; |
| 407 | } |
| 408 | return $carry; |
| 409 | }, []); |
| 410 | $enabled = false; |
| 411 | $availableSignatureMethods = $this->getAvailableSignatureMethods(); |
| 412 | foreach ($availableSignatureMethods as $signatureMethodName) { |
| 413 | $this->signatureMethods[$signatureMethodName] |
| 414 | = $this->getEmptyInstanceOfSignatureMethodByName($signatureMethodName); |
| 415 | if (isset($this->settings['signatureMethods'][$signatureMethodName]['enabled']) |
| 416 | && $this->settings['signatureMethods'][$signatureMethodName]['enabled'] |
| 417 | ) { |
| 418 | $this->signatureMethods[$signatureMethodName]->enable(); |
| 419 | $enabled = true; |
| 420 | } |
| 421 | } |
| 422 | if (isset($this->settings['signatureMethods'])) { |
| 423 | foreach (array_keys($this->settings['signatureMethods']) as $signatureMethodName) { |
| 424 | if (!in_array($signatureMethodName, $availableSignatureMethods, true)) { |
| 425 | unset($this->settings['signatureMethods'][$signatureMethodName]); |
| 426 | } |
| 427 | } |
| 428 | } |
| 429 | if (!$enabled && $this->defaultSignatureMethod) { |
| 430 | $this->signatureMethods[$this->defaultSignatureMethod]->enable(); |
| 431 | } |
| 432 | } |
| 433 | |
| 434 | private function applyDefault(array $customConfig, array $default): array { |
| 435 | foreach ($default as $key => $value) { |
| 436 | if (!isset($customConfig[$key])) { |
| 437 | $customConfig[$key] = $value; |
| 438 | } elseif (gettype($value) !== gettype($customConfig[$key])) { |
| 439 | $customConfig[$key] = $value; |
| 440 | } elseif (gettype($value) === 'array') { |
| 441 | $customConfig[$key] = $this->applyDefault($customConfig[$key], $value); |
| 442 | } |
| 443 | } |
| 444 | return $customConfig; |
| 445 | } |
| 446 | |
| 447 | #[\Override] |
| 448 | public function save(?string $deliveryEventId = null): void { |
| 449 | $this->identifyService->save($this->getEntity()); |
| 450 | $this->notify($deliveryEventId); |
| 451 | } |
| 452 | |
| 453 | #[\Override] |
| 454 | public function delete(): void { |
| 455 | $this->identifyService->delete($this->getEntity()); |
| 456 | } |
| 457 | |
| 458 | private function removeKeysThatDontExists(array $default): void { |
| 459 | $diff = array_diff_key($this->settings, $default); |
| 460 | foreach (array_keys($diff) as $invalidKey) { |
| 461 | unset($this->settings[$invalidKey]); |
| 462 | } |
| 463 | } |
| 464 | |
| 465 | #[\Override] |
| 466 | public function validateToRenew(?IUser $user = null): void { |
| 467 | $this->throwIfMaximumValidityExpired(); |
| 468 | $this->throwIfAlreadySigned(); |
| 469 | $this->throwIfFileNotFound(); |
| 470 | } |
| 471 | } |