summaryrefslogtreecommitdiffstats
path: root/application/forms/MigrationForm.php
blob: c5d517f03b7ef17741297477810bd6afb9111dcf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
<?php

/* Icinga Web 2 | (c) 2023 Icinga GmbH | GPLv2+ */

namespace Icinga\Forms;

use Icinga\Application\MigrationManager;
use ipl\Html\Attributes;
use ipl\Html\FormElement\CheckboxElement;
use ipl\Html\FormElement\FieldsetElement;
use ipl\Html\HtmlElement;
use ipl\Html\Text;
use ipl\I18n\Translation;
use ipl\Validator\CallbackValidator;
use ipl\Web\Common\CsrfCounterMeasure;
use ipl\Web\Common\FormUid;
use ipl\Web\Compat\CompatForm;
use ipl\Web\FormDecorator\IcingaFormDecorator;
use PDOException;

class MigrationForm extends CompatForm
{
    use CsrfCounterMeasure;
    use FormUid;
    use Translation;

    protected $defaultAttributes = [
        'class' => ['icinga-form', 'migration-form', 'icinga-controls'],
        'name'  => 'migration-form'
    ];

    /** @var bool Whether to allow changing the current database user and password */
    protected $renderDatabaseUserChange = false;

    public function hasBeenSubmitted(): bool
    {
        if (! $this->hasBeenSent()) {
            return false;
        }

        $pressedButton = $this->getPressedSubmitElement();

        return $pressedButton && strpos($pressedButton->getName(), 'migrate-') !== false;
    }

    public function setRenderDatabaseUserChange(bool $value = true): self
    {
        $this->renderDatabaseUserChange = $value;

        return $this;
    }

    public function hasDefaultElementDecorator()
    {
        // The base implementation registers a decorator we don't want here
        return false;
    }

    protected function assemble(): void
    {
        $this->addHtml($this->createUidElement());

        if ($this->renderDatabaseUserChange) {
            $mm = MigrationManager::instance();
            $newDbSetup = new FieldsetElement('database_setup', ['required' => true]);
            $newDbSetup
                ->setDefaultElementDecorator(new IcingaFormDecorator())
                ->addElement('text', 'username', [
                    'required'    => true,
                    'label'       => $this->translate('Username'),
                    'description' => $this->translate(
                        'A user which is able to create and/or alter the database schema.'
                    )
                ])
                ->addElement('password', 'password', [
                    'required'     => true,
                    'autocomplete' => 'new-password',
                    'label'        => $this->translate('Password'),
                    'description'  => $this->translate('The password for the database user defined above.'),
                    'validators'   => [
                        new CallbackValidator(function ($_, CallbackValidator $validator) use ($mm, $newDbSetup): bool {
                            /** @var array<string, string> $values */
                            $values = $this->getValue('database_setup');
                            /** @var CheckboxElement $checkBox */
                            $checkBox = $newDbSetup->getElement('grant_privileges');
                            $canIssueGrants = $checkBox->isChecked();
                            $elevationConfig = [
                                'username' => $values['username'],
                                'password' => $values['password']
                            ];

                            try {
                                if (! $mm->validateDatabasePrivileges($elevationConfig, $canIssueGrants)) {
                                    $validator->addMessage(sprintf(
                                        $this->translate(
                                            'The provided credentials cannot be used to execute "%s" SQL commands'
                                            . ' and/or grant the missing privileges to other users.'
                                        ),
                                        implode(' ,', $mm->getRequiredDatabasePrivileges())
                                    ));

                                    return false;
                                }
                            } catch (PDOException $e) {
                                $validator->addMessage($e->getMessage());

                                return false;
                            }

                            return true;
                        })
                    ]
                ])
                ->addElement('checkbox', 'grant_privileges', [
                    'required'    => false,
                    'label'       => $this->translate('Grant Missing Privileges'),
                    'description' => $this->translate(
                        'Allows to automatically grant the required privileges to the database user specified'
                        . ' in the respective resource config. If you do not want to provide additional credentials'
                        . ' each time, you can enable this and Icinga Web will grant the active database user the'
                        . ' missing privileges.'
                    )
                ]);

            $this->addHtml(
                new HtmlElement(
                    'div',
                    Attributes::create(['class' => 'change-database-user-description']),
                    new HtmlElement('span', null, Text::create(sprintf(
                        $this->translate(
                            'It seems that the currently used database user does not have the required privileges to'
                            . ' execute the %s SQL commands. Please provide an alternative user'
                            . ' that has the appropriate credentials to resolve this issue.'
                        ),
                        implode(', ', $mm->getRequiredDatabasePrivileges())
                    )))
                )
            );

            $this->addElement($newDbSetup);
        }
    }
}