RaCandidateRepository::createOptionsQuery()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 19
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 1
eloc 10
nc 1
nop 1
dl 0
loc 19
rs 9.9332
c 2
b 0
f 0
1
<?php
2
3
/**
4
 * Copyright 2014 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\ORM\Query;
23
use Doctrine\ORM\Query\Expr\Join;
24
use Doctrine\ORM\QueryBuilder;
25
use Doctrine\Persistence\ManagerRegistry;
26
use Surfnet\Stepup\Identity\Value\VettingType;
27
use Surfnet\StepupMiddleware\ApiBundle\Authorization\Filter\InstitutionAuthorizationRepositoryFilter;
28
use Surfnet\StepupMiddleware\ApiBundle\Configuration\Entity\InstitutionAuthorization;
29
use Surfnet\StepupMiddleware\ApiBundle\Identity\Entity\Identity;
30
use Surfnet\StepupMiddleware\ApiBundle\Identity\Entity\RaCandidate;
31
use Surfnet\StepupMiddleware\ApiBundle\Identity\Entity\RaListing;
32
use Surfnet\StepupMiddleware\ApiBundle\Identity\Entity\VettedSecondFactor;
33
use Surfnet\StepupMiddleware\ApiBundle\Identity\Query\RaCandidateQuery;
34
35
/**
36
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
37
 * @SuppressWarnings(PHPMD.TooManyPublicMethods)
38
 * @extends ServiceEntityRepository<RaCandidate>
0 ignored issues
show
Coding Style introduced by
Tag value for @extends tag indented incorrectly; expected 40 spaces but found 1
Loading history...
39
 */
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...
40
class RaCandidateRepository extends ServiceEntityRepository
41
{
42
    public function __construct(
43
        ManagerRegistry $registry,
44
        private readonly InstitutionAuthorizationRepositoryFilter $authorizationRepositoryFilter,
45
    ) {
46
        parent::__construct($registry, RaCandidate::class);
47
    }
48
49
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $query should have a doc-comment as per coding-style.
Loading history...
50
     * @return Query
51
     */
52
    public function createSearchQuery(RaCandidateQuery $query): Query
53
    {
54
        $queryBuilder = $this->getBaseQuery();
55
56
        // Modify query to filter on authorization:
57
        // For the RA candidates we want the identities that we could make RA. Because we then need to look at the
58
        // select_raa's we have to look at the institution of the candidate because that's the institution we could
59
        // select RA's from. Hence the 'rac.institution'.
60
        $this->authorizationRepositoryFilter->filter(
61
            $queryBuilder,
62
            $query->authorizationContext,
63
            'i.institution',
64
            'iac',
65
        );
66
67
        if ($query->institution) {
68
            $queryBuilder
69
                ->andWhere('i.institution = :institution')
70
                ->setParameter('institution', $query->institution);
71
        }
72
73
        if ($query->commonName !== '' && $query->commonName !== '0') {
74
            $queryBuilder
75
                ->andWhere('i.commonName LIKE :commonName')
76
                ->setParameter('commonName', sprintf('%%%s%%', $query->commonName));
77
        }
78
79
        if ($query->email !== '' && $query->email !== '0') {
80
            $queryBuilder
81
                ->andWhere('i.email LIKE :email')
82
                ->setParameter('email', sprintf('%%%s%%', $query->email));
83
        }
84
85
        if (isset($query->secondFactorTypes) && $query->secondFactorTypes !== []) {
86
            $queryBuilder
87
                ->andWhere('vsf.type IN (:secondFactorTypes)')
88
                ->setParameter('secondFactorTypes', $query->secondFactorTypes);
89
            // Self asserted tokens diminish the LoA never resulting in a valid
90
            // second factor candidate. Only on-premise and self-vetted tokens
91
            // can be a valid option.
92
            $queryBuilder
93
                ->andWhere('vsf.vettingType != :vettingType')
94
                ->setParameter('vettingType', VettingType::TYPE_SELF_ASSERTED_REGISTRATION);
95
        }
96
97
        if (!empty($query->raInstitution)) {
98
            $queryBuilder
99
                ->andWhere('a.raInstitution = :raInstitution')
100
                ->setParameter('raInstitution', $query->raInstitution);
101
        }
102
103
        $queryBuilder->groupBy('i.id');
104
105
        return $queryBuilder->getQuery();
106
    }
107
108
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $query should have a doc-comment as per coding-style.
Loading history...
109
     * @return Query
110
     */
111
    public function createOptionsQuery(RaCandidateQuery $query): Query
112
    {
113
        $queryBuilder = $this->getEntityManager()->createQueryBuilder()
114
            ->select('a.institution')
115
            ->from(InstitutionAuthorization::class, 'a')
116
            ->where("a.institutionRole = 'select_raa'");
117
118
        // Modify query to filter on authorization:
119
        // For the RA candidates we want the identities that we could make RA. Because we then need to look at the
120
        // select_raa's we have to look at the institution of the candidate because that's the institution we could
121
        // select RA's from. Hence the 'rac.institution'.
122
        $this->authorizationRepositoryFilter->filter(
123
            $queryBuilder,
124
            $query->authorizationContext,
125
            'a.institution',
126
            'iac',
127
        );
128
129
        return $queryBuilder->getQuery();
130
    }
131
132
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $identityId should have a doc-comment as per coding-style.
Loading history...
133
     * @return array|null
134
     */
135
    public function findOneByIdentityId(string $identityId): ?array
136
    {
137
        // Finds a single identity by its identity id. Returns the identity as an array
138
        $queryBuilder = $this->getBaseQuery()
139
            ->andWhere('i.id = :identityId')
140
            ->setParameter('identityId', $identityId)
141
            ->groupBy('i.id')
142
            ->orderBy('a.institution');
143
144
        return $queryBuilder->getQuery()->getOneOrNullResult();
145
    }
146
147
    /**
148
     * @return QueryBuilder
149
     */
150
    private function getBaseQuery(): QueryBuilder
0 ignored issues
show
Coding Style introduced by
Private method name "RaCandidateRepository::getBaseQuery" must be prefixed with an underscore
Loading history...
151
    {
152
        // Base query to get all allowed ra candidates
153
        $queryBuilder = $this->getEntityManager()->createQueryBuilder()
154
            ->select(
155
                'i.id as identity_id, i.institution, i.commonName as common_name, i.email, i.nameId AS name_id, a.institution AS ra_institution',
156
            )
157
            ->from(VettedSecondFactor::class, 'vsf')
158
            ->innerJoin(Identity::class, 'i', Join::WITH, "vsf.identityId = i.id")
159
            ->innerJoin(
160
                InstitutionAuthorization::class,
161
                'a',
162
                Join::WITH,
163
                "a.institutionRole = 'select_raa' AND a.institutionRelation = i.institution",
164
            );
165
166
        // Filter out candidates who are already ra
167
        // Todo: filter out SRAA's ?
168
        $subQuery = $this->getEntityManager()->createQueryBuilder()
169
            ->select('l')
170
            ->from(RaListing::class, "l")
171
            ->where("l.identityId = i.id AND l.raInstitution = a.institution");
172
173
        $queryBuilder->andWhere($queryBuilder->expr()->not($queryBuilder->expr()->exists($subQuery->getDQL())));
174
175
        return $queryBuilder;
176
    }
177
}
178