Passed
Push — devel-3.0 ( 330e85...5f7f30 )
by Rubén
03:30
created

AuthTokenService::isSecuredAction()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
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\AuthToken;
26
27
use SP\Core\Acl\Acl;
28
use SP\Core\Acl\ActionsInterface;
29
use SP\Core\Crypt\Hash;
30
use SP\Core\Crypt\Vault;
31
use SP\Core\Exceptions\SPException;
32
use SP\DataModel\AuthTokenData;
33
use SP\DataModel\ItemSearchData;
34
use SP\Repositories\AuthToken\AuthTokenRepository;
35
use SP\Repositories\NoSuchItemException;
36
use SP\Services\Service;
37
use SP\Services\ServiceException;
38
use SP\Services\ServiceItemTrait;
39
use SP\Storage\Database\QueryResult;
40
use SP\Util\Util;
41
42
/**
43
 * Class AuthTokenService
44
 *
45
 * @package SP\Services\AuthToken
46
 */
47
final class AuthTokenService extends Service
48
{
49
    use ServiceItemTrait;
50
51
    const SECURED_ACTIONS = [
52
        ActionsInterface::ACCOUNT_VIEW_PASS,
53
        ActionsInterface::ACCOUNT_EDIT_PASS,
54
        ActionsInterface::ACCOUNT_CREATE
55
    ];
56
57
    /**
58
     * @var AuthTokenRepository
59
     */
60
    protected $authTokenRepository;
61
62
    /**
63
     * Devuelver un array de acciones posibles para los tokens
64
     *
65
     * @return array
66
     */
67
    public static function getTokenActions()
68
    {
69
        $actions = [
70
            ActionsInterface::ACCOUNT_SEARCH => Acl::getActionInfo(ActionsInterface::ACCOUNT_SEARCH),
71
            ActionsInterface::ACCOUNT_VIEW => Acl::getActionInfo(ActionsInterface::ACCOUNT_VIEW),
72
            ActionsInterface::ACCOUNT_VIEW_PASS => Acl::getActionInfo(ActionsInterface::ACCOUNT_VIEW_PASS),
73
            ActionsInterface::ACCOUNT_EDIT_PASS => Acl::getActionInfo(ActionsInterface::ACCOUNT_EDIT_PASS),
74
            ActionsInterface::ACCOUNT_DELETE => Acl::getActionInfo(ActionsInterface::ACCOUNT_DELETE),
75
            ActionsInterface::ACCOUNT_CREATE => Acl::getActionInfo(ActionsInterface::ACCOUNT_CREATE),
76
            ActionsInterface::ACCOUNT_EDIT => Acl::getActionInfo(ActionsInterface::ACCOUNT_EDIT),
77
            ActionsInterface::CATEGORY_SEARCH => Acl::getActionInfo(ActionsInterface::CATEGORY_SEARCH),
78
            ActionsInterface::CATEGORY_VIEW => Acl::getActionInfo(ActionsInterface::CATEGORY_VIEW),
79
            ActionsInterface::CATEGORY_CREATE => Acl::getActionInfo(ActionsInterface::CATEGORY_CREATE),
80
            ActionsInterface::CATEGORY_EDIT => Acl::getActionInfo(ActionsInterface::CATEGORY_EDIT),
81
            ActionsInterface::CATEGORY_DELETE => Acl::getActionInfo(ActionsInterface::CATEGORY_DELETE),
82
            ActionsInterface::CLIENT_SEARCH => Acl::getActionInfo(ActionsInterface::CLIENT_SEARCH),
83
            ActionsInterface::CLIENT_VIEW => Acl::getActionInfo(ActionsInterface::CLIENT_VIEW),
84
            ActionsInterface::CLIENT_CREATE => Acl::getActionInfo(ActionsInterface::CLIENT_CREATE),
85
            ActionsInterface::CLIENT_EDIT => Acl::getActionInfo(ActionsInterface::CLIENT_EDIT),
86
            ActionsInterface::CLIENT_DELETE => Acl::getActionInfo(ActionsInterface::CLIENT_DELETE),
87
            ActionsInterface::TAG_SEARCH => Acl::getActionInfo(ActionsInterface::TAG_SEARCH),
88
            ActionsInterface::TAG_VIEW => Acl::getActionInfo(ActionsInterface::TAG_VIEW),
89
            ActionsInterface::TAG_CREATE => Acl::getActionInfo(ActionsInterface::TAG_CREATE),
90
            ActionsInterface::TAG_EDIT => Acl::getActionInfo(ActionsInterface::TAG_EDIT),
91
            ActionsInterface::TAG_DELETE => Acl::getActionInfo(ActionsInterface::TAG_DELETE),
92
            ActionsInterface::CONFIG_BACKUP_RUN => Acl::getActionInfo(ActionsInterface::CONFIG_BACKUP_RUN),
93
            ActionsInterface::CONFIG_EXPORT_RUN => Acl::getActionInfo(ActionsInterface::CONFIG_EXPORT_RUN)
94
        ];
95
96
        return $actions;
97
    }
98
99
    /**
100
     * @param ItemSearchData $itemSearchData
101
     *
102
     * @return QueryResult
103
     * @throws \SP\Core\Exceptions\ConstraintException
104
     * @throws \SP\Core\Exceptions\QueryException
105
     */
106
    public function search(ItemSearchData $itemSearchData)
107
    {
108
        return $this->authTokenRepository->search($itemSearchData);
109
    }
110
111
    /**
112
     * @param $id
113
     *
114
     * @return AuthTokenData
115
     * @throws \SP\Core\Exceptions\ConstraintException
116
     * @throws \SP\Core\Exceptions\QueryException
117
     */
118
    public function getById($id)
119
    {
120
        return $this->authTokenRepository->getById($id)->getData();
121
    }
122
123
    /**
124
     * @param $id
125
     *
126
     * @return AuthTokenService
127
     * @throws \SP\Core\Exceptions\ConstraintException
128
     * @throws \SP\Core\Exceptions\QueryException
129
     * @throws NoSuchItemException
130
     */
131
    public function delete($id)
132
    {
133
        if ($this->authTokenRepository->delete($id) === 0) {
134
            throw new NoSuchItemException(__u('Token no encontrado'));
135
        }
136
137
        return $this;
138
    }
139
140
    /**
141
     * Deletes all the items for given ids
142
     *
143
     * @param array $ids
144
     *
145
     * @return bool
146
     * @throws ServiceException
147
     * @throws \SP\Core\Exceptions\ConstraintException
148
     * @throws \SP\Core\Exceptions\QueryException
149
     */
150
    public function deleteByIdBatch(array $ids)
151
    {
152
        if (($count = $this->authTokenRepository->deleteByIdBatch($ids)) !== count($ids)) {
153
            throw new ServiceException(__u('Error al eliminar tokens'), ServiceException::WARNING);
154
        }
155
156
        return $count;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $count returns the type integer which is incompatible with the documented return type boolean.
Loading history...
157
    }
158
159
    /**
160
     * @param $itemData
161
     *
162
     * @return mixed
163
     * @throws SPException
164
     * @throws \Defuse\Crypto\Exception\CryptoException
165
     * @throws \Defuse\Crypto\Exception\EnvironmentIsBrokenException
166
     * @throws \SP\Core\Exceptions\ConstraintException
167
     * @throws \SP\Core\Exceptions\QueryException
168
     */
169
    public function create($itemData)
170
    {
171
        return $this->authTokenRepository->create($this->injectSecureData($itemData));
172
    }
173
174
    /**
175
     * Injects secure data for token
176
     *
177
     * @param AuthTokenData $authTokenData
178
     * @param string        $token
179
     *
180
     * @return AuthTokenData
181
     * @throws ServiceException
182
     * @throws \Defuse\Crypto\Exception\CryptoException
183
     * @throws \Defuse\Crypto\Exception\EnvironmentIsBrokenException
184
     * @throws \SP\Core\Exceptions\ConstraintException
185
     * @throws \SP\Core\Exceptions\QueryException
186
     */
187
    private function injectSecureData(AuthTokenData $authTokenData, $token = null)
188
    {
189
        if ($token === null) {
190
            $token = $this->authTokenRepository->getTokenByUserId($authTokenData->getUserId()) ?: $this->generateToken();
191
        }
192
193
        if (self::isSecuredAction($authTokenData->getActionId())) {
194
            $authTokenData->setVault($this->getSecureData($token, $authTokenData->getHash()));
195
            $authTokenData->setHash(Hash::hashKey($authTokenData->getHash()));
196
        } else {
197
            $authTokenData->setHash(null);
198
        }
199
200
        $authTokenData->setToken($token);
201
        $authTokenData->setCreatedBy($this->context->getUserData()->getId());
202
203
        return $authTokenData;
204
    }
205
206
    /**
207
     * Generar un token de acceso
208
     *
209
     * @return string
210
     * @throws \Defuse\Crypto\Exception\EnvironmentIsBrokenException
211
     */
212
    private function generateToken()
213
    {
214
        return Util::generateRandomBytes(32);
215
    }
216
217
    /**
218
     * @param int $action
219
     *
220
     * @return bool
221
     */
222
    public static function isSecuredAction(int $action)
223
    {
224
        return in_array($action, self::SECURED_ACTIONS, true);
225
    }
226
227
    /**
228
     * Generar la llave segura del token
229
     *
230
     * @param string $token
231
     * @param string $key
232
     *
233
     * @return Vault
234
     * @throws ServiceException
235
     * @throws \Defuse\Crypto\Exception\CryptoException
236
     */
237
    private function getSecureData($token, $key)
238
    {
239
        return (new Vault())->saveData($this->getMasterKeyFromContext(), $key . $token);
240
    }
241
242
    /**
243
     * @param AuthTokenData $itemData
244
     *
245
     * @throws \Exception
246
     */
247
    public function refreshAndUpdate(AuthTokenData $itemData)
248
    {
249
        $this->transactionAware(function () use ($itemData) {
250
            $token = $this->generateToken();
251
            $vault = serialize($this->getSecureData($token, $itemData->getHash()));
252
253
            $this->authTokenRepository->refreshTokenByUserId($itemData->getUserId(), $token);
254
            $this->authTokenRepository->refreshVaultByUserId($itemData->getUserId(), $vault, Hash::hashKey($itemData->getHash()));
255
256
            $this->update($itemData, $token);
257
        });
258
    }
259
260
    /**
261
     * @param AuthTokenData $itemData
262
     * @param string        $token
263
     *
264
     * @throws SPException
265
     * @throws \Defuse\Crypto\Exception\CryptoException
266
     * @throws \SP\Core\Exceptions\ConstraintException
267
     * @throws \SP\Core\Exceptions\QueryException
268
     */
269
    public function update(AuthTokenData $itemData, $token = null)
270
    {
271
        if ($this->authTokenRepository->update($this->injectSecureData($itemData, $token)) === 0) {
272
            throw new NoSuchItemException(__u('Token no encontrado'));
273
        }
274
    }
275
276
    /**
277
     * @param AuthTokenData $itemData
278
     *
279
     * @throws SPException
280
     * @throws \SP\Core\Exceptions\ConstraintException
281
     * @throws \SP\Core\Exceptions\QueryException
282
     */
283
    public function updateRaw(AuthTokenData $itemData)
284
    {
285
        if ($this->authTokenRepository->update($itemData) === 0) {
286
            throw new NoSuchItemException(__u('Token no encontrado'));
287
        }
288
    }
289
290
    /**
291
     * Devolver los datos de un token
292
     *
293
     * @param $actionId int El id de la accion
294
     * @param $token    string El token de seguridad
295
     *
296
     * @return false|AuthTokenData
297
     * @throws \SP\Core\Exceptions\ConstraintException
298
     * @throws \SP\Core\Exceptions\QueryException
299
     * @throws ServiceException
300
     */
301
    public function getTokenByToken($actionId, $token)
302
    {
303
        $result = $this->authTokenRepository->getTokenByToken($actionId, $token);
304
305
        if ($result->getNumRows() === 0) {
306
            throw new ServiceException(__u('Error interno'));
307
        }
308
309
        return $result->getData();
310
    }
311
312
    /**
313
     * @return AuthTokenData[]
314
     * @throws \SP\Core\Exceptions\ConstraintException
315
     * @throws \SP\Core\Exceptions\QueryException
316
     */
317
    public function getAllBasic()
318
    {
319
        return $this->authTokenRepository->getAll()->getDataAsArray();
320
    }
321
322
    /**
323
     * @throws \Psr\Container\ContainerExceptionInterface
324
     * @throws \Psr\Container\NotFoundExceptionInterface
325
     */
326
    protected function initialize()
327
    {
328
        $this->authTokenRepository = $this->dic->get(AuthTokenRepository::class);
329
    }
330
}