Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
29 / 29
100.00% covered (success)
100.00%
3 / 3
CRAP
100.00% covered (success)
100.00%
1 / 1
MailSenderStrategyPolicy
100.00% covered (success)
100.00%
29 / 29
100.00% covered (success)
100.00%
3 / 3
7
100.00% covered (success)
100.00%
1 / 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
100.00% covered (success)
100.00%
25 / 25
100.00% covered (success)
100.00%
1 / 1
5
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\MailSenderStrategy;
10
11use OCA\Libresign\Service\Policy\Contract\IPolicyDefinition;
12use OCA\Libresign\Service\Policy\Contract\IPolicyDefinitionProvider;
13use OCA\Libresign\Service\Policy\Model\PolicySpec;
14use OCA\Libresign\Service\Policy\Provider\Helper\PolicyKeyNormalizer;
15use OCP\Mail\Provider\IManager as IMailProviderManager;
16
17/**
18 * Controls which mail account LibreSign uses to send signature request
19 * notifications: the system mailer or the account of the requester.
20 */
21final class MailSenderStrategyPolicy implements IPolicyDefinitionProvider {
22    public const KEY = 'mail_sender_strategy';
23    public const SYSTEM_APP_CONFIG_KEY = self::KEY;
24
25    public const STRATEGY_SYSTEM = 'system';
26    public const STRATEGY_REQUESTER = 'requester';
27
28    private const STRATEGIES = [
29        self::STRATEGY_SYSTEM,
30        self::STRATEGY_REQUESTER,
31    ];
32
33    public function __construct(
34        private IMailProviderManager $mailProviderManager,
35    ) {
36    }
37
38    #[\Override]
39    public function keys(): array {
40        return [
41            self::KEY,
42        ];
43    }
44
45    #[\Override]
46    public function get(string|\BackedEnum $policyKey): IPolicyDefinition {
47        return match (PolicyKeyNormalizer::normalize($policyKey)) {
48            self::KEY => new PolicySpec(
49                key: self::KEY,
50                defaultSystemValue: self::STRATEGY_SYSTEM,
51                allowedValues: self::STRATEGIES,
52                normalizer: static fn (mixed $rawValue): string => strtolower(trim((string)$rawValue)),
53                validator: static function (mixed $value): void {
54                    if (!is_string($value) || !in_array($value, self::STRATEGIES, true)) {
55                        throw new \InvalidArgumentException('Invalid value for ' . self::KEY);
56                    }
57                },
58                appConfigKey: self::SYSTEM_APP_CONFIG_KEY,
59                supportsUserPreference: false,
60                supportsRequestOverride: false,
61                requestLifecycle: PolicySpec::LIFECYCLE_RUNTIME,
62                resolvedStateMeta: fn (): array => [
63                    'mailProviderAvailable' => $this->mailProviderManager->has(),
64                ],
65                supportedScopes: [PolicySpec::SCOPE_SYSTEM],
66                // The requester strategy can only be configured while a mail provider
67                // is available. Once stored, runtime resolution keeps the value and
68                // MailService falls back to the system mailer when the environment
69                // changes later (provider removed, account deleted, sending failure).
70                persistenceValidator: function (mixed $value): void {
71                    if ($value === self::STRATEGY_REQUESTER && !$this->mailProviderManager->has()) {
72                        throw new \InvalidArgumentException('The requester strategy requires an available mail provider');
73                    }
74                },
75            ),
76            default => throw new \InvalidArgumentException('Unknown policy key: ' . PolicyKeyNormalizer::normalize($policyKey)),
77        };
78    }
79}