Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
80.56% covered (warning)
80.56%
29 / 36
50.00% covered (danger)
50.00%
2 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
RequestSignGroupsPolicyGuard
80.56% covered (warning)
80.56%
29 / 36
50.00% covered (danger)
50.00%
2 / 4
19.12
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
 assertUserScopeSupported
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 normalizeManagedValue
91.67% covered (success)
91.67%
22 / 24
0.00% covered (danger)
0.00%
0 / 1
11.07
 resolveAllowedGroupIdsForActor
37.50% covered (danger)
37.50%
3 / 8
0.00% covered (danger)
0.00%
0 / 1
5.20
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\RequestSignGroups;
10
11use OCA\Libresign\Service\Policy\PolicyAuthorizationService;
12use OCP\Group\ISubAdmin;
13use OCP\IGroup;
14use OCP\IGroupManager;
15use OCP\IL10N;
16use OCP\IUser;
17use OCP\IUserSession;
18
19final class RequestSignGroupsPolicyGuard {
20    public function __construct(
21        private IL10N $l10n,
22        private IUserSession $userSession,
23        private IGroupManager $groupManager,
24        private ISubAdmin $subAdmin,
25        private PolicyAuthorizationService $policyAuthorizationService,
26    ) {
27    }
28
29    public function assertUserScopeSupported(string $policyKey): void {
30        if ($policyKey !== RequestSignGroupsPolicy::KEY) {
31            return;
32        }
33
34        // TRANSLATORS Error shown when trying to save the request-sign-groups policy at user scope, which is not supported.
35        throw new \InvalidArgumentException($this->l10n->t('User-level scope is not supported for this policy'));
36    }
37
38    public function normalizeManagedValue(string $policyKey, mixed $value, bool $allowNullReset = false, ?string $requiredGroupId = null): mixed {
39        if ($policyKey !== RequestSignGroupsPolicy::KEY) {
40            return $value;
41        }
42
43        if ($allowNullReset && $value === null) {
44            return null;
45        }
46
47        $user = $this->userSession->getUser();
48        if (!$user instanceof IUser) {
49            // TRANSLATORS Permission error shown when the current user is not allowed to manage the request-sign-groups policy.
50            throw new \InvalidArgumentException($this->l10n->t('Not allowed to manage this policy'));
51        }
52
53        $decodedPolicy = RequestSignGroupsPolicyValue::decodePolicy($value);
54        $allowGroupIds = $decodedPolicy['allowGroups'];
55        $denyGroupIds = $decodedPolicy['denyGroups'];
56
57        if ($allowGroupIds === []) {
58            // TRANSLATORS Validation error shown when saving the request-sign-groups policy without any authorized group.
59            throw new \InvalidArgumentException($this->l10n->t('At least one authorized group is required'));
60        }
61
62        $isSystemAdmin = $this->groupManager->isAdmin($user->getUID());
63        if (!$isSystemAdmin
64            && is_string($requiredGroupId)
65            && trim($requiredGroupId) !== ''
66            && !in_array($requiredGroupId, $allowGroupIds, true)) {
67            // TRANSLATORS Validation error shown when a group administrator tries to remove their own managed group from the request-sign-groups rule.
68            throw new \InvalidArgumentException($this->l10n->t('You cannot remove your managed group from this rule'));
69        }
70
71        $allowedGroupIds = $this->resolveAllowedGroupIdsForActor($user);
72        $groupsToValidate = array_values(array_unique(array_merge($allowGroupIds, $denyGroupIds)));
73        $unknownGroupIds = array_values(array_diff($groupsToValidate, $allowedGroupIds));
74        if ($unknownGroupIds !== []) {
75            // TRANSLATORS Permission error shown when selected groups are outside the current administrator scope for the request-sign-groups policy.
76            throw new \InvalidArgumentException($this->l10n->t('One or more selected groups are not allowed for your administration scope'));
77        }
78
79        return RequestSignGroupsPolicyValue::encode($decodedPolicy);
80    }
81
82    /** @return list<string> */
83    private function resolveAllowedGroupIdsForActor(IUser $user): array {
84        if ($this->groupManager->isAdmin($user->getUID())) {
85            return array_values(array_map(
86                static fn (IGroup $group): string => $group->getGID(),
87                $this->groupManager->search(''),
88            ));
89        }
90
91        if (!$this->subAdmin->isSubAdmin($user)) {
92            return [];
93        }
94
95        return $this->policyAuthorizationService->getManageablePolicyGroupIds($user);
96    }
97}