Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 103 |
|
0.00% |
0 / 10 |
CRAP | |
0.00% |
0 / 1 |
Users | |
0.00% |
0 / 103 |
|
0.00% |
0 / 10 |
756 | |
0.00% |
0 / 1 |
getParametersRequirements | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
getGuardedProperties | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getParametersAliases | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
2 | |||
get | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
put | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
delete | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
post | |
0.00% |
0 / 38 |
|
0.00% |
0 / 1 |
72 | |||
validateParameters | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
6 | |||
processRoles | |
0.00% |
0 / 23 |
|
0.00% |
0 / 1 |
42 | |||
processLanguages | |
0.00% |
0 / 19 |
|
0.00% |
0 / 1 |
30 |
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 | */ |
21 | |
22 | namespace oat\tao\controller\api; |
23 | |
24 | use common_Exception; |
25 | use common_exception_Error; |
26 | use common_exception_MethodNotAllowed; |
27 | use common_exception_MissingParameter; |
28 | use common_exception_RestApi; |
29 | use common_exception_ValidationFailed; |
30 | use common_Utils; |
31 | use core_kernel_classes_Resource; |
32 | use core_kernel_users_Exception; |
33 | use oat\generis\model\user\UserRdf; |
34 | use oat\oatbox\service\ServiceManager; |
35 | use tao_actions_CommonRestModule; |
36 | use tao_models_classes_LanguageService; |
37 | use tao_models_classes_RoleService; |
38 | use tao_models_classes_UserService; |
39 | use oat\generis\Helper\UserHashForEncryption; |
40 | |
41 | /** |
42 | * @OA\Post( |
43 | * path="tao/api/users", |
44 | * summary="Create new user", |
45 | * @OA\RequestBody( |
46 | * @OA\MediaType( |
47 | * mediaType="application/x-www-form-urlencoded", |
48 | * @OA\Schema(ref="#/components/schemas/tao.User.New") |
49 | * ) |
50 | * ), |
51 | * @OA\Response( |
52 | * response="200", |
53 | * description="User created", |
54 | * @OA\JsonContent(ref="#/components/schemas/tao.CommonRestModule.CreatedResourceResponse") |
55 | * ), |
56 | * @OA\Response( |
57 | * response="400", |
58 | * description="Invalid request data", |
59 | * @OA\JsonContent(ref="#/components/schemas/tao.RestTrait.FailureResponse") |
60 | * ) |
61 | * ) |
62 | */ |
63 | class Users extends tao_actions_CommonRestModule |
64 | { |
65 | /** |
66 | * @OA\Schema( |
67 | * schema="tao.User.New", |
68 | * type="object", |
69 | * allOf={ |
70 | * @OA\Schema(ref="#/components/schemas/tao.GenerisClass.Search"), |
71 | * @OA\Schema(ref="#/components/schemas/tao.User.Update") |
72 | * }, |
73 | * @OA\Property( |
74 | * property="login", |
75 | * type="string", |
76 | * description="Login" |
77 | * ), |
78 | * required={"login", "password", "userLanguage", "roles"} |
79 | * ) |
80 | * @OA\Schema( |
81 | * schema="tao.User.Update", |
82 | * type="object", |
83 | * @OA\Property( |
84 | * property="login", |
85 | * type="string", |
86 | * description="Login" |
87 | * ), |
88 | * @OA\Property( |
89 | * property="password", |
90 | * type="string", |
91 | * description="Password" |
92 | * ), |
93 | * @OA\Property( |
94 | * property="userLanguage", |
95 | * type="string", |
96 | * description="Interface language uri" |
97 | * ), |
98 | * @OA\Property( |
99 | * property="defaultLanguage", |
100 | * type="string", |
101 | * description="Default language uri" |
102 | * ), |
103 | * @OA\Property( |
104 | * property="firstName", |
105 | * type="string", |
106 | * description="First name" |
107 | * ), |
108 | * @OA\Property( |
109 | * property="lastName", |
110 | * type="string", |
111 | * description="Last name" |
112 | * ), |
113 | * @OA\Property( |
114 | * property="mail", |
115 | * type="string", |
116 | * description="Email" |
117 | * ), |
118 | * @OA\Property( |
119 | * property="roles", |
120 | * type="string", |
121 | * description="List of roles (URIs)" |
122 | * ) |
123 | * ) |
124 | */ |
125 | |
126 | /** |
127 | * Optional Requirements for parameters to be sent on every service |
128 | */ |
129 | protected function getParametersRequirements() |
130 | { |
131 | return [ |
132 | 'post' => ['login', 'password', 'userLanguage', 'roles'] |
133 | ]; |
134 | } |
135 | |
136 | /** |
137 | * @return array |
138 | */ |
139 | protected function getGuardedProperties() |
140 | { |
141 | return ['login', 'password', 'roles', 'type']; |
142 | } |
143 | |
144 | /** |
145 | * @return array |
146 | */ |
147 | protected function getParametersAliases() |
148 | { |
149 | return array_merge(parent::getParametersAliases(), [ |
150 | 'login' => UserRdf::PROPERTY_LOGIN, |
151 | 'password' => UserRdf::PROPERTY_PASSWORD, |
152 | 'userLanguage' => UserRdf::PROPERTY_UILG, |
153 | 'defaultLanguage' => UserRdf::PROPERTY_DEFLG, |
154 | 'firstName' => UserRdf::PROPERTY_FIRSTNAME, |
155 | 'lastName' => UserRdf::PROPERTY_LASTNAME, |
156 | 'mail' => UserRdf::PROPERTY_MAIL, |
157 | 'roles' => UserRdf::PROPERTY_ROLES |
158 | ]); |
159 | } |
160 | |
161 | /** |
162 | * @param null $uri |
163 | * @return void |
164 | * @throws \common_exception_NotImplemented |
165 | */ |
166 | public function get($uri = null) |
167 | { |
168 | $this->returnFailure(new common_exception_RestApi('Not implemented')); |
169 | } |
170 | |
171 | /** |
172 | * @param string $uri |
173 | * @return void |
174 | * @throws \common_exception_NotImplemented |
175 | */ |
176 | public function put($uri) |
177 | { |
178 | $this->returnFailure(new common_exception_RestApi('Not implemented')); |
179 | } |
180 | |
181 | /** |
182 | * @param string $uri |
183 | * @return void |
184 | * @throws \common_exception_NotImplemented |
185 | */ |
186 | public function delete($uri = null) |
187 | { |
188 | $this->returnFailure(new common_exception_RestApi('Not implemented')); |
189 | } |
190 | |
191 | /** |
192 | * @return void |
193 | * @throws common_Exception |
194 | */ |
195 | public function post() |
196 | { |
197 | /** @var tao_models_classes_UserService $userService */ |
198 | $userService = ServiceManager::getServiceManager()->get(tao_models_classes_UserService::SERVICE_ID); |
199 | |
200 | if (!$userService->getOption(tao_models_classes_UserService::OPTION_ALLOW_API)) { |
201 | $this->returnFailure(new common_exception_RestApi((new common_exception_MethodNotAllowed())->getMessage())); |
202 | return; |
203 | } |
204 | |
205 | try { |
206 | $parameters = $this->getParameters(); |
207 | $this->validateParameters($parameters); |
208 | |
209 | $roles = $this->processRoles($parameters); |
210 | $login = $parameters[UserRdf::PROPERTY_LOGIN]; |
211 | $plainPassword = $parameters[UserRdf::PROPERTY_PASSWORD]; |
212 | unset($parameters[UserRdf::PROPERTY_PASSWORD]); |
213 | |
214 | $guarded = array_intersect_key($this->getParametersAliases(), array_flip($this->getGuardedProperties())); |
215 | $parameters = array_filter($parameters, static function ($key) use ($guarded) { |
216 | return !in_array($key, $guarded, true); |
217 | }, ARRAY_FILTER_USE_KEY); |
218 | |
219 | $this->processLanguages($parameters); |
220 | |
221 | /** @var core_kernel_classes_Resource $user */ |
222 | $user = $userService->addUser($login, $plainPassword, $this->getResource(array_shift($roles))); |
223 | |
224 | foreach ($roles as $role) { |
225 | $userService->attachRole($user, $this->getResource($role)); |
226 | } |
227 | |
228 | $userService->attachProperties($user, $parameters); |
229 | |
230 | $userService->triggerUpdatedEvent( |
231 | $user, |
232 | [UserRdf::PROPERTY_PASSWORD => $user->getProperty(UserRdf::PROPERTY_PASSWORD)], |
233 | UserHashForEncryption::hash($plainPassword) |
234 | ); |
235 | |
236 | $this->returnSuccess([ |
237 | 'success' => true, |
238 | 'uri' => $user->getUri(), |
239 | ], false); |
240 | } catch (common_exception_MissingParameter $e) { |
241 | $this->returnFailure(new common_exception_RestApi($e->getMessage())); |
242 | } catch (common_exception_ValidationFailed $e) { |
243 | $this->returnFailure(new common_exception_RestApi($e->getMessage())); |
244 | } catch (common_exception_Error $e) { |
245 | $this->returnFailure(new common_exception_RestApi($e->getMessage())); |
246 | } catch (core_kernel_users_Exception $e) { |
247 | $this->returnFailure(new common_exception_RestApi($e->getMessage())); |
248 | } catch (common_exception_RestApi $e) { |
249 | $this->returnFailure($e); |
250 | } |
251 | } |
252 | |
253 | /** |
254 | * @param array $parameters |
255 | * @throws common_exception_ValidationFailed |
256 | */ |
257 | protected function validateParameters(array $parameters) |
258 | { |
259 | if (empty($parameters[UserRdf::PROPERTY_LOGIN])) { |
260 | throw new common_exception_ValidationFailed( |
261 | null, |
262 | // phpcs:disable Generic.Files.LineLength |
263 | __("Validation for field '%s' has failed. Should not be empty", $this->reverseSearchAlias(UserRdf::PROPERTY_LOGIN)) |
264 | // phpcs:enable Generic.Files.LineLength |
265 | ); |
266 | } |
267 | } |
268 | |
269 | /** |
270 | * @param array $parameters |
271 | * @return array |
272 | * @throws common_exception_MissingParameter |
273 | * @throws common_exception_ValidationFailed |
274 | */ |
275 | protected function processRoles(array $parameters) |
276 | { |
277 | $roles = $parameters[UserRdf::PROPERTY_ROLES]; |
278 | |
279 | if (!is_array($roles)) { |
280 | throw new common_exception_ValidationFailed( |
281 | null, |
282 | __("Validation for field '%s' has failed. List of values expected", 'roles') |
283 | ); |
284 | } |
285 | |
286 | if (!count($roles)) { |
287 | throw new common_exception_MissingParameter('roles'); |
288 | } |
289 | |
290 | $roleService = tao_models_classes_RoleService::singleton(); |
291 | |
292 | foreach ($roles as $role) { |
293 | if (!common_Utils::isUri($role)) { |
294 | throw new common_exception_ValidationFailed( |
295 | null, |
296 | // phpcs:disable Generic.Files.LineLength |
297 | __("Validation for field '%s' has failed. Valid URI expected. Given: %s", 'roles', $role) |
298 | // phpcs:enable Generic.Files.LineLength |
299 | ); |
300 | } |
301 | if (!array_key_exists($role, $roleService->getAllRoles())) { |
302 | throw new common_exception_ValidationFailed( |
303 | null, |
304 | // phpcs:disable Generic.Files.LineLength |
305 | __("Validation for field '%s' has failed. Valid role expected. Given: %s", 'roles', $role) |
306 | // phpcs:enable Generic.Files.LineLength |
307 | ); |
308 | } |
309 | } |
310 | |
311 | return $roles; |
312 | } |
313 | |
314 | /** |
315 | * @param array $parameters |
316 | * @throws common_exception_ValidationFailed |
317 | * @throws common_exception_Error |
318 | */ |
319 | protected function processLanguages(array $parameters) |
320 | { |
321 | $uriProperties = array_intersect_key( |
322 | $this->getParametersAliases(), |
323 | array_flip(['userLanguage', 'defaultLanguage']) |
324 | ); |
325 | |
326 | foreach ($parameters as $key => $value) { |
327 | if (!in_array($key, $uriProperties, true)) { |
328 | continue; |
329 | } |
330 | |
331 | if (!common_Utils::isUri($value)) { |
332 | throw new common_exception_ValidationFailed( |
333 | null, |
334 | // phpcs:disable Generic.Files.LineLength |
335 | __("Validation for field '%s' has failed. Valid URI expected", array_search($key, $uriProperties, true)) |
336 | // phpcs:enable Generic.Files.LineLength |
337 | ); |
338 | } |
339 | |
340 | if (!tao_models_classes_LanguageService::getExistingLanguageUri($value)) { |
341 | throw new common_exception_ValidationFailed( |
342 | null, |
343 | // phpcs:disable Generic.Files.LineLength |
344 | __("Validation for field '%s' has failed. Language does not exist in the system", array_search($key, $uriProperties, true)) |
345 | // phpcs:enable Generic.Files.LineLength |
346 | ); |
347 | } |
348 | } |
349 | } |
350 | } |