Skip to content

Commit ab91469

Browse files
committed
feat: migrate policy settings and footer workflow
Signed-off-by: Vitor Mattos <[email protected]>
1 parent 95f920d commit ab91469

45 files changed

Lines changed: 2926 additions & 2684 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

eslint.config.mjs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,23 @@
33
* SPDX-License-Identifier: AGPL-3.0-or-later
44
*/
55

6+
import js from '@eslint/js'
7+
import { FlatCompat } from '@eslint/eslintrc'
68
import nextcloudConfig from '@nextcloud/eslint-config'
7-
import globals from 'globals'
9+
import { dirname } from 'node:path'
10+
import { fileURLToPath } from 'node:url'
11+
12+
const compat = new FlatCompat({
13+
baseDirectory: dirname(fileURLToPath(import.meta.url)),
14+
recommendedConfig: js.configs.recommended,
15+
allConfig: js.configs.all,
16+
})
17+
18+
const compatConfigs = (Array.isArray(nextcloudConfig) ? nextcloudConfig : [nextcloudConfig])
19+
.flatMap((config) => compat.config(config))
820

921
export default [
10-
...(Array.isArray(nextcloudConfig) ? nextcloudConfig : [nextcloudConfig]),
22+
...compatConfigs,
1123

1224
{
1325
name: 'libresign/ignores',

lib/Handler/FooterHandler.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ class FooterHandler {
3636
private QrCode $qrCode;
3737
/** @var array<string, mixed> */
3838
private array $requestPolicyOverrides = [];
39+
private ?string $templateOverride = null;
3940
private ?bool $writeQrcodeOnFooterOverride = null;
4041
private const MIN_QRCODE_SIZE = 100;
4142
private const POINT_TO_MILIMETER = 0.3527777778;
@@ -129,6 +130,11 @@ public function setWriteQrcodeOnFooterOverride(?bool $value): self {
129130
return $this;
130131
}
131132

133+
public function setTemplateOverride(?string $template): self {
134+
$this->templateOverride = $template;
135+
return $this;
136+
}
137+
132138
public function getEffectiveFooterPolicyAsJson(): string {
133139
return (string)$this->policyService->resolve(FooterPolicy::KEY, $this->requestPolicyOverrides)->getEffectiveValue();
134140
}
@@ -201,6 +207,10 @@ private function prepareTemplateVars(bool $forceEnabled = false): array {
201207
}
202208

203209
public function getTemplate(): string {
210+
if ($this->templateOverride !== null) {
211+
return trim($this->templateOverride) !== '' ? $this->templateOverride : $this->getDefaultTemplate();
212+
}
213+
204214
$footerPolicy = $this->resolveFooterPolicy();
205215

206216
if ($footerPolicy['customizeFooterTemplate']) {

lib/Middleware/InjectionMiddleware.php

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
use OCA\Libresign\Middleware\Attribute\RequireSignerUuid;
3030
use OCA\Libresign\Middleware\Attribute\RequireSignRequestUuid;
3131
use OCA\Libresign\Service\FileAccessService;
32+
use OCA\Libresign\Service\Policy\PolicyService;
33+
use OCA\Libresign\Service\Policy\Provider\ValidationAccess\ValidationAccessPolicy;
3234
use OCA\Libresign\Service\SignFileService;
3335
use OCA\Libresign\Service\UuidResolverService;
3436
use OCP\AppFramework\Controller;
@@ -41,7 +43,6 @@
4143
use OCP\AppFramework\Http\TemplateResponse;
4244
use OCP\AppFramework\Middleware;
4345
use OCP\AppFramework\Services\IInitialState;
44-
use OCP\IAppConfig;
4546
use OCP\IL10N;
4647
use OCP\IRequest;
4748
use OCP\ISession;
@@ -63,8 +64,8 @@ public function __construct(
6364
private FileAccessService $fileAccessService,
6465
private SignFileService $signFileService,
6566
private UuidResolverService $uuidResolverService,
67+
private PolicyService $policyService,
6668
private IL10N $l10n,
67-
private IAppConfig $appConfig,
6869
private IURLGenerator $urlGenerator,
6970
protected ?string $userId,
7071
) {
@@ -114,7 +115,9 @@ private function privateValidation(\ReflectionMethod $reflectionMethod): void {
114115
if ($this->userSession->isLoggedIn()) {
115116
return;
116117
}
117-
$isValidationUrlPrivate = (bool)$this->appConfig->getValueBool(Application::APP_ID, 'make_validation_url_private', false);
118+
$isValidationUrlPrivate = $this->policyService
119+
->resolve(ValidationAccessPolicy::KEY)
120+
->getEffectiveValueAsBool();
118121
if (!$isValidationUrlPrivate) {
119122
return;
120123
}

lib/Service/FooterService.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,19 @@ public function getTemplate(): string {
2929
return $this->footerHandler->getTemplate();
3030
}
3131

32+
public function getDefaultTemplate(): string {
33+
return $this->footerHandler->getDefaultTemplate();
34+
}
35+
3236
public function saveTemplate(string $template = ''): void {
33-
$currentPolicy = $this->getEffectiveFooterPolicy();
34-
$defaultTemplateFromPolicy = $currentPolicy['footerTemplate'];
37+
$defaultTemplate = $this->footerHandler->getDefaultTemplate();
3538

3639
if (empty($template)) {
3740
$this->syncFooterPolicyTemplate('', false);
3841
return;
3942
}
4043

41-
$isProvidedTemplateEqualsDefault = $template === $defaultTemplateFromPolicy;
44+
$isProvidedTemplateEqualsDefault = $template === $defaultTemplate;
4245

4346
if ($isProvidedTemplateEqualsDefault) {
4447
$this->syncFooterPolicyTemplate('', false);
@@ -68,10 +71,6 @@ private function getEffectiveFooterPolicy(): array {
6871
}
6972

7073
public function renderPreviewPdf(string $template = '', int $width = 595, int $height = 50, ?bool $writeQrcodeOnFooter = null): string {
71-
if (!empty($template)) {
72-
$this->saveTemplate($template);
73-
}
74-
7574
$previewUuid = sprintf(
7675
'preview-%04x-%04x-%04x-%012x',
7776
random_int(0, 0xffff),
@@ -81,6 +80,7 @@ public function renderPreviewPdf(string $template = '', int $width = 595, int $h
8180
);
8281

8382
$handler = $this->footerHandler
83+
->setTemplateOverride($template !== '' ? $template : null)
8484
->setTemplateVar('uuid', $previewUuid)
8585
->setTemplateVar('signers', [
8686
[

lib/Service/Policy/Model/ResolvedPolicy.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,31 @@ public function getEffectiveValue(): mixed {
4040
return $this->effectiveValue;
4141
}
4242

43+
public function getEffectiveValueAsBool(bool $default = false): bool {
44+
if (is_bool($this->effectiveValue)) {
45+
return $this->effectiveValue;
46+
}
47+
48+
if ($this->effectiveValue === null) {
49+
return $default;
50+
}
51+
52+
if (is_int($this->effectiveValue)) {
53+
return $this->effectiveValue !== 0;
54+
}
55+
56+
if (is_float($this->effectiveValue)) {
57+
return $this->effectiveValue !== 0.0;
58+
}
59+
60+
if (is_string($this->effectiveValue)) {
61+
$parsed = filter_var($this->effectiveValue, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
62+
return $parsed ?? $default;
63+
}
64+
65+
return $default;
66+
}
67+
4368
public function setInheritedValue(mixed $inheritedValue): self {
4469
$this->inheritedValue = $inheritedValue;
4570
return $this;

lib/Service/Policy/Provider/Footer/FooterPolicy.php

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,15 @@ public function keys(): array {
2626

2727
#[\Override]
2828
public function get(string|\BackedEnum $policyKey): IPolicyDefinition {
29-
$instanceBaseTemplate = $this->resolveInstanceBaseTemplate();
3029
return match ($this->normalizePolicyKey($policyKey)) {
3130
self::KEY => new PolicySpec(
3231
key: self::KEY,
33-
defaultSystemValue: FooterPolicyValue::encode(FooterPolicyValue::defaults($instanceBaseTemplate)),
32+
defaultSystemValue: FooterPolicyValue::encode(FooterPolicyValue::defaults()),
3433
allowedValues: static fn (): array => [],
35-
normalizer: function (mixed $rawValue) use ($instanceBaseTemplate): mixed {
36-
return FooterPolicyValue::encode(FooterPolicyValue::normalize($rawValue, $instanceBaseTemplate));
34+
normalizer: static function (mixed $rawValue): mixed {
35+
return FooterPolicyValue::encode(FooterPolicyValue::normalize($rawValue));
3736
},
38-
validator: function (mixed $value, PolicyContext $context) use ($instanceBaseTemplate): void {
37+
validator: static function (mixed $value, PolicyContext $context): void {
3938
if (!is_string($value) || trim($value) === '') {
4039
throw new \InvalidArgumentException('Invalid value for ' . self::KEY);
4140
}
@@ -46,7 +45,7 @@ public function get(string|\BackedEnum $policyKey): IPolicyDefinition {
4645
}
4746

4847
if (!self::canManageTechnicalFooterSettings($context)) {
49-
$normalized = FooterPolicyValue::normalize($decoded, $instanceBaseTemplate);
48+
$normalized = FooterPolicyValue::normalize($decoded);
5049
if ($normalized['validationSite'] !== '') {
5150
throw new \InvalidArgumentException('Validation URL override is not allowed for this actor');
5251
}
@@ -72,11 +71,4 @@ private static function canManageTechnicalFooterSettings(PolicyContext $context)
7271
return ($capabilities['canManageSystemPolicies'] ?? false) === true
7372
|| ($capabilities['canManageGroupPolicies'] ?? false) === true;
7473
}
75-
76-
private function resolveInstanceBaseTemplate(): string {
77-
$defaultTemplatePath = __DIR__ . '/../../../../Handler/Templates/footer.twig';
78-
$defaultTemplate = @file_get_contents($defaultTemplatePath);
79-
80-
return is_string($defaultTemplate) ? $defaultTemplate : '';
81-
}
8274
}

lib/Service/Policy/Provider/PolicyProviders.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
use OCA\Libresign\Service\Policy\Provider\IdentificationDocuments\IdentificationDocumentsPolicy;
1616
use OCA\Libresign\Service\Policy\Provider\RequestSignGroups\RequestSignGroupsPolicy;
1717
use OCA\Libresign\Service\Policy\Provider\Signature\SignatureFlowPolicy;
18+
use OCA\Libresign\Service\Policy\Provider\SignatureBackground\SignatureBackgroundPolicy;
1819
use OCA\Libresign\Service\Policy\Provider\SignatureText\SignatureTextPolicy;
20+
use OCA\Libresign\Service\Policy\Provider\ValidationAccess\ValidationAccessPolicy;
1921

2022
final class PolicyProviders {
2123
/** @var array<string, class-string> */
@@ -25,7 +27,9 @@ final class PolicyProviders {
2527
FooterPolicy::KEY => FooterPolicy::class,
2628
DocMdpPolicy::KEY => DocMdpPolicy::class,
2729
RequestSignGroupsPolicy::KEY => RequestSignGroupsPolicy::class,
30+
ValidationAccessPolicy::KEY => ValidationAccessPolicy::class,
2831
SignatureFlowPolicy::KEY => SignatureFlowPolicy::class,
32+
SignatureBackgroundPolicy::KEY => SignatureBackgroundPolicy::class,
2933
IdentificationDocumentsPolicy::KEY => IdentificationDocumentsPolicy::class,
3034
SignatureTextPolicy::KEY => SignatureTextPolicy::class,
3135
SignatureTextPolicy::KEY_TEMPLATE => SignatureTextPolicy::class,
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
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\SignatureBackground;
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 SignatureBackgroundPolicy implements IPolicyDefinitionProvider {
16+
public const KEY = 'signature_background_type';
17+
public const SYSTEM_APP_CONFIG_KEY = 'signature_background_type';
18+
19+
#[\Override]
20+
public function keys(): array {
21+
return [
22+
self::KEY,
23+
];
24+
}
25+
26+
#[\Override]
27+
public function get(string|\BackedEnum $policyKey): IPolicyDefinition {
28+
return match ($this->normalizePolicyKey($policyKey)) {
29+
self::KEY => new PolicySpec(
30+
key: self::KEY,
31+
defaultSystemValue: 'default',
32+
allowedValues: [
33+
'default',
34+
'custom',
35+
'deleted',
36+
],
37+
normalizer: static fn (mixed $rawValue): string => self::normalizeBackgroundType($rawValue),
38+
appConfigKey: self::SYSTEM_APP_CONFIG_KEY,
39+
),
40+
default => throw new \InvalidArgumentException('Unknown policy key: ' . $this->normalizePolicyKey($policyKey)),
41+
};
42+
}
43+
44+
private static function normalizeBackgroundType(mixed $rawValue): string {
45+
if (!is_string($rawValue)) {
46+
return 'default';
47+
}
48+
49+
$normalized = trim(strtolower($rawValue));
50+
if (in_array($normalized, ['default', 'custom', 'deleted'], true)) {
51+
return $normalized;
52+
}
53+
54+
return 'default';
55+
}
56+
57+
private function normalizePolicyKey(string|\BackedEnum $policyKey): string {
58+
if ($policyKey instanceof \BackedEnum) {
59+
return (string)$policyKey->value;
60+
}
61+
62+
return $policyKey;
63+
}
64+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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\ValidationAccess;
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 ValidationAccessPolicy implements IPolicyDefinitionProvider {
16+
public const KEY = 'make_validation_url_private';
17+
public const SYSTEM_APP_CONFIG_KEY = 'make_validation_url_private';
18+
19+
#[\Override]
20+
public function keys(): array {
21+
return [
22+
self::KEY,
23+
];
24+
}
25+
26+
#[\Override]
27+
public function get(string|\BackedEnum $policyKey): IPolicyDefinition {
28+
return match ($this->normalizePolicyKey($policyKey)) {
29+
self::KEY => new PolicySpec(
30+
key: self::KEY,
31+
defaultSystemValue: false,
32+
allowedValues: [
33+
false,
34+
true,
35+
],
36+
normalizer: static fn (mixed $rawValue): bool => filter_var($rawValue, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) ?? false,
37+
appConfigKey: self::SYSTEM_APP_CONFIG_KEY,
38+
),
39+
default => throw new \InvalidArgumentException('Unknown policy key: ' . $this->normalizePolicyKey($policyKey)),
40+
};
41+
}
42+
43+
private function normalizePolicyKey(string|\BackedEnum $policyKey): string {
44+
if ($policyKey instanceof \BackedEnum) {
45+
return (string)$policyKey->value;
46+
}
47+
48+
return $policyKey;
49+
}
50+
}

lib/Service/Policy/Runtime/PolicySource.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -544,7 +544,15 @@ public function saveSystemPolicy(string $policyKey, mixed $value, bool $allowChi
544544
$defaultValue = $definition->normalizeValue($definition->defaultSystemValue());
545545
$allowOverrideConfigKey = $this->getSystemAllowOverrideConfigKey($definition->getAppConfigKey());
546546

547-
if ($normalizedValue === $defaultValue) {
547+
$valuesAreEqual = $normalizedValue === $defaultValue;
548+
if (!$valuesAreEqual && is_string($normalizedValue) && is_string($defaultValue)) {
549+
$d1 = json_decode($normalizedValue, true);
550+
$d2 = json_decode($defaultValue, true);
551+
if (is_array($d1) && is_array($d2)) {
552+
$valuesAreEqual = $d1 === $d2;
553+
}
554+
}
555+
if ($valuesAreEqual) {
548556
if ($allowChildOverride) {
549557
$this->writeSystemValue($definition->getAppConfigKey(), $normalizedValue);
550558
$this->appConfig->setAppValueString($allowOverrideConfigKey, '1');

0 commit comments

Comments
 (0)