Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
93.02% covered (success)
93.02%
40 / 43
50.00% covered (danger)
50.00%
3 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
ObserverProfileFilePolicyApplier
93.02% covered (success)
93.02%
40 / 43
50.00% covered (danger)
50.00%
3 / 6
17.10
0.00% covered (danger)
0.00%
0 / 1
 apply
91.67% covered (success)
91.67%
11 / 12
0.00% covered (danger)
0.00%
0 / 1
3.01
 sync
92.86% covered (success)
92.86%
13 / 14
0.00% covered (danger)
0.00%
0 / 1
6.01
 supportsCoreFlowSync
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 assertOverrideAllowed
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 storeObserverProfilePolicySnapshot
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 requestContainsObserver
90.00% covered (success)
90.00%
9 / 10
0.00% covered (danger)
0.00%
0 / 1
5.03
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\FilePolicy;
10
11use OCA\Libresign\Db\File as FileEntity;
12use OCA\Libresign\Enum\ParticipantRole;
13use OCA\Libresign\Service\Policy\AbstractFilePolicyApplier;
14use OCA\Libresign\Service\Policy\Model\ResolvedPolicy;
15use OCA\Libresign\Service\Policy\Provider\ObserverProfile\ObserverProfilePolicy;
16use OCA\Libresign\Service\Policy\Provider\ObserverProfile\ObserverProfilePolicyValue;
17use OCP\IUser;
18
19final class ObserverProfileFilePolicyApplier extends AbstractFilePolicyApplier {
20    #[\Override]
21    public function apply(FileEntity $file, array $data): void {
22        $user = ($data['userManager'] ?? null) instanceof IUser ? $data['userManager'] : null;
23        $requestOverrides = $this->extractSinglePolicyOverride(
24            $data,
25            ObserverProfilePolicy::KEY,
26            ObserverProfilePolicyValue::normalize(...),
27        );
28        $activeContext = $this->extractActiveContext($data);
29        $resolvedPolicy = $activeContext === null
30            ? $this->policyService->resolveForUser(ObserverProfilePolicy::KEY, $user, $requestOverrides)
31            : $this->policyService->resolveForUser(ObserverProfilePolicy::KEY, $user, $requestOverrides, $activeContext);
32        $this->assertOverrideAllowed($requestOverrides, $resolvedPolicy);
33        $this->storeObserverProfilePolicySnapshot($file, $resolvedPolicy);
34    }
35
36    #[\Override]
37    public function sync(FileEntity $file, array $data): void {
38        if (!$this->requestContainsObserver($data)) {
39            return;
40        }
41
42        if (ObserverProfilePolicyValue::getSnapshotEffectiveValue($file) === true) {
43            return;
44        }
45
46        $activeContext = $this->extractActiveContext($data);
47        $resolvedPolicy = $activeContext === null
48            ? $this->policyService->resolveForUserId(ObserverProfilePolicy::KEY, $file->getUserId(), [])
49            : $this->policyService->resolveForUserId(ObserverProfilePolicy::KEY, $file->getUserId(), [], $activeContext);
50        if (!ObserverProfilePolicyValue::normalize($resolvedPolicy->getEffectiveValue())) {
51            return;
52        }
53
54        $metadataBeforeUpdate = $file->getMetadata() ?? [];
55        $this->storeObserverProfilePolicySnapshot($file, $resolvedPolicy);
56        if (($file->getMetadata() ?? []) !== $metadataBeforeUpdate) {
57            $this->fileService->update($file);
58        }
59    }
60
61    #[\Override]
62    public function supportsCoreFlowSync(): bool {
63        return true;
64    }
65
66    /** @param array<string, mixed> $requestOverrides */
67    private function assertOverrideAllowed(array $requestOverrides, ResolvedPolicy $resolvedPolicy): void {
68        $this->assertRequestOverrideAllowed($requestOverrides, $resolvedPolicy, 'Observer profile override is blocked by %s.');
69    }
70
71    private function storeObserverProfilePolicySnapshot(FileEntity $file, ResolvedPolicy $resolvedPolicy): void {
72        parent::storePolicySnapshot(
73            $file,
74            $resolvedPolicy,
75            ObserverProfilePolicyValue::normalize($resolvedPolicy->getEffectiveValue()),
76        );
77    }
78
79    /** @param array<string, mixed> $data */
80    private function requestContainsObserver(array $data): bool {
81        $signers = $data['signers'] ?? null;
82        if (!is_array($signers)) {
83            return false;
84        }
85
86        foreach ($signers as $signer) {
87            if (!is_array($signer)) {
88                continue;
89            }
90
91            $roleValue = $signer['participantRole'] ?? ParticipantRole::SIGNER->value;
92            if ($roleValue === ParticipantRole::OBSERVER->value) {
93                return true;
94            }
95        }
96
97        return false;
98    }
99}