Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
92.31% covered (success)
92.31%
36 / 39
62.50% covered (warning)
62.50%
5 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
FileAccessService
92.31% covered (success)
92.31%
36 / 39
62.50% covered (warning)
62.50%
5 / 8
20.18
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
 userCanAccessFileById
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 userCanAccessFileByNodeId
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 resolveUser
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 userCanAccessFile
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 userIsAssociatedSigner
92.31% covered (success)
92.31%
12 / 13
0.00% covered (danger)
0.00%
0 / 1
5.01
 signRequestMatchesUser
85.71% covered (warning)
85.71%
6 / 7
0.00% covered (danger)
0.00%
0 / 1
4.05
 userCanSignFile
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
2.06
1<?php
2
3declare(strict_types=1);
4/**
5 * SPDX-FileCopyrightText: 2024 LibreCode coop and contributors
6 * SPDX-License-Identifier: AGPL-3.0-or-later
7 */
8
9namespace OCA\Libresign\Service;
10
11use OCA\Libresign\Db\File as FileEntity;
12use OCA\Libresign\Db\FileMapper;
13use OCA\Libresign\Db\IdentifyMethod;
14use OCA\Libresign\Db\SignRequest;
15use OCA\Libresign\Db\SignRequestMapper;
16use OCA\Libresign\Enum\FileStatus;
17use OCA\Libresign\Enum\SignRequestStatus;
18use OCP\IUser;
19use OCP\IUserSession;
20
21class FileAccessService {
22    public function __construct(
23        private FileMapper $fileMapper,
24        private SignRequestMapper $signRequestMapper,
25        private SignFileService $signFileService,
26        private IUserSession $userSession,
27    ) {
28    }
29
30    public function userCanAccessFileById(int $fileId, ?IUser $user = null): bool {
31        $user = $this->resolveUser($user);
32        if (!$user) {
33            return false;
34        }
35
36        return $this->userCanAccessFile($this->fileMapper->getById($fileId), $user);
37    }
38
39    public function userCanAccessFileByNodeId(int $nodeId, ?IUser $user = null): bool {
40        $user = $this->resolveUser($user);
41        if (!$user) {
42            return false;
43        }
44
45        return $this->userCanAccessFile($this->fileMapper->getByNodeId($nodeId), $user);
46    }
47
48    private function resolveUser(?IUser $user): ?IUser {
49        return $user ?? $this->userSession->getUser();
50    }
51
52    private function userCanAccessFile(FileEntity $file, IUser $user): bool {
53        if ($file->getUserId() === $user->getUID()) {
54            return true;
55        }
56
57        if ($this->userIsAssociatedSigner($file, $user)) {
58            return true;
59        }
60
61        return $this->userCanSignFile($file, $user);
62    }
63
64    private function userIsAssociatedSigner(FileEntity $file, IUser $user): bool {
65        if ($file->getStatus() === FileStatus::DRAFT->value) {
66            return false;
67        }
68
69        $signRequests = array_values(array_filter(
70            $this->signRequestMapper->getByFileId($file->getId()),
71            static fn (SignRequest $signRequest): bool => $signRequest->getStatus() !== SignRequestStatus::DRAFT->value,
72        ));
73        if ($signRequests === []) {
74            return false;
75        }
76
77        $identifyMethods = $this->signRequestMapper->getIdentifyMethodsFromSigners($signRequests);
78        foreach ($signRequests as $signRequest) {
79            if ($this->signRequestMatchesUser($identifyMethods[$signRequest->getId()] ?? [], $user)) {
80                return true;
81            }
82        }
83
84        return false;
85    }
86
87    /**
88     * @param array<int|string, IdentifyMethod> $identifyMethods
89     */
90    private function signRequestMatchesUser(array $identifyMethods, IUser $user): bool {
91        return array_reduce($identifyMethods, function (bool $carry, IdentifyMethod $identifyMethod) use ($user): bool {
92            if ($identifyMethod->getIdentifierKey() === IdentifyMethodService::IDENTIFY_ACCOUNT) {
93                return $user->getUID() === $identifyMethod->getIdentifierValue();
94            }
95            if ($identifyMethod->getIdentifierKey() === IdentifyMethodService::IDENTIFY_EMAIL && $user->getEMailAddress()) {
96                return $user->getEMailAddress() === $identifyMethod->getIdentifierValue();
97            }
98            return $carry;
99        }, false);
100    }
101
102    private function userCanSignFile(FileEntity $file, IUser $user): bool {
103        try {
104            $this->signFileService->getSignRequestToSign($file, null, $user);
105            return true;
106        } catch (\Exception) {
107            return false;
108        }
109    }
110}