RecoveryTokenRepository::createOptionsQuery()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 10
nc 2
nop 1
dl 0
loc 17
rs 9.9332
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Copyright 2022 SURFnet bv
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 *     http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
0 ignored issues
show
Coding Style introduced by
Missing @link tag in file comment
Loading history...
18
19
namespace Surfnet\StepupMiddleware\ApiBundle\Identity\Repository;
20
21
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
22
use Doctrine\DBAL\Types\Type;
23
use Doctrine\ORM\Query;
24
use Doctrine\Persistence\ManagerRegistry;
25
use Surfnet\Stepup\Identity\Value\IdentityId;
26
use Surfnet\StepupMiddleware\ApiBundle\Authorization\Filter\InstitutionAuthorizationRepositoryFilter;
27
use Surfnet\StepupMiddleware\ApiBundle\Authorization\Value\InstitutionAuthorizationContextInterface;
28
use Surfnet\StepupMiddleware\ApiBundle\Doctrine\Type\RecoveryTokenStatusType;
29
use Surfnet\StepupMiddleware\ApiBundle\Exception\RuntimeException;
30
use Surfnet\StepupMiddleware\ApiBundle\Identity\Entity\RecoveryToken;
31
use Surfnet\StepupMiddleware\ApiBundle\Identity\Query\RecoveryTokenQuery;
32
use Surfnet\StepupMiddleware\ApiBundle\Identity\Value\RecoveryTokenStatus;
0 ignored issues
show
Bug introduced by
The type Surfnet\StepupMiddleware...lue\RecoveryTokenStatus was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
33
34
/**
35
 * @extends ServiceEntityRepository<RecoveryToken>
36
 */
0 ignored issues
show
Coding Style introduced by
Missing @category tag in class comment
Loading history...
Coding Style introduced by
Missing @package tag in class comment
Loading history...
Coding Style introduced by
Missing @author tag in class comment
Loading history...
Coding Style introduced by
Missing @license tag in class comment
Loading history...
Coding Style introduced by
Missing @link tag in class comment
Loading history...
37
class RecoveryTokenRepository extends ServiceEntityRepository
38
{
39
    public function __construct(
40
        ManagerRegistry $registry,
41
        private readonly InstitutionAuthorizationRepositoryFilter $authorizationRepositoryFilter,
42
    ) {
43
        parent::__construct($registry, RecoveryToken::class);
44
    }
45
46
    public function save(RecoveryToken $entry): void
47
    {
48
        $entityManager = $this->getEntityManager();
49
        $entityManager->persist($entry);
50
        $entityManager->flush();
51
    }
52
53
    public function remove(RecoveryToken $recoveryToken): void
54
    {
55
        $this->getEntityManager()->remove($recoveryToken);
56
        $this->getEntityManager()->flush();
57
    }
58
59
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $query should have a doc-comment as per coding-style.
Loading history...
60
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
61
     * @SuppressWarnings(PHPMD.NPathComplexity)
62
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
63
    public function createSearchQuery(RecoveryTokenQuery $query): Query
64
    {
65
        $queryBuilder = $this->createQueryBuilder('rt');
66
67
        if ($query->authorizationContext instanceof InstitutionAuthorizationContextInterface) {
68
            // Modify query to filter on authorization context
69
            // We want to list all recovery tokens of the institution we are RA for.
70
            $this->authorizationRepositoryFilter->filter(
71
                $queryBuilder,
72
                $query->authorizationContext,
73
                'rt.institution',
74
                'iac',
75
            );
76
        }
77
        if ($query->identityId instanceof IdentityId) {
78
            $queryBuilder
79
                ->andWhere('rt.identityId = :identityId')
80
                ->setParameter('identityId', $query->identityId);
81
        }
82
        if ($query->type) {
83
            $queryBuilder
84
                ->andWhere('rt.type = :type')
85
                ->setParameter('type', $query->type);
86
        }
87
        if ($query->status) {
88
            $stringStatus = $query->status;
89
            if (!RecoveryTokenStatus::isValidStatus($stringStatus)) {
90
                throw new RuntimeException(
91
                    sprintf(
92
                        'Received invalid status "%s" in RecoveryTokenRepository::createSearchQuery',
93
                        $stringStatus,
94
                    ),
95
                );
96
            }
97
98
            // we need to resolve the string value to database value using the correct doctrine type. Normally this is
99
            // done by doctrine itself, however the queries PagerFanta creates somehow manages to mangle this...
100
            // so we do it by hand
101
            $doctrineType = Type::getType(RecoveryTokenStatusType::NAME);
102
            $secondFactorStatus = RecoveryTokenStatus::$stringStatus();
103
104
            $databaseValue = $doctrineType->convertToDatabaseValue(
105
                $secondFactorStatus,
106
                $this->getEntityManager()->getConnection()->getDatabasePlatform(),
107
            );
108
109
            $queryBuilder->andWhere('rt.status = :status')->setParameter('status', $databaseValue);
110
        }
111
        if ($query->name) {
112
            $queryBuilder
113
                ->andWhere('rt.name LIKE :name')
114
                ->setParameter('name', sprintf('%%%s%%', $query->name));
115
        }
116
        if ($query->email) {
117
            $queryBuilder
118
                ->andWhere('rt.email LIKE :email')
119
                ->setParameter('email', sprintf('%%%s%%', $query->email));
120
        }
121
        if ($query->institution) {
122
            $queryBuilder
123
                ->andWhere('rt.institution = :institution')
124
                ->setParameter('institution', $query->institution);
125
        }
126
        match ($query->orderBy) {
127
            'name', 'type', 'email', 'institution', 'status' => $queryBuilder->orderBy(
128
                sprintf('rt.%s', $query->orderBy),
129
                $query->orderDirection === 'desc' ? 'DESC' : 'ASC',
130
            ),
131
            default => $queryBuilder->getQuery(),
132
        };
133
134
        return $queryBuilder->getQuery();
135
    }
136
137
    public function createOptionsQuery(RecoveryTokenQuery $query): Query
138
    {
139
        $queryBuilder = $this->createQueryBuilder('sf')
140
            ->select('sf.institution')
141
            ->groupBy('sf.institution');
142
143
        if ($query->authorizationContext instanceof InstitutionAuthorizationContextInterface) {
144
            // Modify query to filter on authorization context
145
            // We want to list all second factors of the institution we are RA for.
146
            $this->authorizationRepositoryFilter->filter(
147
                $queryBuilder,
148
                $query->authorizationContext,
149
                'sf.institution',
150
                'iac',
151
            );
152
        }
153
        return $queryBuilder->getQuery();
154
    }
155
156
    public function removeByIdentity(IdentityId $identityId): void
157
    {
158
        $this->getEntityManager()->createQueryBuilder()
159
            ->delete($this->getEntityName(), 'rt')
160
            ->where('rt.identityId = :identityId')
161
            ->setParameter('identityId', $identityId->getIdentityId())
162
            ->getQuery()
163
            ->execute();
164
    }
165
}
166