Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
94.59% covered (success)
94.59%
70 / 74
57.14% covered (warning)
57.14%
4 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
ValidationEffectivePolicyService
94.59% covered (success)
94.59%
70 / 74
57.14% covered (warning)
57.14%
4 / 7
20.06
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
 appendEffectivePolicies
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 getPublicValidationEffectivePolicies
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
2
 projectPublicValidationPolicies
91.67% covered (success)
91.67%
11 / 12
0.00% covered (danger)
0.00%
0 / 1
3.01
 removePolicySnapshots
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 extractRequesterUserId
87.50% covered (warning)
87.50%
7 / 8
0.00% covered (danger)
0.00%
0 / 1
4.03
 preferPolicySnapshotWhenAvailable
94.44% covered (success)
94.44%
34 / 36
0.00% covered (danger)
0.00%
0 / 1
6.01
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;
10
11use OCA\Libresign\Service\Policy\Provider\LegalInformation\LegalInformationPolicy;
12
13final class ValidationEffectivePolicyService {
14    public function __construct(
15        private PolicyService $policyService,
16    ) {
17    }
18
19    /**
20     * @param array<string, mixed> $payload
21     * @return array<string, mixed>
22     */
23    public function appendEffectivePolicies(array $payload): array {
24        $effectivePolicies = $this->getPublicValidationEffectivePolicies($payload);
25        $payload = $this->removePolicySnapshots($payload);
26        $payload['effective_policies'] = $effectivePolicies;
27
28        return $payload;
29    }
30
31    /**
32     * @param array<string, mixed> $payload
33     * @return array{policies: array<string, array<string, mixed>>}
34     */
35    public function getPublicValidationEffectivePolicies(array $payload): array {
36        $requesterUserId = $this->extractRequesterUserId($payload);
37        $resolvedPolicyStates = $requesterUserId !== null
38            ? $this->policyService->resolveKnownPolicyStatesForUserIdWithoutUserScope($requesterUserId)
39            : $this->policyService->resolveKnownPolicyStates();
40
41        $resolvedPolicyStates = $this->preferPolicySnapshotWhenAvailable($resolvedPolicyStates, $payload);
42
43        return [
44            'policies' => $this->projectPublicValidationPolicies($resolvedPolicyStates),
45        ];
46    }
47
48    /**
49     * @param array<string, array<string, mixed>> $resolvedPolicyStates
50     * @return array<string, array<string, mixed>>
51     */
52    private function projectPublicValidationPolicies(array $resolvedPolicyStates): array {
53        $legalInformation = $resolvedPolicyStates[LegalInformationPolicy::KEY] ?? null;
54        if (!is_array($legalInformation)) {
55            return [];
56        }
57
58        $effectiveValue = $legalInformation['effectiveValue'] ?? null;
59        if (!is_string($effectiveValue)) {
60            return [];
61        }
62
63        return [
64            LegalInformationPolicy::KEY => [
65                'policyKey' => LegalInformationPolicy::KEY,
66                'effectiveValue' => $effectiveValue,
67            ],
68        ];
69    }
70
71    /**
72     * @param array<array-key, mixed> $payload
73     * @return array<array-key, mixed>
74     */
75    private function removePolicySnapshots(array $payload): array {
76        unset($payload['policy_snapshot']);
77
78        foreach ($payload as $key => $value) {
79            if (is_array($value)) {
80                $payload[$key] = $this->removePolicySnapshots($value);
81            }
82        }
83
84        return $payload;
85    }
86
87    /**
88     * @param array<string, mixed> $payload
89     */
90    private function extractRequesterUserId(array $payload): ?string {
91        $requestedBy = $payload['requested_by'] ?? null;
92        if (!is_array($requestedBy)) {
93            return null;
94        }
95
96        $userId = $requestedBy['userId'] ?? null;
97        if (!is_string($userId)) {
98            return null;
99        }
100
101        $userId = trim($userId);
102
103        return $userId !== '' ? $userId : null;
104    }
105
106    /**
107     * @param array<string, array<string, mixed>> $resolvedPolicyStates
108     * @param array<string, mixed> $payload
109     * @return array<string, array<string, mixed>>
110     */
111    private function preferPolicySnapshotWhenAvailable(array $resolvedPolicyStates, array $payload): array {
112        $metadata = $payload['metadata'] ?? null;
113        if (!is_array($metadata)) {
114            return $resolvedPolicyStates;
115        }
116
117        $policySnapshot = $metadata['policy_snapshot'] ?? null;
118        if (!is_array($policySnapshot)) {
119            return $resolvedPolicyStates;
120        }
121
122        $legalInformationSnapshot = $policySnapshot[LegalInformationPolicy::KEY] ?? null;
123        if (!is_array($legalInformationSnapshot)) {
124            return $resolvedPolicyStates;
125        }
126
127        $effectiveValue = $legalInformationSnapshot['effectiveValue'] ?? null;
128        $sourceScope = $legalInformationSnapshot['sourceScope'] ?? null;
129        if (!is_string($effectiveValue) || !is_string($sourceScope)) {
130            return $resolvedPolicyStates;
131        }
132
133        $resolvedPolicyStates[LegalInformationPolicy::KEY] = array_merge(
134            $resolvedPolicyStates[LegalInformationPolicy::KEY] ?? [
135                'policyKey' => LegalInformationPolicy::KEY,
136                'effectiveValue' => '',
137                'inheritedValue' => null,
138                'sourceScope' => 'system',
139                'visible' => true,
140                'editableByCurrentActor' => false,
141                'allowedValues' => [],
142                'canSaveAsUserDefault' => false,
143                'canUseAsRequestOverride' => false,
144                'preferenceWasCleared' => false,
145                'blockedBy' => null,
146                'groupCount' => 0,
147                'userCount' => 0,
148                'everyoneCount' => 0,
149            ],
150            [
151                'policyKey' => LegalInformationPolicy::KEY,
152                'effectiveValue' => $effectiveValue,
153                'sourceScope' => $sourceScope,
154            ],
155        );
156
157        return $resolvedPolicyStates;
158    }
159}