Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
66.10% covered (warning)
66.10%
39 / 59
66.67% covered (warning)
66.67%
2 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
ObserverProfilePolicy
66.10% covered (warning)
66.10%
39 / 59
66.67% covered (warning)
66.67%
2 / 3
19.58
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 keys
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 get
63.64% covered (warning)
63.64%
35 / 55
0.00% covered (danger)
0.00%
0 / 1
16.82
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\ObserverProfile;
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;
18use OCA\Libresign\Service\Policy\Provider\Helper\SiblingPolicyEffectiveBoolReader;
19use OCA\Libresign\Service\Policy\Provider\ValidationAccess\ValidationAccessPolicy;
20
21final class ObserverProfilePolicy implements IPolicyDefinitionProvider {
22    public const KEY = 'enable_observer_profile';
23    public const SYSTEM_APP_CONFIG_KEY = self::KEY;
24
25    public function __construct(
26        private SiblingPolicyEffectiveBoolReader $siblingPolicyEffectiveBoolReader,
27    ) {
28    }
29
30    #[\Override]
31    public function keys(): array {
32        return [
33            self::KEY,
34        ];
35    }
36
37    #[\Override]
38    public function get(string|\BackedEnum $policyKey): IPolicyDefinition {
39        return match (PolicyKeyNormalizer::normalize($policyKey)) {
40            self::KEY => new PolicySpec(
41                key: self::KEY,
42                defaultSystemValue: false,
43                allowedValues: [
44                    false,
45                    true,
46                ],
47                normalizer: ObserverProfilePolicyValue::normalize(...),
48                appConfigKey: self::SYSTEM_APP_CONFIG_KEY,
49                supportsRequestOverride: true,
50                requestLifecycle: PolicySpec::LIFECYCLE_REQUEST_SNAPSHOT,
51                resolvedStateMeta: fn (PolicyContext $context): array => [
52                    'validationUrlIsPrivate' => $this->siblingPolicyEffectiveBoolReader->getEffectiveBool(
53                        ValidationAccessPolicy::KEY,
54                        $context,
55                    ),
56                ],
57                supportedScopes: [
58                    PolicySpec::SCOPE_SYSTEM,
59                    PolicySpec::SCOPE_GROUP,
60                    PolicySpec::SCOPE_USER,
61                ],
62                groupPolicyManager: static function (PolicyContext $context, ?PolicyLayer $systemPolicy, array $groupLayers): bool {
63                    $actorRole = $context->getActorRole();
64
65                    if ($actorRole->canManageSystemPolicies) {
66                        return true;
67                    }
68
69                    if (!$actorRole->canManageGroupPolicies) {
70                        return false;
71                    }
72
73                    if ($actorRole->manageableGroupCount < 1) {
74                        return false;
75                    }
76
77                    if (DelegationLayerHelper::hasExplicitGlobalDelegation($systemPolicy)) {
78                        return true;
79                    }
80
81                    return DelegationLayerHelper::hasSystemCreatedGroupDelegation($groupLayers);
82                },
83                systemCreatedGroupRuleEditor: static function (PolicyContext $context, ?PolicyLayer $systemPolicy, PolicyLayer $existingPolicy): bool {
84                    $actorRole = $context->getActorRole();
85
86                    if ($actorRole->canManageSystemPolicies) {
87                        return true;
88                    }
89
90                    if (!$actorRole->canManageGroupPolicies) {
91                        return false;
92                    }
93
94                    if (!$existingPolicy->isVisibleToChild()) {
95                        return false;
96                    }
97
98                    if (!$existingPolicy->isAllowChildOverride()) {
99                        return false;
100                    }
101
102                    if ($existingPolicy->getValue() === null) {
103                        return false;
104                    }
105
106                    if (DelegationLayerHelper::hasExplicitGlobalDelegation($systemPolicy)) {
107                        return true;
108                    }
109
110                    return $existingPolicy->isCreatedBySystemAdmin();
111                },
112                supportsGroupAdminDelegation: true,
113            ),
114            default => throw new \InvalidArgumentException('Unknown policy key: ' . PolicyKeyNormalizer::normalize($policyKey)),
115        };
116    }
117}