Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
94.74% covered (success)
94.74%
36 / 38
50.00% covered (danger)
50.00%
2 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
DavAccountResolver
94.59% covered (success)
94.59%
35 / 37
50.00% covered (danger)
50.00%
2 / 4
14.03
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
 resolveByUserId
92.86% covered (success)
92.86%
13 / 14
0.00% covered (danger)
0.00%
0 / 1
5.01
 listActiveAccounts
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
4
 resolveCredential
83.33% covered (warning)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
4.07
1<?php
2
3declare(strict_types=1);
4
5/** @license For full copyright and license information, please see the LICENSE.md file. */
6
7namespace App\Modules\Dav\Application\Service;
8
9defined('AMMONLY_APP') || exit('Direct script access is forbidden.');
10
11use App\Modules\Dav\Domain\Model\DavAccount;
12use PDO;
13
14/**
15 * DAV Account Resolver Application Service.
16 *
17 * Resolves user credentials and CalDAV/CardDAV collection parameters from system user records.
18 *
19 * @package App\Modules\Dav\Application\Service
20 */
21final readonly class DavAccountResolver
22{
23    private const string DEFAULT_BASE_URL = 'https://mail.ammonly.com/SOGo/dav';
24
25    /**
26     * DavAccountResolver constructor.
27     *
28     * @param PDO    $pdo           PDO database connection.
29     * @param string $tablePrefix   Optional database table prefix.
30     * @param string $baseUrl       Base DAV endpoint URL.
31     * @param string $defaultSecret Default mailbox authentication credential fallback.
32     */
33    public function __construct(
34        private PDO $pdo,
35        private string $tablePrefix = 'a_',
36        private string $baseUrl = self::DEFAULT_BASE_URL,
37        private string $defaultSecret = ''
38    ) {
39    }
40
41    /**
42     * Resolves a DavAccount for a given user ID.
43     *
44     * @param int $userId Local CRM user ID.
45     * @return DavAccount|null DavAccount object or null if user has no DAV account.
46     */
47    public function resolveByUserId(int $userId): ?DavAccount
48    {
49        $table = $this->tablePrefix . 'mod_users_records';
50        $sql = sprintf(
51            'SELECT `id`, `email`, `dav_account`, `has_dav_account` FROM `%s` WHERE `id` = :id LIMIT 1',
52            $table
53        );
54        $stmt = $this->pdo->prepare($sql);
55        $stmt->execute([':id' => $userId]);
56        $row = $stmt->fetch(PDO::FETCH_ASSOC);
57
58        if (!is_array($row) || ((int) ($row['has_dav_account'] ?? 0)) !== 1) {
59            return null;
60        }
61
62        $davEmail = (string) ($row['dav_account'] ?: $row['email']);
63        if ($davEmail === '') {
64            return null;
65        }
66
67        return new DavAccount($userId, $davEmail, $this->resolveCredential(), $this->baseUrl);
68    }
69
70    /**
71     * Retrieves all active users configured with an active DAV synchronization account.
72     *
73     * @return array<int, DavAccount> List of active DavAccount profiles.
74     */
75    public function listActiveAccounts(): array
76    {
77        $table = $this->tablePrefix . 'mod_users_records';
78        $sql = sprintf(
79            'SELECT `id`, `email`, `dav_account` FROM `%s` '
80            . 'WHERE `status` = "active" AND `special_access` = 1 '
81            . 'AND `has_dav_account` = 1 AND `dav_account` IS NOT NULL',
82            $table
83        );
84        $stmt = $this->pdo->query($sql);
85        $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
86
87        $accounts = [];
88        foreach ($rows as $row) {
89            $userId = (int) $row['id'];
90            $email = (string) ($row['dav_account'] ?: $row['email']);
91            if ($email !== '') {
92                $accounts[] = new DavAccount($userId, $email, $this->resolveCredential(), $this->baseUrl);
93            }
94        }
95
96        return $accounts;
97    }
98
99    /**
100     * Resolves fallback mailbox credential from property or environment.
101     *
102     * @return string Credential string.
103     */
104    private function resolveCredential(): string
105    {
106        if ($this->defaultSecret !== '') {
107            return $this->defaultSecret;
108        }
109
110        $envSecret = getenv('DAV_MAILBOX_SECRET');
111        if (is_string($envSecret) && $envSecret !== '') {
112            return $envSecret;
113        }
114
115        return '';
116    }
117}