DKIMSetupCommand::execute()   B
last analyzed

Complexity

Conditions 8
Paths 10

Size

Total Lines 46
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 8
eloc 26
c 1
b 0
f 0
nc 10
nop 2
dl 0
loc 46
rs 8.4444
1
<?php
2
3
declare(strict_types=1);
4
/**
5
 * This file is part of the mailserver-admin package.
6
 * (c) Jeffrey Boehm <https://github.com/jeboehm/mailserver-admin>
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace App\Command;
12
13
use App\Entity\Domain;
14
use App\Service\DKIM\Config\Manager;
15
use App\Service\DKIM\FormatterService;
16
use App\Service\DKIM\KeyGenerationService;
17
use Doctrine\Persistence\ManagerRegistry;
18
use Symfony\Component\Console\Command\Command;
19
use Symfony\Component\Console\Helper\QuestionHelper;
20
use Symfony\Component\Console\Input\InputInterface;
21
use Symfony\Component\Console\Input\InputOption;
22
use Symfony\Component\Console\Output\OutputInterface;
23
use Symfony\Component\Console\Question\ConfirmationQuestion;
24
25
class DKIMSetupCommand extends Command
26
{
27
    private ManagerRegistry $manager;
28
    private KeyGenerationService $keyGenerationService;
29
    private FormatterService $formatterService;
30
    private Manager $dkimManager;
31
32
    public function __construct(
33
        ManagerRegistry $manager,
34
        KeyGenerationService $keyGenerationService,
35
        FormatterService $formatterService,
36
        Manager $dkimManager
37
    ) {
38
        parent::__construct();
39
40
        $this->manager = $manager;
41
        $this->keyGenerationService = $keyGenerationService;
42
        $this->formatterService = $formatterService;
43
        $this->dkimManager = $dkimManager;
44
    }
45
46
    protected function configure(): void
47
    {
48
        $this
49
            ->setName('dkim:setup')
50
            ->addArgument('domain')
51
            ->addOption('enable', null, InputOption::VALUE_NONE, 'Enable DKIM signing for outgoing mails.')
52
            ->addOption('regenerate', null, InputOption::VALUE_NONE, 'Regenerate private key.')
53
            ->addOption('selector', null, InputOption::VALUE_REQUIRED, 'Set DKIM selector.');
54
    }
55
56
    protected function execute(InputInterface $input, OutputInterface $output): int
57
    {
58
        $domain = $this->getDomain($input, $output);
59
60
        if (null === $domain) {
61
            return 1;
62
        }
63
64
        $regenerateKey = (bool) $input->getOption('regenerate');
65
66
        if ($regenerateKey && !$this->warnOnKeyRegeneration($input, $output)) {
67
            return 1;
68
        }
69
70
        if (empty($domain->getDkimPrivateKey())) {
71
            $regenerateKey = true;
72
        }
73
74
        $selector = $input->getOption('selector') ?: \date('Y');
75
76
        if ($regenerateKey) {
77
            $keyPair = $this->keyGenerationService->createKeyPair();
78
            $domain->setDkimPrivateKey($keyPair->getPrivate());
79
        }
80
81
        $domain->setDkimSelector($selector);
82
        $domain->setDkimEnabled((bool) $input->getOption('enable'));
83
84
        $expectedDnsRecord = $this->formatterService->getTXTRecord(
85
            $this->keyGenerationService->extractPublicKey($domain->getDkimPrivateKey()),
86
            KeyGenerationService::DIGEST_ALGORITHM
87
        );
88
89
        $this->manager->getManager()->flush();
90
        $this->dkimManager->refresh();
91
92
        $output->writeln(sprintf('<info>Add the following TXT record to %s.%s:</info>', $selector, $domain->getName()));
93
        $output->writeln('');
94
        $output->writeln($expectedDnsRecord);
95
        $output->writeln('');
96
97
        if ($domain->getDkimEnabled()) {
98
            $output->writeln('<info>DKIM is enabled.</info>');
99
        }
100
101
        return 0;
102
    }
103
104
    private function getDomain(InputInterface $input, OutputInterface $output): ?Domain
105
    {
106
        $name = $input->getArgument('domain');
107
        /** @var Domain $domain */
108
        $domain = $this->manager->getRepository(Domain::class)->findOneBy(['name' => $name]);
109
110
        if (!$domain) {
0 ignored issues
show
introduced by
$domain is of type App\Entity\Domain, thus it always evaluated to true.
Loading history...
111
            $output->writeln(sprintf('<error>Domain "%s" was not found.</error>', $name));
112
113
            return null;
114
        }
115
116
        return $domain;
117
    }
118
119
    private function warnOnKeyRegeneration(InputInterface $input, OutputInterface $output): bool
120
    {
121
        /** @var QuestionHelper $questionHelper */
122
        $questionHelper = $this->getHelper('question');
123
        $result = $questionHelper->ask(
124
            $input,
125
            $output,
126
            new ConfirmationQuestion(
127
                "<question>If you regenerate your private key, you'll have to update your DNS settings. Continue?</question>"
128
            )
129
        );
130
131
        if (!$result) {
132
            $output->writeln('Aborting.');
133
134
            return false;
135
        }
136
137
        return true;
138
    }
139
}
140