RegenerateSecret::doExecute()   B
last analyzed

Complexity

Conditions 7
Paths 19

Size

Total Lines 50
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 31
c 1
b 0
f 0
dl 0
loc 50
rs 8.4906
cc 7
nc 19
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace AbterPhp\Admin\Console\Commands\ApiClient;
6
7
use AbterPhp\Admin\Orm\ApiClientRepo;
8
use AbterPhp\Framework\Authorization\CacheManager;
9
use AbterPhp\Framework\Crypto\Crypto;
10
use Hackzilla\PasswordGenerator\Generator\ComputerPasswordGenerator as PasswordGenerator;
11
use Opulence\Console\Commands\Command;
12
use Opulence\Console\Requests\Argument;
13
use Opulence\Console\Requests\ArgumentTypes;
14
use Opulence\Console\Requests\Option;
15
use Opulence\Console\Requests\OptionTypes;
16
use Opulence\Console\Responses\IResponse;
17
use Opulence\Console\StatusCodes;
18
use Opulence\Orm\IUnitOfWork;
19
use ZxcvbnPhp\Zxcvbn;
20
21
class RegenerateSecret extends Command
22
{
23
    public const COMMAND_NAME            = 'apiclient:regenerate-secret';
24
    public const COMMAND_DESCRIPTION     = 'Regenerate the secret of an existing API client';
25
    public const COMMAND_SUCCESS         = '<success>API client secret is updated.</success>';
26
    public const COMMAND_DRY_RUN_MESSAGE = '<info>Dry run prevented updating existing API client.</info>';
27
28
    public const ARGUMENT_IDENTIFIER = 'identifier';
29
30
    public const OPTION_DRY_RUN    = 'dry-run';
31
    public const SHORTENED_DRY_RUN = 'd';
32
33
    public const RESPONSE_SECRET = '<info>Secret generated: <b>%s</b></info>';
34
35
    protected ApiClientRepo $apiClientRepo;
36
37
    /** @var PasswordGenerator */
38
    protected PasswordGenerator $passwordGenerator;
39
40
    protected Crypto $crypto;
41
42
    protected IUnitOfWork $unitOfWork;
43
44
    protected CacheManager $cacheManager;
45
46
    protected Zxcvbn $zxcvbn;
47
48
    /**
49
     * RegenerateSecret constructor.
50
     *
51
     * @param ApiClientRepo     $apiClientRepo
52
     * @param PasswordGenerator $passwordGenerator
53
     * @param Crypto            $crypto
54
     * @param IUnitOfWork       $unitOfWork
55
     * @param CacheManager      $cacheManager
56
     * @param Zxcvbn            $zxcvbn
57
     */
58
    public function __construct(
59
        ApiClientRepo $apiClientRepo,
60
        PasswordGenerator $passwordGenerator,
61
        Crypto $crypto,
62
        IUnitOfWork $unitOfWork,
63
        CacheManager $cacheManager,
64
        Zxcvbn $zxcvbn
65
    ) {
66
        $this->apiClientRepo     = $apiClientRepo;
67
        $this->passwordGenerator = $passwordGenerator;
68
        $this->crypto            = $crypto;
69
        $this->unitOfWork        = $unitOfWork;
70
        $this->cacheManager      = $cacheManager;
71
        $this->zxcvbn            = $zxcvbn;
72
73
        parent::__construct();
74
    }
75
76
    /**
77
     * @inheritdoc
78
     */
79
    protected function define()
80
    {
81
        $this->setName(static::COMMAND_NAME)
82
            ->setDescription(static::COMMAND_DESCRIPTION)
83
            ->addArgument(
84
                new Argument(
85
                    static::ARGUMENT_IDENTIFIER,
86
                    ArgumentTypes::REQUIRED,
87
                    'Identifier'
88
                )
89
            )
90
            ->addOption(
91
                new Option(
92
                    static::OPTION_DRY_RUN,
93
                    static::SHORTENED_DRY_RUN,
94
                    OptionTypes::OPTIONAL_VALUE,
95
                    'Dry run (default: 0)',
96
                    '0'
97
                )
98
            );
99
    }
100
101
    /**
102
     * @inheritdoc
103
     */
104
    protected function doExecute(IResponse $response)
105
    {
106
        $identifier = $this->getArgumentValue(static::ARGUMENT_IDENTIFIER);
107
        $dryRun     = $this->getOptionValue(static::OPTION_DRY_RUN);
108
109
        try {
110
            $apiClient = $this->apiClientRepo->getById($identifier);
111
            if (!$apiClient) {
0 ignored issues
show
introduced by
$apiClient is of type object, thus it always evaluated to true.
Loading history...
112
                $response->writeln(sprintf('<fatal>API client not found</fatal>'));
113
114
                return StatusCodes::ERROR;
115
            }
116
117
            $rawSecret        = $this->passwordGenerator->generatePassword();
118
            $preparedPassword = $this->crypto->prepareSecret($rawSecret);
119
            $packedPassword   = $this->crypto->hashCrypt($preparedPassword);
120
121
            $apiClient->setSecret($packedPassword);
122
        } catch (\Exception $e) {
123
            if ($e->getPrevious()) {
124
                $response->writeln(sprintf('<error>%s</error>', $e->getPrevious()->getMessage()));
125
            }
126
            $response->writeln(sprintf('<fatal>%s</fatal>', $e->getMessage()));
127
128
            return StatusCodes::FATAL;
129
        }
130
131
        if ($dryRun) {
132
            $this->unitOfWork->dispose();
133
            $response->writeln(static::COMMAND_DRY_RUN_MESSAGE);
134
135
            return StatusCodes::OK;
136
        }
137
138
        try {
139
            $this->unitOfWork->commit();
140
            $this->cacheManager->clearAll();
141
        } catch (\Exception $e) {
142
            if ($e->getPrevious()) {
143
                $response->writeln(sprintf('<error>%s</error>', $e->getPrevious()->getMessage()));
144
            }
145
            $response->writeln(sprintf('<fatal>%s</fatal>', $e->getMessage()));
146
147
            return StatusCodes::FATAL;
148
        }
149
150
        $response->writeln(sprintf(static::RESPONSE_SECRET, $rawSecret));
151
        $response->writeln(static::COMMAND_SUCCESS);
152
153
        return StatusCodes::OK;
154
    }
155
}
156