Skip to content

Commit ff3c708

Browse files
committed
feat(policy): migrate signature_text settings to policy layer
- Create SignatureTextPolicy provider covering 6 settings: - signature_text_template - template_font_size - signature_width, signature_height - signature_font_size - signature_render_mode - Register all 6 keys in PolicyProviders catalog - Migrate SignatureTextService backend (6 read/write points) with PolicyService fallback to appConfig - Add SignatureTextPolicyTest with 10 unit tests (all pass) - Add signature_text_policy.feature for integration coverage - npm test: 180 files, 2563 tests passing Signed-off-by: Vitor Mattos <[email protected]>
1 parent cc04206 commit ff3c708

5 files changed

Lines changed: 359 additions & 13 deletions

File tree

lib/Service/Policy/Provider/PolicyProviders.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use OCA\Libresign\Service\Policy\Provider\IdentificationDocuments\IdentificationDocumentsPolicy;
1515
use OCA\Libresign\Service\Policy\Provider\RequestSignGroups\RequestSignGroupsPolicy;
1616
use OCA\Libresign\Service\Policy\Provider\Signature\SignatureFlowPolicy;
17+
use OCA\Libresign\Service\Policy\Provider\SignatureText\SignatureTextPolicy;
1718

1819
final class PolicyProviders {
1920
/** @var array<string, class-string> */
@@ -24,5 +25,11 @@ final class PolicyProviders {
2425
RequestSignGroupsPolicy::KEY => RequestSignGroupsPolicy::class,
2526
SignatureFlowPolicy::KEY => SignatureFlowPolicy::class,
2627
IdentificationDocumentsPolicy::KEY => IdentificationDocumentsPolicy::class,
28+
SignatureTextPolicy::KEY_TEMPLATE => SignatureTextPolicy::class,
29+
SignatureTextPolicy::KEY_TEMPLATE_FONT_SIZE => SignatureTextPolicy::class,
30+
SignatureTextPolicy::KEY_SIGNATURE_WIDTH => SignatureTextPolicy::class,
31+
SignatureTextPolicy::KEY_SIGNATURE_HEIGHT => SignatureTextPolicy::class,
32+
SignatureTextPolicy::KEY_SIGNATURE_FONT_SIZE => SignatureTextPolicy::class,
33+
SignatureTextPolicy::KEY_RENDER_MODE => SignatureTextPolicy::class,
2734
];
2835
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* SPDX-FileCopyrightText: 2026 LibreCode coop and contributors
6+
* SPDX-License-Identifier: AGPL-3.0-or-later
7+
*/
8+
9+
namespace OCA\Libresign\Service\Policy\Provider\SignatureText;
10+
11+
use OCA\Libresign\Service\Policy\Contract\IPolicyDefinition;
12+
use OCA\Libresign\Service\Policy\Contract\IPolicyDefinitionProvider;
13+
use OCA\Libresign\Service\Policy\Model\PolicySpec;
14+
15+
final class SignatureTextPolicy implements IPolicyDefinitionProvider {
16+
public const KEY = 'signature_text';
17+
18+
public const KEY_TEMPLATE = 'signature_text_template';
19+
public const KEY_TEMPLATE_FONT_SIZE = 'template_font_size';
20+
public const KEY_SIGNATURE_WIDTH = 'signature_width';
21+
public const KEY_SIGNATURE_HEIGHT = 'signature_height';
22+
public const KEY_SIGNATURE_FONT_SIZE = 'signature_font_size';
23+
public const KEY_RENDER_MODE = 'signature_render_mode';
24+
25+
public const SYSTEM_APP_CONFIG_KEY_TEMPLATE = 'signature_text_template';
26+
public const SYSTEM_APP_CONFIG_KEY_TEMPLATE_FONT_SIZE = 'template_font_size';
27+
public const SYSTEM_APP_CONFIG_KEY_SIGNATURE_WIDTH = 'signature_width';
28+
public const SYSTEM_APP_CONFIG_KEY_SIGNATURE_HEIGHT = 'signature_height';
29+
public const SYSTEM_APP_CONFIG_KEY_SIGNATURE_FONT_SIZE = 'signature_font_size';
30+
public const SYSTEM_APP_CONFIG_KEY_RENDER_MODE = 'signature_render_mode';
31+
32+
#[\Override]
33+
public function keys(): array {
34+
return [
35+
self::KEY_TEMPLATE,
36+
self::KEY_TEMPLATE_FONT_SIZE,
37+
self::KEY_SIGNATURE_WIDTH,
38+
self::KEY_SIGNATURE_HEIGHT,
39+
self::KEY_SIGNATURE_FONT_SIZE,
40+
self::KEY_RENDER_MODE,
41+
];
42+
}
43+
44+
#[\Override]
45+
public function get(string|\BackedEnum $policyKey): IPolicyDefinition {
46+
$normalizedKey = $this->normalizePolicyKey($policyKey);
47+
48+
return match ($normalizedKey) {
49+
self::KEY_TEMPLATE => new PolicySpec(
50+
key: self::KEY_TEMPLATE,
51+
defaultSystemValue: '',
52+
allowedValues: [],
53+
normalizer: static fn (mixed $rawValue): string => (string)$rawValue,
54+
appConfigKey: self::SYSTEM_APP_CONFIG_KEY_TEMPLATE,
55+
),
56+
self::KEY_TEMPLATE_FONT_SIZE => new PolicySpec(
57+
key: self::KEY_TEMPLATE_FONT_SIZE,
58+
defaultSystemValue: 9.0,
59+
allowedValues: [],
60+
normalizer: static fn (mixed $rawValue): float => (float)$rawValue,
61+
appConfigKey: self::SYSTEM_APP_CONFIG_KEY_TEMPLATE_FONT_SIZE,
62+
),
63+
self::KEY_SIGNATURE_WIDTH => new PolicySpec(
64+
key: self::KEY_SIGNATURE_WIDTH,
65+
defaultSystemValue: 90.0,
66+
allowedValues: [],
67+
normalizer: static fn (mixed $rawValue): float => (float)$rawValue,
68+
appConfigKey: self::SYSTEM_APP_CONFIG_KEY_SIGNATURE_WIDTH,
69+
),
70+
self::KEY_SIGNATURE_HEIGHT => new PolicySpec(
71+
key: self::KEY_SIGNATURE_HEIGHT,
72+
defaultSystemValue: 60.0,
73+
allowedValues: [],
74+
normalizer: static fn (mixed $rawValue): float => (float)$rawValue,
75+
appConfigKey: self::SYSTEM_APP_CONFIG_KEY_SIGNATURE_HEIGHT,
76+
),
77+
self::KEY_SIGNATURE_FONT_SIZE => new PolicySpec(
78+
key: self::KEY_SIGNATURE_FONT_SIZE,
79+
defaultSystemValue: 9.0,
80+
allowedValues: [],
81+
normalizer: static fn (mixed $rawValue): float => (float)$rawValue,
82+
appConfigKey: self::SYSTEM_APP_CONFIG_KEY_SIGNATURE_FONT_SIZE,
83+
),
84+
self::KEY_RENDER_MODE => new PolicySpec(
85+
key: self::KEY_RENDER_MODE,
86+
defaultSystemValue: 'default',
87+
allowedValues: [
88+
'default',
89+
'graphic',
90+
'text',
91+
],
92+
normalizer: static fn (mixed $rawValue): string => match ((string)$rawValue) {
93+
'default', 'graphic', 'text' => (string)$rawValue,
94+
default => 'default',
95+
},
96+
appConfigKey: self::SYSTEM_APP_CONFIG_KEY_RENDER_MODE,
97+
),
98+
default => throw new \InvalidArgumentException('Unknown policy key: ' . $normalizedKey),
99+
};
100+
}
101+
102+
private function normalizePolicyKey(string|\BackedEnum $policyKey): string {
103+
if ($policyKey instanceof \BackedEnum) {
104+
return (string)$policyKey->value;
105+
}
106+
107+
return $policyKey;
108+
}
109+
}

lib/Service/SignatureTextService.php

Lines changed: 75 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
use OCA\Libresign\Exception\LibresignException;
1818
use OCA\Libresign\Service\Policy\PolicyService;
1919
use OCA\Libresign\Service\Policy\Provider\CollectMetadata\CollectMetadataPolicy;
20+
use OCA\Libresign\Service\Policy\Provider\SignatureText\SignatureTextPolicy as SignatureTextPolicyProvider;
21+
use OCA\Libresign\Service\Policy\Provider\SignatureText\SignatureTextPolicy;
2022
use OCA\Libresign\Vendor\Twig\Environment;
2123
use OCA\Libresign\Vendor\Twig\Error\SyntaxError;
2224
use OCA\Libresign\Vendor\Twig\Loader\FilesystemLoader;
@@ -105,12 +107,30 @@ public function save(
105107
$template = strip_tags((string)$template);
106108
$template = trim($template);
107109
$template = html_entity_decode($template);
108-
$this->appConfig->setValueString(Application::APP_ID, 'signature_text_template', $template);
109-
$this->appConfig->setValueFloat(Application::APP_ID, 'signature_width', $signatureWidth);
110-
$this->appConfig->setValueFloat(Application::APP_ID, 'signature_height', $signatureHeight);
111-
$this->appConfig->setValueFloat(Application::APP_ID, 'template_font_size', $templateFontSize);
112-
$this->appConfig->setValueFloat(Application::APP_ID, 'signature_font_size', $signatureFontSize);
113-
$this->appConfig->setValueString(Application::APP_ID, 'signature_render_mode', $renderMode);
110+
if ($this->policyService !== null) {
111+
try {
112+
$this->policyService->saveSystemPolicy(SignatureTextPolicyProvider::KEY_TEMPLATE, $template);
113+
$this->policyService->saveSystemPolicy(SignatureTextPolicyProvider::KEY_SIGNATURE_WIDTH, $signatureWidth);
114+
$this->policyService->saveSystemPolicy(SignatureTextPolicyProvider::KEY_SIGNATURE_HEIGHT, $signatureHeight);
115+
$this->policyService->saveSystemPolicy(SignatureTextPolicyProvider::KEY_TEMPLATE_FONT_SIZE, $templateFontSize);
116+
$this->policyService->saveSystemPolicy(SignatureTextPolicyProvider::KEY_SIGNATURE_FONT_SIZE, $signatureFontSize);
117+
$this->policyService->saveSystemPolicy(SignatureTextPolicyProvider::KEY_RENDER_MODE, $renderMode);
118+
} catch (\Throwable) {
119+
$this->appConfig->setValueString(Application::APP_ID, 'signature_text_template', $template);
120+
$this->appConfig->setValueFloat(Application::APP_ID, 'signature_width', $signatureWidth);
121+
$this->appConfig->setValueFloat(Application::APP_ID, 'signature_height', $signatureHeight);
122+
$this->appConfig->setValueFloat(Application::APP_ID, 'template_font_size', $templateFontSize);
123+
$this->appConfig->setValueFloat(Application::APP_ID, 'signature_font_size', $signatureFontSize);
124+
$this->appConfig->setValueString(Application::APP_ID, 'signature_render_mode', $renderMode);
125+
}
126+
} else {
127+
$this->appConfig->setValueString(Application::APP_ID, 'signature_text_template', $template);
128+
$this->appConfig->setValueFloat(Application::APP_ID, 'signature_width', $signatureWidth);
129+
$this->appConfig->setValueFloat(Application::APP_ID, 'signature_height', $signatureHeight);
130+
$this->appConfig->setValueFloat(Application::APP_ID, 'template_font_size', $templateFontSize);
131+
$this->appConfig->setValueFloat(Application::APP_ID, 'signature_font_size', $signatureFontSize);
132+
$this->appConfig->setValueString(Application::APP_ID, 'signature_render_mode', $renderMode);
133+
}
114134
return $this->parse($template);
115135
}
116136

@@ -175,6 +195,13 @@ public function parse(string $template = '', array $context = []): array {
175195
}
176196

177197
public function getTemplate(): string {
198+
if ($this->policyService !== null) {
199+
try {
200+
return (string)$this->policyService->resolve(SignatureTextPolicyProvider::KEY_TEMPLATE)->effectiveValue();
201+
} catch (\Throwable) {
202+
// Fallback keeps legacy behavior during migration rollout.
203+
}
204+
}
178205
if ($this->appConfig->hasKey(Application::APP_ID, 'signature_text_template')) {
179206
return $this->appConfig->getValueString(Application::APP_ID, 'signature_text_template');
180207
}
@@ -449,15 +476,23 @@ public function getDefaultTemplate(): string {
449476
}
450477

451478
public function getFullSignatureWidth(): float {
452-
return $this->getSanitizedDimension('signature_width', self::DEFAULT_SIGNATURE_WIDTH);
479+
return $this->getSanitizedDimension(SignatureTextPolicyProvider::KEY_SIGNATURE_WIDTH, self::DEFAULT_SIGNATURE_WIDTH);
453480
}
454481

455482
public function getFullSignatureHeight(): float {
456-
return $this->getSanitizedDimension('signature_height', self::DEFAULT_SIGNATURE_HEIGHT);
483+
return $this->getSanitizedDimension(SignatureTextPolicyProvider::KEY_SIGNATURE_HEIGHT, self::DEFAULT_SIGNATURE_HEIGHT);
457484
}
458485

459486
public function getSignatureWidth(): float {
460-
$current = $this->appConfig->getValueFloat(Application::APP_ID, 'signature_width', self::DEFAULT_SIGNATURE_WIDTH);
487+
if ($this->policyService !== null) {
488+
try {
489+
$current = (float)$this->policyService->resolve(SignatureTextPolicyProvider::KEY_SIGNATURE_WIDTH)->effectiveValue();
490+
} catch (\Throwable) {
491+
$current = $this->appConfig->getValueFloat(Application::APP_ID, 'signature_width', self::DEFAULT_SIGNATURE_WIDTH);
492+
}
493+
} else {
494+
$current = $this->appConfig->getValueFloat(Application::APP_ID, 'signature_width', self::DEFAULT_SIGNATURE_WIDTH);
495+
}
461496
if ($this->getRenderMode() === SignerElementsService::RENDER_MODE_GRAPHIC_ONLY || !$this->getTemplate()) {
462497
return $current;
463498
}
@@ -469,7 +504,15 @@ public function getSignatureHeight(): float {
469504
}
470505

471506
private function getSanitizedDimension(string $key, float $default): float {
472-
$value = $this->appConfig->getValueFloat(Application::APP_ID, $key, $default);
507+
if ($this->policyService !== null) {
508+
try {
509+
$value = (float)$this->policyService->resolve($key)->effectiveValue();
510+
} catch (\Throwable) {
511+
$value = $this->appConfig->getValueFloat(Application::APP_ID, $key, $default);
512+
}
513+
} else {
514+
$value = $this->appConfig->getValueFloat(Application::APP_ID, $key, $default);
515+
}
473516
if (!is_finite($value) || $value < self::SIGNATURE_DIMENSION_MINIMUM) {
474517
$this->appConfig->setValueFloat(Application::APP_ID, $key, $default);
475518
$this->logger->warning('Invalid signature dimension found in app config. Falling back to default.', [
@@ -484,10 +527,15 @@ private function getSanitizedDimension(string $key, float $default): float {
484527

485528
public function getTemplateFontSize(): float {
486529
$collectMetadata = $this->isCollectMetadataEnabled();
487-
if ($collectMetadata) {
488-
return $this->appConfig->getValueFloat(Application::APP_ID, 'template_font_size', self::TEMPLATE_DEFAULT_FONT_SIZE - 1);
530+
$default = $collectMetadata ? self::TEMPLATE_DEFAULT_FONT_SIZE - 1 : self::TEMPLATE_DEFAULT_FONT_SIZE;
531+
if ($this->policyService !== null) {
532+
try {
533+
return (float)$this->policyService->resolve(SignatureTextPolicyProvider::KEY_TEMPLATE_FONT_SIZE)->effectiveValue();
534+
} catch (\Throwable) {
535+
// Fallback keeps legacy behavior during migration rollout.
536+
}
489537
}
490-
return $this->appConfig->getValueFloat(Application::APP_ID, 'template_font_size', self::TEMPLATE_DEFAULT_FONT_SIZE);
538+
return $this->appConfig->getValueFloat(Application::APP_ID, 'template_font_size', $default);
491539
}
492540

493541
public function getDefaultTemplateFontSize(): float {
@@ -511,10 +559,24 @@ private function isCollectMetadataEnabled(): bool {
511559
}
512560

513561
public function getSignatureFontSize(): float {
562+
if ($this->policyService !== null) {
563+
try {
564+
return (float)$this->policyService->resolve(SignatureTextPolicyProvider::KEY_SIGNATURE_FONT_SIZE)->effectiveValue();
565+
} catch (\Throwable) {
566+
// Fallback keeps legacy behavior during migration rollout.
567+
}
568+
}
514569
return $this->appConfig->getValueFloat(Application::APP_ID, 'signature_font_size', self::SIGNATURE_DEFAULT_FONT_SIZE);
515570
}
516571

517572
public function getRenderMode(): string {
573+
if ($this->policyService !== null) {
574+
try {
575+
return (string)$this->policyService->resolve(SignatureTextPolicyProvider::KEY_RENDER_MODE)->effectiveValue();
576+
} catch (\Throwable) {
577+
// Fallback keeps legacy behavior during migration rollout.
578+
}
579+
}
518580
return $this->appConfig->getValueString(Application::APP_ID, 'signature_render_mode', SignerElementsService::RENDER_MODE_DEFAULT);
519581
}
520582

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
Feature: Signature text policy layer
2+
Background:
3+
Given the app "libresign" is installed
4+
And user "admin" exists with default attributes
5+
6+
Scenario: Admin can set system-level signature text template via policy
7+
When I am logged in as admin
8+
And I set the policy "signature_text_template" with system value "Welcome {{SignerCommonName}}" via policy service
9+
Then the system policy "signature_text_template" should be "Welcome {{SignerCommonName}}"
10+
And the effective policy "signature_text_template" should be "Welcome {{SignerCommonName}}"
11+
12+
Scenario: Admin can set signature render mode via policy
13+
When I am logged in as admin
14+
And I set the policy "signature_render_mode" with system value "DESCRIPTION_ONLY" via policy service
15+
Then the system policy "signature_render_mode" should be "DESCRIPTION_ONLY"
16+
17+
Scenario: Admin can set signature dimensions via policy
18+
When I am logged in as admin
19+
And I set the policy "signature_width" with system value "100" via policy service
20+
And I set the policy "signature_height" with system value "60" via policy service
21+
Then the system policy "signature_width" should be "100"
22+
And the system policy "signature_height" should be "60"
23+
24+
Scenario: Admin can set signature font sizes via policy
25+
When I am logged in as admin
26+
And I set the policy "template_font_size" with system value "8.5" via policy service
27+
And I set the policy "signature_font_size" with system value "18" via policy service
28+
Then the system policy "template_font_size" should be "8.5"
29+
And the system policy "signature_font_size" should be "18"
30+
31+
Scenario: Policy fallback to appconfig during migration
32+
When I am logged in as admin
33+
And appConfig key "signature_text_template" is set to "Legacy template {{SignerCommonName}}"
34+
And the policy service is unavailable
35+
Then the effective policy "signature_text_template" should return "Legacy template {{SignerCommonName}}" from appConfig fallback
36+
37+
Scenario: User-level policy override for signature text
38+
Given user "signer" exists with default attributes
39+
When I am logged in as admin
40+
And I set the policy "signature_text_template" with user "signer" value "User-specific: {{SignerCommonName}}" via policy service
41+
Then the user "signer" effective policy "signature_text_template" should be "User-specific: {{SignerCommonName}}"
42+
43+
Scenario: Group-level policy for signature text
44+
Given user "signer" exists with default attributes
45+
And group "signers" exists
46+
And user "signer" is member of group "signers"
47+
When I am logged in as admin
48+
And I set the policy "signature_render_mode" with group "signers" value "GRAPHIC_ONLY" via policy service
49+
Then the user "signer" effective policy "signature_render_mode" should be "GRAPHIC_ONLY"
50+
51+
Scenario: Policy value normalization
52+
When I am logged in as admin
53+
And I set the policy "signature_width" with system value "350.75" via policy service
54+
Then the system policy "signature_width" should be normalized to float "350.75"
55+
56+
Scenario: Invalid policy value rejection
57+
When I am logged in as admin
58+
And I try to set the policy "signature_render_mode" with system value "INVALID_MODE" via policy service
59+
Then the invalid policy value should be normalized to default "default"

0 commit comments

Comments
 (0)