Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
43 / 43
100.00% covered (success)
100.00%
2 / 2
CRAP
100.00% covered (success)
100.00%
1 / 1
SignatureHashAlgorithmPolicy
100.00% covered (success)
100.00%
43 / 43
100.00% covered (success)
100.00%
2 / 2
14
100.00% covered (success)
100.00%
1 / 1
 keys
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 get
100.00% covered (success)
100.00%
40 / 40
100.00% covered (success)
100.00%
1 / 1
13
1<?php
2
3declare(strict_types=1);
4/**
5 * SPDX-FileCopyrightText: 2026 LibreCode coop and contributors
6 * SPDX-License-Identifier: AGPL-3.0-or-later
7 */
8
9namespace OCA\Libresign\Service\Policy\Provider\SignatureHashAlgorithm;
10
11use OCA\Libresign\Service\Policy\Contract\IPolicyDefinition;
12use OCA\Libresign\Service\Policy\Contract\IPolicyDefinitionProvider;
13use OCA\Libresign\Service\Policy\Model\PolicyContext;
14use OCA\Libresign\Service\Policy\Model\PolicyLayer;
15use OCA\Libresign\Service\Policy\Model\PolicySpec;
16use OCA\Libresign\Service\Policy\Provider\Helper\DelegationLayerHelper;
17use OCA\Libresign\Service\Policy\Provider\Helper\PolicyKeyNormalizer;
18
19final class SignatureHashAlgorithmPolicy implements IPolicyDefinitionProvider {
20    public const KEY = 'signature_hash_algorithm';
21    public const SYSTEM_APP_CONFIG_KEY = 'signature_hash_algorithm';
22
23    /** @var string[] */
24    private const ALGORITHMS = [
25        'SHA1',
26        'SHA256',
27        'SHA384',
28        'SHA512',
29        'RIPEMD160',
30    ];
31
32    #[\Override]
33    public function keys(): array {
34        return [
35            self::KEY,
36        ];
37    }
38
39    #[\Override]
40    public function get(string|\BackedEnum $policyKey): IPolicyDefinition {
41        return match (PolicyKeyNormalizer::normalize($policyKey)) {
42            self::KEY => new PolicySpec(
43                key: self::KEY,
44                defaultSystemValue: 'SHA256',
45                allowedValues: self::ALGORITHMS,
46                normalizer: static fn (mixed $rawValue): string => strtoupper(trim((string)$rawValue)),
47                validator: static function (mixed $value): void {
48                    if (!is_string($value) || !in_array($value, self::ALGORITHMS, true)) {
49                        throw new \InvalidArgumentException('Invalid value for ' . self::KEY);
50                    }
51                },
52                appConfigKey: self::SYSTEM_APP_CONFIG_KEY,
53                supportsRequestOverride: true,
54                requestLifecycle: PolicySpec::LIFECYCLE_RUNTIME,
55                groupPolicyManager: static function (PolicyContext $context, ?PolicyLayer $systemPolicy, array $groupLayers): bool {
56                    $actorRole = $context->getActorRole();
57                    if ($actorRole->canManageSystemPolicies) {
58                        return true;
59                    }
60
61                    if (!$actorRole->canManageGroupPolicies) {
62                        return false;
63                    }
64
65                    if ($actorRole->manageableGroupCount < 1) {
66                        return false;
67                    }
68
69                    return DelegationLayerHelper::hasExplicitGlobalDelegation($systemPolicy)
70                        || DelegationLayerHelper::hasSystemCreatedGroupDelegation($groupLayers);
71                },
72                systemCreatedGroupRuleEditor: static function (PolicyContext $context, ?PolicyLayer $systemPolicy, PolicyLayer $existingPolicy): bool {
73                    $actorRole = $context->getActorRole();
74                    if ($actorRole->canManageSystemPolicies) {
75                        return true;
76                    }
77
78                    if (!$actorRole->canManageGroupPolicies) {
79                        return false;
80                    }
81
82                    if (!$existingPolicy->isVisibleToChild() || !$existingPolicy->isAllowChildOverride() || $existingPolicy->getValue() === null) {
83                        return false;
84                    }
85
86                    if (DelegationLayerHelper::hasExplicitGlobalDelegation($systemPolicy)) {
87                        return true;
88                    }
89
90                    return $existingPolicy->isCreatedBySystemAdmin();
91                },
92                supportsGroupAdminDelegation: true,
93            ),
94            default => throw new \InvalidArgumentException('Unknown policy key: ' . PolicyKeyNormalizer::normalize($policyKey)),
95        };
96    }
97
98}