Passed
Push — master ( 7c51ba...a22fab )
by Peter
04:20
created

Create::getAdminResources()   A

Complexity

Conditions 5
Paths 7

Size

Total Lines 21
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 11
dl 0
loc 21
rs 9.6111
c 0
b 0
f 0
cc 5
nc 7
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace AbterPhp\Admin\Console\Commands\ApiClient;
6
7
use AbterPhp\Admin\Domain\Entities\ApiClient;
8
use AbterPhp\Admin\Orm\AdminResourceRepo;
9
use AbterPhp\Admin\Orm\ApiClientRepo;
10
use AbterPhp\Admin\Orm\UserRepo;
11
use AbterPhp\Framework\Authorization\CacheManager;
12
use AbterPhp\Framework\Crypto\Crypto;
13
use Hackzilla\PasswordGenerator\Generator\ComputerPasswordGenerator as PasswordGenerator;
0 ignored issues
show
Bug introduced by
The type Hackzilla\PasswordGenera...mputerPasswordGenerator was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
14
use Opulence\Console\Commands\Command;
15
use Opulence\Console\Requests\Argument;
16
use Opulence\Console\Requests\ArgumentTypes;
17
use Opulence\Console\Requests\Option;
18
use Opulence\Console\Requests\OptionTypes;
19
use Opulence\Console\Responses\IResponse;
20
use Opulence\Orm\IUnitOfWork;
21
use ZxcvbnPhp\Zxcvbn;
22
23
class Create extends Command
24
{
25
    const COMMAND_NAME            = 'apiclient:create';
26
    const COMMAND_DESCRIPTION     = 'Creates a new API client';
27
    const COMMAND_SUCCESS         = '<success>New API client is created. ID: <b>%s</b></success>';
28
    const COMMAND_DRY_RUN_MESSAGE = '<info>Dry run prevented creating new API client.</info>';
29
30
    const ARGUMENT_USER        = 'user';
31
    const ARGUMENT_DESCRIPTION = 'description';
32
    const ARGUMENT_RESOURCES   = 'resources';
33
34
    const OPTION_DRY_RUN    = 'dry-run';
35
    const SHORTENED_DRY_RUN = 'd';
36
37
    const RESPONSE_SECRET = '<info>Secret generated: <b>%s</b></info>';
38
39
    /** @var UserRepo */
40
    protected $userRepo;
41
42
    /** @var AdminResourceRepo */
43
    protected $adminResourceRepo;
44
45
    /** @var ApiClientRepo */
46
    protected $apiClientRepo;
47
48
    /** @var PasswordGenerator */
49
    protected $passwordGenerator;
50
51
    /** @var Crypto */
52
    protected $crypto;
53
54
    /** @var IUnitOfWork */
55
    protected $unitOfWork;
56
57
    /** @var CacheManager */
58
    protected $cacheManager;
59
60
    /** @var Zxcvbn */
61
    protected $zxcvbn;
62
63
    /**
64
     * Create constructor.
65
     *
66
     * @param UserRepo          $userRepo
67
     * @param AdminResourceRepo $adminResourceRepo
68
     * @param ApiClientRepo     $apiClientRepo
69
     * @param PasswordGenerator $passwordGenerator
70
     * @param Crypto            $crypto
71
     * @param IUnitOfWork       $unitOfWork
72
     * @param CacheManager      $cacheManager
73
     * @param Zxcvbn            $zxcvbn
74
     */
75
    public function __construct(
76
        UserRepo $userRepo,
77
        AdminResourceRepo $adminResourceRepo,
78
        ApiClientRepo $apiClientRepo,
79
        PasswordGenerator $passwordGenerator,
80
        Crypto $crypto,
81
        IUnitOfWork $unitOfWork,
82
        CacheManager $cacheManager,
83
        Zxcvbn $zxcvbn
84
    ) {
85
        $this->userRepo          = $userRepo;
86
        $this->adminResourceRepo = $adminResourceRepo;
87
        $this->apiClientRepo     = $apiClientRepo;
88
        $this->passwordGenerator = $passwordGenerator;
89
        $this->crypto            = $crypto;
90
        $this->unitOfWork        = $unitOfWork;
91
        $this->cacheManager      = $cacheManager;
92
        $this->zxcvbn            = $zxcvbn;
93
94
        parent::__construct();
95
    }
96
97
    /**
98
     * @inheritdoc
99
     */
100
    protected function define()
101
    {
102
        $this->setName(static::COMMAND_NAME)
103
            ->setDescription(static::COMMAND_DESCRIPTION)
104
            ->addArgument(
105
                new Argument(
106
                    static::ARGUMENT_USER,
107
                    ArgumentTypes::REQUIRED,
108
                    'User Identifier (Email or Username)'
109
                )
110
            )
111
            ->addArgument(
112
                new Argument(
113
                    static::ARGUMENT_DESCRIPTION,
114
                    ArgumentTypes::REQUIRED,
115
                    'Description'
116
                )
117
            )
118
            ->addArgument(
119
                new Argument(
120
                    static::ARGUMENT_RESOURCES,
121
                    ArgumentTypes::REQUIRED,
122
                    'Resources (Comma separated list)'
123
                )
124
            )
125
            ->addOption(
126
                new Option(
127
                    static::OPTION_DRY_RUN,
128
                    static::SHORTENED_DRY_RUN,
129
                    OptionTypes::OPTIONAL_VALUE,
130
                    'Dry run (default: 0)',
131
                    '0'
132
                )
133
            );
134
    }
135
136
    /**
137
     * @inheritdoc
138
     */
139
    protected function doExecute(IResponse $response)
140
    {
141
        $userIdentifier = $this->getArgumentValue(static::ARGUMENT_USER);
142
143
        try {
144
            $user = $this->userRepo->find($userIdentifier);
145
            if (!$user) {
146
                throw new \RuntimeException();
147
            }
148
149
            $adminResources = $this->getAdminResources($user->getId());
150
151
            $rawSecret        = $this->passwordGenerator->generatePassword();
152
            $preparedPassword = $this->crypto->prepareSecret($rawSecret);
153
            $packedPassword   = $this->crypto->hashCrypt($preparedPassword);
154
155
            $apiClient = $this->getApiClient($user->getId(), $packedPassword, $adminResources);
156
157
            $this->apiClientRepo->add($apiClient);
158
        } catch (\Exception $e) {
159
            if ($e->getPrevious()) {
160
                $response->writeln(sprintf('<error>%s</error>', $e->getPrevious()->getMessage()));
161
            }
162
            $response->writeln(sprintf('<fatal>%s</fatal>', $e->getMessage()));
163
164
            return;
165
        }
166
167
168
        $dryRun = (bool)$this->getOptionValue(static::OPTION_DRY_RUN);
169
        if ($dryRun) {
170
            $this->unitOfWork->dispose();
171
            $response->writeln(static::COMMAND_DRY_RUN_MESSAGE);
172
173
            return;
174
        }
175
176
        try {
177
            $this->unitOfWork->commit();
178
            $this->cacheManager->clearAll();
179
        } catch (\Exception $e) {
180
            if ($e->getPrevious()) {
181
                $response->writeln(sprintf('<error>%s</error>', $e->getPrevious()->getMessage()));
182
            }
183
            $response->writeln(sprintf('<fatal>%s</fatal>', $e->getMessage()));
184
185
            return;
186
        }
187
188
        $response->writeln(sprintf(static::RESPONSE_SECRET, $rawSecret));
189
        $response->writeln(sprintf(static::COMMAND_SUCCESS, $apiClient->getId()));
190
    }
191
192
    /**
193
     * @param string          $userId
194
     * @param string          $packedPassword
195
     * @param AdminResource[] $adminResources
196
     *
197
     * @return ApiClient
198
     * @throws \Opulence\Orm\OrmException
199
     * @throws \RuntimeException
200
     */
201
    protected function getApiClient(string $userId, string $packedPassword, array $adminResources): ApiClient
202
    {
203
        $description = $this->getArgumentValue(static::ARGUMENT_DESCRIPTION);
204
        $resources   = $this->getArgumentValue(static::ARGUMENT_RESOURCES);
0 ignored issues
show
Unused Code introduced by
The assignment to $resources is dead and can be removed.
Loading history...
205
206
        return new ApiClient(
207
            '',
208
            $userId,
209
            $description,
210
            $packedPassword,
211
            $adminResources
212
        );
213
    }
214
215
    /**
216
     * @param string $userId
217
     *
218
     * @return AdminResource[]
219
     */
220
    protected function getAdminResources(string $userId): array
221
    {
222
        $resources = explode(',', $this->getArgumentValue(static::ARGUMENT_RESOURCES));
223
        if (!$resources) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $resources of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
224
            return [];
225
        }
226
227
        $adminResources = [];
228
        foreach ($this->adminResourceRepo->getByUserId($userId) as $adminResource) {
229
            if (!in_array($adminResource->getIdentifier(), $resources, true)) {
230
                continue;
231
            }
232
233
            $adminResources[] = $adminResource;
234
        }
235
236
        if (count($resources) != count($adminResources)) {
237
            throw new \RuntimeException('User does not have all requested resources');
238
        }
239
240
        return $adminResources;
241
    }
242
}
243