Skip to content

Commit 5696f98

Browse files
committed
fix(ci): add missing policy providers and restore scalar policy payload typing
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
1 parent 31cf1a8 commit 5696f98

11 files changed

Lines changed: 377 additions & 20 deletions

File tree

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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\IdentifyMethods;
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 IdentifyMethodsPolicy implements IPolicyDefinitionProvider {
16+
public const KEY = 'identify_methods';
17+
public const SYSTEM_APP_CONFIG_KEY = self::KEY;
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: [],
32+
allowedValues: static fn (): array => [],
33+
normalizer: static fn (mixed $rawValue): array => \OCA\Libresign\Service\Policy\Provider\IdentifyMethods\IdentifyMethodsPolicyValue::normalize($rawValue),
34+
appConfigKey: self::SYSTEM_APP_CONFIG_KEY,
35+
),
36+
default => throw new \InvalidArgumentException('Unknown policy key: ' . $this->normalizePolicyKey($policyKey)),
37+
};
38+
}
39+
40+
private function normalizePolicyKey(string|\BackedEnum $policyKey): string {
41+
if ($policyKey instanceof \BackedEnum) {
42+
return (string)$policyKey->value;
43+
}
44+
45+
return $policyKey;
46+
}
47+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
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\IdentifyMethods;
10+
11+
final class IdentifyMethodsPolicyValue {
12+
/**
13+
* @return list<array<string, mixed>>
14+
*/
15+
public static function normalize(mixed $rawValue): array {
16+
if (is_string($rawValue)) {
17+
$decoded = json_decode($rawValue, true);
18+
if (is_array($decoded)) {
19+
$rawValue = $decoded;
20+
}
21+
}
22+
23+
if (!is_array($rawValue)) {
24+
return [];
25+
}
26+
27+
$normalized = [];
28+
foreach ($rawValue as $entry) {
29+
if (!is_array($entry)) {
30+
continue;
31+
}
32+
33+
$name = isset($entry['name']) && is_string($entry['name'])
34+
? trim($entry['name'])
35+
: '';
36+
if ($name === '') {
37+
continue;
38+
}
39+
40+
$signatureMethods = [];
41+
if (isset($entry['signatureMethods']) && is_array($entry['signatureMethods'])) {
42+
foreach ($entry['signatureMethods'] as $signatureMethodName => $signatureMethodConfig) {
43+
if (!is_string($signatureMethodName) || trim($signatureMethodName) === '' || !is_array($signatureMethodConfig)) {
44+
continue;
45+
}
46+
47+
$normalizedSignatureMethod = [];
48+
if (array_key_exists('enabled', $signatureMethodConfig)) {
49+
$normalizedSignatureMethod['enabled'] = (bool)$signatureMethodConfig['enabled'];
50+
}
51+
52+
if (isset($signatureMethodConfig['label']) && is_string($signatureMethodConfig['label'])) {
53+
$normalizedSignatureMethod['label'] = $signatureMethodConfig['label'];
54+
}
55+
56+
$signatureMethods[$signatureMethodName] = $normalizedSignatureMethod;
57+
}
58+
}
59+
60+
$normalizedEntry = [
61+
'name' => $name,
62+
'enabled' => array_key_exists('enabled', $entry) ? (bool)$entry['enabled'] : true,
63+
'signatureMethods' => $signatureMethods,
64+
];
65+
66+
if (isset($entry['friendly_name']) && is_string($entry['friendly_name'])) {
67+
$normalizedEntry['friendly_name'] = $entry['friendly_name'];
68+
}
69+
70+
if (array_key_exists('can_create_account', $entry)) {
71+
$normalizedEntry['can_create_account'] = (bool)$entry['can_create_account'];
72+
}
73+
74+
if (array_key_exists('mandatory', $entry)) {
75+
$normalizedEntry['mandatory'] = (bool)$entry['mandatory'];
76+
}
77+
78+
if (isset($entry['signatureMethodEnabled']) && is_string($entry['signatureMethodEnabled'])) {
79+
$normalizedEntry['signatureMethodEnabled'] = $entry['signatureMethodEnabled'];
80+
}
81+
82+
$normalized[] = $normalizedEntry;
83+
}
84+
85+
return $normalized;
86+
}
87+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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\Tsa;
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 TsaPolicy implements IPolicyDefinitionProvider {
16+
public const KEY = 'tsa_settings';
17+
public const SYSTEM_APP_CONFIG_KEY = self::KEY;
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: TsaPolicyValue::encode(TsaPolicyValue::defaults()),
32+
allowedValues: static fn (): array => [],
33+
normalizer: static fn (mixed $rawValue): string => TsaPolicyValue::encode($rawValue),
34+
appConfigKey: self::SYSTEM_APP_CONFIG_KEY,
35+
supportsUserPreference: false,
36+
),
37+
default => throw new \InvalidArgumentException('Unknown policy key: ' . $this->normalizePolicyKey($policyKey)),
38+
};
39+
}
40+
41+
private function normalizePolicyKey(string|\BackedEnum $policyKey): string {
42+
if ($policyKey instanceof \BackedEnum) {
43+
return (string)$policyKey->value;
44+
}
45+
46+
return $policyKey;
47+
}
48+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
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\Tsa;
10+
11+
final class TsaPolicyValue {
12+
/** @return array{url: string, policy_oid: string, auth_type: string, username: string} */
13+
public static function defaults(): array {
14+
return [
15+
'url' => '',
16+
'policy_oid' => '',
17+
'auth_type' => 'none',
18+
'username' => '',
19+
];
20+
}
21+
22+
/** @return array{url: string, policy_oid: string, auth_type: string, username: string} */
23+
public static function decode(mixed $rawValue): array {
24+
if (is_string($rawValue)) {
25+
$decoded = json_decode($rawValue, true);
26+
if (is_array($decoded)) {
27+
$rawValue = $decoded;
28+
}
29+
}
30+
31+
if (!is_array($rawValue)) {
32+
return self::defaults();
33+
}
34+
35+
$url = isset($rawValue['url']) && is_string($rawValue['url'])
36+
? trim($rawValue['url'])
37+
: '';
38+
39+
$policyOid = isset($rawValue['policy_oid']) && is_string($rawValue['policy_oid'])
40+
? trim($rawValue['policy_oid'])
41+
: '';
42+
43+
if ($policyOid !== '' && !preg_match('/^[0-9]+(\.[0-9]+)*$/', $policyOid)) {
44+
$policyOid = '';
45+
}
46+
47+
$authType = isset($rawValue['auth_type']) && is_string($rawValue['auth_type'])
48+
? trim($rawValue['auth_type'])
49+
: 'none';
50+
if (!in_array($authType, ['none', 'basic'], true)) {
51+
$authType = 'none';
52+
}
53+
54+
$username = isset($rawValue['username']) && is_string($rawValue['username'])
55+
? trim($rawValue['username'])
56+
: '';
57+
58+
if ($authType !== 'basic') {
59+
$username = '';
60+
}
61+
62+
return [
63+
'url' => $url,
64+
'policy_oid' => $policyOid,
65+
'auth_type' => $authType,
66+
'username' => $username,
67+
];
68+
}
69+
70+
public static function encode(mixed $rawValue): string {
71+
return json_encode(self::decode($rawValue), JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES);
72+
}
73+
}

src/types/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export type SignatureFlowValue = SignatureFlowMode | 0 | 1 | 2
5454
export type EffectivePoliciesResponse = ApiOcsResponseData<ApiOperations['policy-effective'], 200>
5555
export type EffectivePoliciesState = EffectivePoliciesResponse['policies']
5656
export type EffectivePolicyState = ApiRecordValue<EffectivePoliciesState>
57-
export type EffectivePolicyValue = Exclude<ApiRequestJsonBody<AdminOperations['policy-set-system']>['value'], undefined> | unknown[]
57+
export type EffectivePolicyValue = Exclude<ApiRequestJsonBody<AdminOperations['policy-set-system']>['value'], undefined>
5858
export type GroupPolicyResponse = ApiOcsResponseData<ApiOperations['policy-get-group'], 200>
5959
export type GroupPolicyState = GroupPolicyResponse['policy']
6060

src/views/Settings/PolicyWorkbench/settings/identify-methods/IdentifyMethodsRuleEditor.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ import type { EffectivePolicyValue } from '../../../../../types/index'
5555
import {
5656
normalizeIdentifyMethodsPolicy,
5757
serializeIdentifyMethodsPolicy,
58-
type IdentifyMethodPolicyEntry,
5958
} from './model'
59+
import type { IdentifyMethodPolicyEntry } from './model'
6060
6161
defineOptions({
6262
name: 'IdentifyMethodsRuleEditor',
@@ -67,7 +67,7 @@ const props = defineProps<{
6767
}>()
6868
6969
const emit = defineEmits<{
70-
'update:modelValue': [value: IdentifyMethodPolicyEntry[]]
70+
'update:modelValue': [value: EffectivePolicyValue]
7171
}>()
7272
7373
const entries = computed(() => {

src/views/Settings/PolicyWorkbench/settings/identify-methods/model.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ export function normalizeIdentifyMethodsPolicy(value: EffectivePolicyValue): Ide
6464
return normalized
6565
}
6666

67-
export function serializeIdentifyMethodsPolicy(entries: IdentifyMethodPolicyEntry[]): IdentifyMethodPolicyEntry[] {
68-
return entries.map((entry) => {
67+
export function serializeIdentifyMethodsPolicy(entries: IdentifyMethodPolicyEntry[]): string {
68+
const normalizedEntries = entries.map((entry) => {
6969
const signatureMethods: Record<string, IdentifyMethodSignatureMethod> = {}
7070

7171
for (const [signatureMethodName, signatureMethod] of Object.entries(entry.signatureMethods)) {
@@ -98,6 +98,8 @@ export function serializeIdentifyMethodsPolicy(entries: IdentifyMethodPolicyEntr
9898

9999
return normalizedEntry
100100
})
101+
102+
return JSON.stringify(normalizedEntries)
101103
}
102104

103105
function normalizeSignatureMethods(value: unknown): Record<string, IdentifyMethodSignatureMethod> {

src/views/Settings/PolicyWorkbench/settings/identify-methods/realDefinition.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export const identifyMethodsRealDefinition: RealPolicySettingDefinition = {
1818
supportedScopes: ['system', 'group', 'user'],
1919
editor: IdentifyMethodsRuleEditor,
2020
resolutionMode: 'precedence',
21-
createEmptyValue: () => [],
21+
createEmptyValue: () => serializeIdentifyMethodsPolicy([]),
2222
normalizeDraftValue: (value: EffectivePolicyValue) => serializeIdentifyMethodsPolicy(normalizeIdentifyMethodsPolicy(value)),
2323
hasSelectableDraftValue: () => true,
2424
normalizeAllowChildOverride: (_scope, allowChildOverride: boolean) => allowChildOverride,
@@ -27,7 +27,7 @@ export const identifyMethodsRealDefinition: RealPolicySettingDefinition = {
2727
return policyValue
2828
}
2929

30-
return []
30+
return serializeIdentifyMethodsPolicy([])
3131
},
3232
summarizeValue: (value: EffectivePolicyValue) => {
3333
const normalized = normalizeIdentifyMethodsPolicy(value)
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\Tests\Unit\Service\Policy\Provider\IdentifyMethods;
10+
11+
use OCA\Libresign\Service\Policy\Provider\IdentifyMethods\IdentifyMethodsPolicy;
12+
use PHPUnit\Framework\TestCase;
13+
14+
final class IdentifyMethodsPolicyTest extends TestCase {
15+
public function testProviderBuildsIdentifyMethodsDefinition(): void {
16+
$provider = new IdentifyMethodsPolicy();
17+
$this->assertSame([IdentifyMethodsPolicy::KEY], $provider->keys());
18+
19+
$definition = $provider->get(IdentifyMethodsPolicy::KEY);
20+
$this->assertSame(IdentifyMethodsPolicy::KEY, $definition->key());
21+
$this->assertSame([], $definition->defaultSystemValue());
22+
}
23+
24+
public function testProviderNormalizesIdentifyMethodsPayload(): void {
25+
$provider = new IdentifyMethodsPolicy();
26+
$definition = $provider->get(IdentifyMethodsPolicy::KEY);
27+
28+
$normalized = $definition->normalizeValue([
29+
[
30+
'name' => 'email',
31+
'friendly_name' => 'Email',
32+
'enabled' => 1,
33+
'can_create_account' => '0',
34+
'signatureMethods' => [
35+
'email' => [
36+
'enabled' => true,
37+
'label' => 'Email token',
38+
],
39+
'clickToSign' => [
40+
'enabled' => false,
41+
],
42+
],
43+
],
44+
]);
45+
46+
$this->assertSame([
47+
[
48+
'name' => 'email',
49+
'enabled' => true,
50+
'signatureMethods' => [
51+
'email' => [
52+
'enabled' => true,
53+
'label' => 'Email token',
54+
],
55+
'clickToSign' => [
56+
'enabled' => false,
57+
],
58+
],
59+
'friendly_name' => 'Email',
60+
'can_create_account' => false,
61+
],
62+
], $normalized);
63+
}
64+
}

0 commit comments

Comments
 (0)