|
1
|
|
|
<?php |
|
2
|
|
|
/** |
|
3
|
|
|
* sysPass |
|
4
|
|
|
* |
|
5
|
|
|
* @author nuxsmin |
|
6
|
|
|
* @link https://syspass.org |
|
7
|
|
|
* @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org |
|
8
|
|
|
* |
|
9
|
|
|
* This file is part of sysPass. |
|
10
|
|
|
* |
|
11
|
|
|
* sysPass is free software: you can redistribute it and/or modify |
|
12
|
|
|
* it under the terms of the GNU General Public License as published by |
|
13
|
|
|
* the Free Software Foundation, either version 3 of the License, or |
|
14
|
|
|
* (at your option) any later version. |
|
15
|
|
|
* |
|
16
|
|
|
* sysPass is distributed in the hope that it will be useful, |
|
17
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
18
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
19
|
|
|
* GNU General Public License for more details. |
|
20
|
|
|
* |
|
21
|
|
|
* You should have received a copy of the GNU General Public License |
|
22
|
|
|
* along with sysPass. If not, see <http://www.gnu.org/licenses/>. |
|
23
|
|
|
*/ |
|
24
|
|
|
|
|
25
|
|
|
namespace SP\Services\Api; |
|
26
|
|
|
|
|
27
|
|
|
use Defuse\Crypto\Exception\CryptoException; |
|
28
|
|
|
use SP\Core\Acl\ActionsInterface; |
|
29
|
|
|
use SP\Core\Crypt\Hash; |
|
30
|
|
|
use SP\Core\Crypt\Vault; |
|
31
|
|
|
use SP\Core\Exceptions\InvalidClassException; |
|
32
|
|
|
use SP\DataModel\AuthTokenData; |
|
33
|
|
|
use SP\Modules\Api\Controllers\Help\HelpInterface; |
|
34
|
|
|
use SP\Repositories\Track\TrackRequest; |
|
35
|
|
|
use SP\Services\AuthToken\AuthTokenService; |
|
36
|
|
|
use SP\Services\Service; |
|
37
|
|
|
use SP\Services\ServiceException; |
|
38
|
|
|
use SP\Services\Track\TrackService; |
|
39
|
|
|
use SP\Services\User\UserService; |
|
40
|
|
|
use SP\Services\UserProfile\UserProfileService; |
|
41
|
|
|
use SP\Util\Filter; |
|
42
|
|
|
|
|
43
|
|
|
/** |
|
44
|
|
|
* Class ApiService |
|
45
|
|
|
* |
|
46
|
|
|
* @package SP\Services\ApiService |
|
47
|
|
|
*/ |
|
48
|
|
|
final class ApiService extends Service |
|
49
|
|
|
{ |
|
50
|
|
|
/** |
|
51
|
|
|
* @var AuthTokenService |
|
52
|
|
|
*/ |
|
53
|
|
|
private $authTokenService; |
|
54
|
|
|
/** |
|
55
|
|
|
* @var TrackService |
|
56
|
|
|
*/ |
|
57
|
|
|
private $trackService; |
|
58
|
|
|
/** |
|
59
|
|
|
* @var ApiRequest |
|
60
|
|
|
*/ |
|
61
|
|
|
private $apiRequest; |
|
62
|
|
|
/** |
|
63
|
|
|
* @var TrackRequest |
|
64
|
|
|
*/ |
|
65
|
|
|
private $trackRequest; |
|
66
|
|
|
/** |
|
67
|
|
|
* @var AuthTokenData |
|
68
|
|
|
*/ |
|
69
|
|
|
private $authTokenData; |
|
70
|
|
|
/** |
|
71
|
|
|
* @var HelpInterface |
|
72
|
|
|
*/ |
|
73
|
|
|
private $helpClass; |
|
74
|
|
|
/** |
|
75
|
|
|
* @var bool |
|
76
|
|
|
*/ |
|
77
|
|
|
private $initialized = false; |
|
78
|
|
|
|
|
79
|
|
|
/** |
|
80
|
|
|
* Sets up API |
|
81
|
|
|
* |
|
82
|
|
|
* @param $actionId |
|
83
|
|
|
* |
|
84
|
|
|
* @throws ServiceException |
|
85
|
|
|
* @throws \SP\Core\Exceptions\SPException |
|
86
|
|
|
* @throws \Exception |
|
87
|
|
|
*/ |
|
88
|
|
|
public function setup($actionId) |
|
89
|
|
|
{ |
|
90
|
|
|
$this->initialized = false; |
|
91
|
|
|
$this->apiRequest = $this->dic->get(ApiRequest::class); |
|
92
|
|
|
|
|
93
|
|
|
if ($this->trackService->checkTracking($this->trackRequest)) { |
|
94
|
|
|
$this->addTracking(); |
|
95
|
|
|
|
|
96
|
|
|
throw new ServiceException( |
|
97
|
|
|
__u('Intentos excedidos'), |
|
98
|
|
|
ServiceException::ERROR, |
|
99
|
|
|
null, |
|
100
|
|
|
JsonRpcResponse::INTERNAL_ERROR |
|
101
|
|
|
); |
|
102
|
|
|
} |
|
103
|
|
|
|
|
104
|
|
|
$this->authTokenData = $this->authTokenService->getTokenByToken($actionId, $this->getParam('authToken')); |
|
|
|
|
|
|
105
|
|
|
|
|
106
|
|
|
if ($this->authTokenData->getActionId() !== $actionId) { |
|
107
|
|
|
$this->accessDenied(); |
|
108
|
|
|
} |
|
109
|
|
|
|
|
110
|
|
|
$this->setupUser(); |
|
111
|
|
|
|
|
112
|
|
|
if ($actionId === ActionsInterface::ACCOUNT_VIEW_PASS |
|
113
|
|
|
|| $actionId === ActionsInterface::ACCOUNT_CREATE |
|
114
|
|
|
) { |
|
115
|
|
|
$this->context->setTrasientKey('_masterpass', $this->getMasterPassFromVault()); |
|
116
|
|
|
} |
|
117
|
|
|
|
|
118
|
|
|
$this->initialized = true; |
|
119
|
|
|
} |
|
120
|
|
|
|
|
121
|
|
|
/** |
|
122
|
|
|
* Añadir un seguimiento |
|
123
|
|
|
* |
|
124
|
|
|
* @throws ServiceException |
|
125
|
|
|
*/ |
|
126
|
|
|
private function addTracking() |
|
127
|
|
|
{ |
|
128
|
|
|
try { |
|
129
|
|
|
$this->trackService->add($this->trackRequest); |
|
130
|
|
|
} catch (\Exception $e) { |
|
131
|
|
|
throw new ServiceException( |
|
132
|
|
|
__u('Error interno'), |
|
133
|
|
|
ServiceException::ERROR, |
|
134
|
|
|
null, |
|
135
|
|
|
JsonRpcResponse::INTERNAL_ERROR |
|
136
|
|
|
); |
|
137
|
|
|
} |
|
138
|
|
|
} |
|
139
|
|
|
|
|
140
|
|
|
/** |
|
141
|
|
|
* Devolver el valor de un parámetro |
|
142
|
|
|
* |
|
143
|
|
|
* @param string $param |
|
144
|
|
|
* @param bool $required Si es requerido |
|
145
|
|
|
* @param mixed $default Valor por defecto |
|
146
|
|
|
* |
|
147
|
|
|
* @return mixed |
|
148
|
|
|
* @throws ServiceException |
|
149
|
|
|
*/ |
|
150
|
|
|
public function getParam($param, $required = false, $default = null) |
|
151
|
|
|
{ |
|
152
|
|
|
if ($this->apiRequest === null |
|
153
|
|
|
|| ($required && !$this->apiRequest->exists($param))) { |
|
154
|
|
|
throw new ServiceException( |
|
155
|
|
|
__u('Parámetros incorrectos'), |
|
156
|
|
|
ServiceException::ERROR, |
|
157
|
|
|
$this->getHelp($this->apiRequest->getMethod()), |
|
|
|
|
|
|
158
|
|
|
JsonRpcResponse::INVALID_PARAMS |
|
159
|
|
|
); |
|
160
|
|
|
} |
|
161
|
|
|
|
|
162
|
|
|
return $this->apiRequest->get($param, $default); |
|
163
|
|
|
} |
|
164
|
|
|
|
|
165
|
|
|
/** |
|
166
|
|
|
* Devuelve la ayuda para una acción |
|
167
|
|
|
* |
|
168
|
|
|
* @param string $action |
|
169
|
|
|
* |
|
170
|
|
|
* @return array |
|
171
|
|
|
*/ |
|
172
|
|
|
public function getHelp($action) |
|
173
|
|
|
{ |
|
174
|
|
|
if ($this->helpClass !== null) { |
|
175
|
|
|
return call_user_func([$this->helpClass, 'getHelpFor'], $action); |
|
176
|
|
|
} |
|
177
|
|
|
|
|
178
|
|
|
return []; |
|
179
|
|
|
} |
|
180
|
|
|
|
|
181
|
|
|
/** |
|
182
|
|
|
* @throws ServiceException |
|
183
|
|
|
*/ |
|
184
|
|
|
private function accessDenied() |
|
185
|
|
|
{ |
|
186
|
|
|
$this->addTracking(); |
|
187
|
|
|
|
|
188
|
|
|
throw new ServiceException( |
|
189
|
|
|
__u('Acceso no permitido'), |
|
190
|
|
|
ServiceException::ERROR, |
|
191
|
|
|
null, |
|
192
|
|
|
JsonRpcResponse::INTERNAL_ERROR |
|
193
|
|
|
); |
|
194
|
|
|
} |
|
195
|
|
|
|
|
196
|
|
|
/** |
|
197
|
|
|
* Sets up user's data in context and performs some user checks |
|
198
|
|
|
* |
|
199
|
|
|
* @throws \SP\Core\Exceptions\SPException |
|
200
|
|
|
*/ |
|
201
|
|
|
private function setupUser() |
|
202
|
|
|
{ |
|
203
|
|
|
$userLoginResponse = UserService::mapUserLoginResponse($this->dic->get(UserService::class)->getById($this->authTokenData->getUserId())); |
|
204
|
|
|
$userLoginResponse->getIsDisabled() && $this->accessDenied(); |
|
205
|
|
|
|
|
206
|
|
|
$this->context->setUserData($userLoginResponse); |
|
207
|
|
|
$this->context->setUserProfile($this->dic->get(UserProfileService::class)->getById($userLoginResponse->getUserProfileId())->getProfile()); |
|
208
|
|
|
} |
|
209
|
|
|
|
|
210
|
|
|
/** |
|
211
|
|
|
* Devolver la clave maestra |
|
212
|
|
|
* |
|
213
|
|
|
* @return string |
|
214
|
|
|
* @throws ServiceException |
|
215
|
|
|
*/ |
|
216
|
|
|
private function getMasterPassFromVault() |
|
217
|
|
|
{ |
|
218
|
|
|
try { |
|
219
|
|
|
$tokenPass = $this->getParam('tokenPass', true); |
|
220
|
|
|
|
|
221
|
|
|
Hash::checkHashKey($tokenPass, $this->authTokenData->getHash()) || $this->accessDenied(); |
|
222
|
|
|
|
|
223
|
|
|
/** @var Vault $vault */ |
|
224
|
|
|
$vault = unserialize($this->authTokenData->getVault()); |
|
225
|
|
|
|
|
226
|
|
|
if ($vault && ($pass = $vault->getData($tokenPass . $this->getParam('authToken')))) { |
|
227
|
|
|
return $pass; |
|
228
|
|
|
} else { |
|
229
|
|
|
throw new ServiceException( |
|
230
|
|
|
__u('Error interno'), |
|
231
|
|
|
ServiceException::ERROR, |
|
232
|
|
|
__u('Datos inválidos'), |
|
233
|
|
|
JsonRpcResponse::INTERNAL_ERROR |
|
234
|
|
|
); |
|
235
|
|
|
} |
|
236
|
|
|
} catch (CryptoException $e) { |
|
237
|
|
|
throw new ServiceException( |
|
238
|
|
|
__u('Error interno'), |
|
239
|
|
|
ServiceException::ERROR, |
|
240
|
|
|
$e->getMessage(), |
|
241
|
|
|
JsonRpcResponse::INTERNAL_ERROR |
|
242
|
|
|
); |
|
243
|
|
|
} |
|
244
|
|
|
} |
|
245
|
|
|
|
|
246
|
|
|
/** |
|
247
|
|
|
* @param string $param |
|
248
|
|
|
* @param bool $required |
|
249
|
|
|
* @param mixed $default |
|
250
|
|
|
* |
|
251
|
|
|
* @return int |
|
252
|
|
|
* @throws ServiceException |
|
253
|
|
|
*/ |
|
254
|
|
|
public function getParamInt($param, $required = false, $default = null) |
|
255
|
|
|
{ |
|
256
|
|
|
return Filter::getInt($this->getParam($param, $required, $default)); |
|
257
|
|
|
} |
|
258
|
|
|
|
|
259
|
|
|
/** |
|
260
|
|
|
* @param string $param |
|
261
|
|
|
* @param bool $required |
|
262
|
|
|
* @param mixed $default |
|
263
|
|
|
* |
|
264
|
|
|
* @return string |
|
265
|
|
|
* @throws ServiceException |
|
266
|
|
|
*/ |
|
267
|
|
|
public function getParamString($param, $required = false, $default = null) |
|
268
|
|
|
{ |
|
269
|
|
|
return Filter::getString($this->getParam($param, $required, $default)); |
|
270
|
|
|
} |
|
271
|
|
|
|
|
272
|
|
|
/** |
|
273
|
|
|
* @param string $param |
|
274
|
|
|
* @param bool $required |
|
275
|
|
|
* @param mixed $default |
|
276
|
|
|
* |
|
277
|
|
|
* @return array |
|
278
|
|
|
* @throws ServiceException |
|
279
|
|
|
*/ |
|
280
|
|
|
public function getParamArray($param, $required = false, $default = null) |
|
281
|
|
|
{ |
|
282
|
|
|
return Filter::getArray($this->getParam($param, $required, $default)); |
|
283
|
|
|
} |
|
284
|
|
|
|
|
285
|
|
|
/** |
|
286
|
|
|
* @param string $param |
|
287
|
|
|
* @param bool $required |
|
288
|
|
|
* @param mixed $default |
|
289
|
|
|
* |
|
290
|
|
|
* @return int|string |
|
291
|
|
|
* @throws ServiceException |
|
292
|
|
|
*/ |
|
293
|
|
|
public function getParamEmail($param, $required = false, $default = null) |
|
294
|
|
|
{ |
|
295
|
|
|
return Filter::getEmail($this->getParam($param, $required, $default)); |
|
296
|
|
|
} |
|
297
|
|
|
|
|
298
|
|
|
/** |
|
299
|
|
|
* @param string $param |
|
300
|
|
|
* @param bool $required |
|
301
|
|
|
* @param mixed $default |
|
302
|
|
|
* |
|
303
|
|
|
* @return string |
|
304
|
|
|
* @throws ServiceException |
|
305
|
|
|
*/ |
|
306
|
|
|
public function getParamRaw($param, $required = false, $default = null) |
|
307
|
|
|
{ |
|
308
|
|
|
return Filter::getRaw($this->getParam($param, $required, $default)); |
|
309
|
|
|
} |
|
310
|
|
|
|
|
311
|
|
|
/** |
|
312
|
|
|
* @return string |
|
313
|
|
|
* @throws ServiceException |
|
314
|
|
|
*/ |
|
315
|
|
|
public function getMasterPass() |
|
316
|
|
|
{ |
|
317
|
|
|
return $this->getMasterKeyFromContext(); |
|
318
|
|
|
} |
|
319
|
|
|
|
|
320
|
|
|
/** |
|
321
|
|
|
* @param ApiRequest $apiRequest |
|
322
|
|
|
* |
|
323
|
|
|
* @return ApiService |
|
324
|
|
|
*/ |
|
325
|
|
|
public function setApiRequest(ApiRequest $apiRequest) |
|
326
|
|
|
{ |
|
327
|
|
|
$this->apiRequest = $apiRequest; |
|
328
|
|
|
|
|
329
|
|
|
return $this; |
|
330
|
|
|
} |
|
331
|
|
|
|
|
332
|
|
|
/** |
|
333
|
|
|
* @return int |
|
334
|
|
|
*/ |
|
335
|
|
|
public function getRequestId() |
|
336
|
|
|
{ |
|
337
|
|
|
return $this->apiRequest->getId(); |
|
338
|
|
|
} |
|
339
|
|
|
|
|
340
|
|
|
/** |
|
341
|
|
|
* @return bool |
|
342
|
|
|
*/ |
|
343
|
|
|
public function isInitialized(): bool |
|
344
|
|
|
{ |
|
345
|
|
|
return $this->initialized; |
|
346
|
|
|
} |
|
347
|
|
|
|
|
348
|
|
|
/** |
|
349
|
|
|
* @param string $helpClass |
|
350
|
|
|
* |
|
351
|
|
|
* @throws InvalidClassException |
|
352
|
|
|
*/ |
|
353
|
|
|
public function setHelpClass(string $helpClass) |
|
354
|
|
|
{ |
|
355
|
|
|
if (class_exists($helpClass)) { |
|
356
|
|
|
$this->helpClass = $helpClass; |
|
|
|
|
|
|
357
|
|
|
return; |
|
358
|
|
|
} |
|
359
|
|
|
|
|
360
|
|
|
throw new InvalidClassException('Invalid class for helper'); |
|
361
|
|
|
} |
|
362
|
|
|
|
|
363
|
|
|
/** |
|
364
|
|
|
* @throws \SP\Core\Exceptions\InvalidArgumentException |
|
365
|
|
|
*/ |
|
366
|
|
|
protected function initialize() |
|
367
|
|
|
{ |
|
368
|
|
|
$this->authTokenService = $this->dic->get(AuthTokenService::class); |
|
369
|
|
|
$this->trackService = $this->dic->get(TrackService::class); |
|
370
|
|
|
$this->trackRequest = $this->trackService->getTrackRequest(__CLASS__); |
|
371
|
|
|
} |
|
372
|
|
|
} |
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountIdthat can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theidproperty of an instance of theAccountclass. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.