Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
54.55% covered (warning)
54.55%
30 / 55
72.73% covered (warning)
72.73%
8 / 11
CRAP
0.00% covered (danger)
0.00%
0 / 1
RdfLtiProviderRepository
54.55% covered (warning)
54.55%
30 / 55
72.73% covered (warning)
72.73%
8 / 11
72.68
0.00% covered (danger)
0.00%
0 / 1
 getRootClass
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 count
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 findAll
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 searchByLabel
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getProviders
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
3
 queryResources
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
4
 getLtiProviderFromResource
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 searchById
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
 searchByOauthKey
71.43% covered (warning)
71.43%
5 / 7
0.00% covered (danger)
0.00%
0 / 1
3.21
 searchByIssuer
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
20
 getLtiProviderFactory
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3/**
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; under version 2
7 * of the License (non-upgradable).
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 *
18 * Copyright (c) 2019 (original work) Open Assessment Technologies SA
19 */
20
21namespace oat\taoLti\models\classes\LtiProvider;
22
23use common_exception_Error as ErrorException;
24use common_exception_InvalidArgumentType as InvalidArgumentTypeException;
25use core_kernel_classes_Resource as RdfResource;
26use oat\generis\model\kernel\persistence\smoothsql\search\ComplexSearchService;
27use oat\generis\model\OntologyRdfs;
28use oat\tao\model\oauth\DataStore;
29use oat\tao\model\OntologyClassService;
30
31/**
32 * Service methods to manage the LTI provider business objects.
33 */
34class RdfLtiProviderRepository extends OntologyClassService implements LtiProviderRepositoryInterface
35{
36    public const CLASS_URI = 'http://www.tao.lu/Ontologies/TAOLTI.rdf#LTIProvider';
37
38    public const LTI_VERSION = 'http://www.tao.lu/Ontologies/TAOLTI.rdf#ltiVersion';
39    public const LTI_TOOL_CLIENT_ID = 'http://www.tao.lu/Ontologies/TAOLTI.rdf#toolClientId';
40    public const LTI_TOOL_IDENTIFIER = 'http://www.tao.lu/Ontologies/TAOLTI.rdf#toolIdentifier';
41    public const LTI_TOOL_NAME = 'http://www.tao.lu/Ontologies/TAOLTI.rdf#toolName';
42    public const LTI_TOOL_DEPLOYMENT_IDS = 'http://www.tao.lu/Ontologies/TAOLTI.rdf#toolDeploymentIds';
43    public const LTI_TOOL_AUDIENCE = 'http://www.tao.lu/Ontologies/TAOLTI.rdf#toolAudience';
44    // phpcs:disable Generic.Files.LineLength
45    public const LTI_TOOL_OIDC_LOGIN_INITATION_URL = 'http://www.tao.lu/Ontologies/TAOLTI.rdf#toolOidcLoginInitiationUrl';
46    // phpcs:enable Generic.Files.LineLength
47    public const LTI_TOOL_LAUNCH_URL = 'http://www.tao.lu/Ontologies/TAOLTI.rdf#toolLaunchUrl';
48
49    public const LTI_TOOL_JWKS_URL = 'http://www.tao.lu/Ontologies/TAOLTI.rdf#ToolJwksUrl';
50    public const LTI_TOOL_PUBLIC_KEY = 'http://www.tao.lu/Ontologies/TAOLTI.rdf#toolPublicKey';
51
52    public const LTI_V_11 = 'http://www.tao.lu/Ontologies/TAOLTI.rdf#lti1p1';
53    public const LTI_V_13 = 'http://www.tao.lu/Ontologies/TAOLTI.rdf#lti1p3';
54
55    public const DEFAULT_LTI_VERSION = self::LTI_V_11;
56
57    /**
58     * @inheritdoc
59     */
60    public function getRootClass()
61    {
62        return $this->getClass(self::CLASS_URI);
63    }
64
65    /**
66     * Returns the number of LtiProviders.
67     *
68     * @return int
69     */
70    public function count()
71    {
72        return $this->queryResources([], 'count', 0);
73    }
74
75    /**
76     * @inheritdoc
77     */
78    public function findAll(): array
79    {
80        return $this->getProviders();
81    }
82
83    /**
84     * @inheritdoc
85     */
86    public function searchByLabel(string $queryString): array
87    {
88        return $this->getProviders([OntologyRdfs::RDFS_LABEL => $queryString]);
89    }
90
91    /**
92     * Retrieves providers from rdf store corresponding to the given criteria.
93     *
94     * @return LtiProvider[]
95     */
96    private function getProviders(array $criteria = []): array
97    {
98        $resources = $this->queryResources($criteria, 'search', []);
99        $ltiProviders = [];
100
101        try {
102            foreach ($resources as $resource) {
103                $ltiProviders[] = $this->getLtiProviderFromResource($resource);
104            }
105        } catch (InvalidArgumentTypeException $exception) {
106            $this->logError('Unable to retrieve provider properties: ' . $exception->getMessage());
107
108            return [];
109        }
110
111        return $ltiProviders;
112    }
113
114    /**
115     * Retrieves resources from rdf store corresponding to the given criteria,
116     * hydrate the result with the $hydration method
117     * or returns $default on failure
118     *
119     * @param array $criteria
120     * @param string $hydration Hydration method ("search" for actual results, "count" for counting results)
121     * @param array|int $default default value to return on failure
122     *
123     * @return mixed
124     */
125    private function queryResources(array $criteria, $hydration, $default)
126    {
127        try {
128            /** @var ComplexSearchService $searchService */
129            $searchService = $this->getServiceLocator()->get(ComplexSearchService::SERVICE_ID);
130            $queryBuilder = $searchService->query();
131            $query = $searchService->searchType($queryBuilder, self::CLASS_URI, true);
132            if (count($criteria)) {
133                foreach ($criteria as $property => $value) {
134                    $query->add($property)->contains($value);
135                }
136            }
137            $queryBuilder->setCriteria($query);
138
139            return $searchService->getGateway()->$hydration($queryBuilder);
140        } catch (ErrorException $e) {
141            $this->logError('Unable to retrieve providers: ' . $e->getMessage());
142
143            return $default;
144        }
145    }
146
147    private function getLtiProviderFromResource(RdfResource $resource): LtiProvider
148    {
149        return $this->getLtiProviderFactory()->createFromResource($resource);
150    }
151
152    public function searchById(string $id): ?LtiProvider
153    {
154        $resource = $this->getResource($id);
155        if ($resource->exists()) {
156            $types = $resource->getTypes();
157            $type = reset($types);
158            if ($type->getUri() !== self::CLASS_URI) {
159                return null;
160            }
161            return $this->getLtiProviderFromResource($this->getResource($id));
162        }
163        return null;
164    }
165
166    public function searchByOauthKey(string $oauthKey): ?LtiProvider
167    {
168        $providers = $this->getProviders([DataStore::PROPERTY_OAUTH_KEY => $oauthKey]);
169        $count = count($providers);
170        if ($count === 0) {
171            return null;
172        }
173        if ($count > 1) {
174            $this->logWarning("Found $count LTI providers with the same oauthKey: '$oauthKey'");
175        }
176        return reset($providers);
177    }
178
179    public function searchByIssuer(string $issuer, string $clientId = null): ?LtiProvider
180    {
181        $criteria = [self::LTI_TOOL_AUDIENCE => $issuer];
182        if ($clientId !== null) {
183            $criteria[self::LTI_TOOL_CLIENT_ID] = $clientId;
184        }
185        $providers = $this->getProviders($criteria);
186        $count = count($providers);
187        if ($count === 0) {
188            return null;
189        }
190        if ($count > 1) {
191            $this->logWarning(sprintf(
192                'Found %d LTI provider with the same clientId: %s and audience: %s',
193                $count,
194                $clientId,
195                $issuer
196            ));
197        }
198        return reset($providers);
199    }
200
201    private function getLtiProviderFactory(): LtiProviderFactory
202    {
203        return $this->getServiceLocator()->get(LtiProviderFactory::class);
204    }
205}