Issues (217)

src/controllers/UsersController.php (6 issues)

1
<?php
2
3
/**
4
 * PHPPgAdmin 6.1.3
5
 */
6
7
namespace PHPPgAdmin\Controller;
8
9
use PHPPgAdmin\Decorators\Decorator;
10
11
/**
12
 * Base controller class.
13
 */
14
class UsersController extends BaseController
15
{
16
    public $controller_title = 'strusers';
17
18
    /**
19
     * Default method to render the controller according to the action parameter.
20
     */
21
    public function render(): void
22
    {
23
        $this->printHeader();
24
        $this->printBody();
25
26
        switch ($this->action) {
27
            case 'changepassword':
28
                if (isset($_REQUEST['ok'])) {
29
                    $this->doChangePassword(false);
30
                } else {
31
                    $this->doAccount();
32
                }
33
34
                break;
35
            case 'confchangepassword':
36
                $this->doChangePassword(true);
37
38
                break;
39
            case 'account':
40
                $this->doAccount();
41
42
                break;
43
            case 'save_create':
44
                if (isset($_REQUEST['cancel'])) {
45
                    $this->doDefault();
46
                } else {
47
                    $this->doSaveCreate();
48
                }
49
50
                break;
51
            case 'create':
52
                $this->doCreate();
53
54
                break;
55
            case 'drop':
56
                if (isset($_REQUEST['cancel'])) {
57
                    $this->doDefault();
58
                } else {
59
                    $this->doDrop(false);
60
                }
61
62
                break;
63
            case 'confirm_drop':
64
                $this->doDrop(true);
65
66
                break;
67
            case 'save_edit':
68
                if (isset($_REQUEST['cancel'])) {
69
                    $this->doDefault();
70
                } else {
71
                    $this->doSaveEdit();
72
                }
73
74
                break;
75
            case 'edit':
76
                $this->doEdit();
77
78
                break;
79
80
            default:
81
                $this->doDefault();
82
83
                break;
84
        }
85
86
        $this->printFooter();
87
    }
88
89
    /**
90
     * Show default list of users in the database.
91
     *
92
     * @param mixed $msg
93
     */
94
    public function doDefault($msg = ''): void
95
    {
96
        $data = $this->misc->getDatabaseAccessor();
97
98
        $lang = $this->lang;
99
        $renderUseExpires = static function ($val) use ($lang) {
100
            return 'infinity' === $val ? $lang['strnever'] : \htmlspecialchars($val);
101
        };
102
103
        $this->printTrail('server');
104
        $this->printTabs('server', 'users');
105
        $this->printMsg($msg);
106
107
        $users = $data->getUsers();
108
109
        $columns = [
110
            'user' => [
111
                'title' => $this->lang['strusername'],
112
                'field' => Decorator::field('usename'),
113
            ],
114
            'group' => [
115
                'title' => $this->lang['strgroup'],
116
                'field' => Decorator::field('group'),
117
            ],
118
            'superuser' => [
119
                'title' => $this->lang['strsuper'],
120
                'field' => Decorator::field('usesuper'),
121
                'type' => 'yesno',
122
            ],
123
            'createdb' => [
124
                'title' => $this->lang['strcreatedb'],
125
                'field' => Decorator::field('usecreatedb'),
126
                'type' => 'yesno',
127
            ],
128
            'expires' => [
129
                'title' => $this->lang['strexpires'],
130
                'field' => Decorator::field('useexpires'),
131
                'type' => 'callback',
132
                'params' => ['function' => $renderUseExpires, 'null' => $this->lang['strnever']],
133
            ],
134
            'defaults' => [
135
                'title' => $this->lang['strsessiondefaults'],
136
                'field' => Decorator::field('useconfig'),
137
            ],
138
            'actions' => [
139
                'title' => $this->lang['stractions'],
140
            ],
141
        ];
142
143
        $actions = [
144
            'alter' => [
145
                'content' => $this->lang['stralter'],
146
                'attr' => [
147
                    'href' => [
148
                        'url' => 'users',
149
                        'urlvars' => [
150
                            'action' => 'edit',
151
                            'username' => Decorator::field('usename'),
152
                        ],
153
                    ],
154
                ],
155
            ],
156
            'drop' => [
157
                'content' => $this->lang['strdrop'],
158
                'attr' => [
159
                    'href' => [
160
                        'url' => 'users',
161
                        'urlvars' => [
162
                            'action' => 'confirm_drop',
163
                            'username' => Decorator::field('usename'),
164
                        ],
165
                    ],
166
                ],
167
            ],
168
        ];
169
170
        echo $this->printTable($users, $columns, $actions, 'users-users', $this->lang['strnousers']);
0 ignored issues
show
It seems like $users can also be of type integer; however, parameter $tabledata of PHPPgAdmin\Controller\BaseController::printTable() does only seem to accept ADORecordSet|PHPPgAdmin\ArrayRecordSet, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

170
        echo $this->printTable(/** @scrutinizer ignore-type */ $users, $columns, $actions, 'users-users', $this->lang['strnousers']);
Loading history...
171
172
        $this->printNavLinks(['create' => [
173
            'attr' => [
174
                'href' => [
175
                    'url' => 'users',
176
                    'urlvars' => [
177
                        'action' => 'create',
178
                        'server' => $_REQUEST['server'],
179
                    ],
180
                ],
181
            ],
182
            'content' => $this->lang['strcreateuser'],
183
        ]], 'users-users', \get_defined_vars());
184
    }
185
186
    /**
187
     * If a user is not a superuser, then we have an 'account management' page
188
     * where they can change their password, etc.  We don't prevent them from
189
     * messing with the URL to gain access to other user admin stuff, because
190
     * the PostgreSQL permissions will prevent them changing anything anyway.
191
     *
192
     * @param mixed $msg
193
     */
194
    public function doAccount($msg = ''): void
195
    {
196
        $data = $this->misc->getDatabaseAccessor();
197
198
        $server_info = $this->misc->getServerInfo();
199
200
        $userdata = $data->getUser($server_info['username']);
201
        $_REQUEST['user'] = $server_info['username'];
202
203
        $this->printTrail('user');
204
        $this->printTabs('server', 'account');
205
        $this->printMsg($msg);
206
207
        if (0 < $userdata->recordCount()) {
208
            $userdata->fields['usesuper'] = $data->phpBool($userdata->fields['usesuper']);
209
            $userdata->fields['usecreatedb'] = $data->phpBool($userdata->fields['usecreatedb']);
210
            echo '<table>' . \PHP_EOL;
211
            echo "<tr><th class=\"data\">{$this->lang['strusername']}</th><th class=\"data\">{$this->lang['strsuper']}</th><th class=\"data\">{$this->lang['strcreatedb']}</th><th class=\"data\">{$this->lang['strexpires']}</th>";
0 ignored issues
show
This line exceeds maximum limit of 190 characters; contains 228 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
212
            echo "<th class=\"data\">{$this->lang['strsessiondefaults']}</th>";
213
            echo '</tr>' . \PHP_EOL;
214
            echo "<tr>\n\t<td class=\"data1\">", $this->misc->printVal($userdata->fields['usename']), '</td>' . \PHP_EOL;
215
            echo "\t<td class=\"data1\">", $this->misc->printVal($userdata->fields['usesuper'], 'yesno'), '</td>' . \PHP_EOL;
216
            echo "\t<td class=\"data1\">", $this->misc->printVal($userdata->fields['usecreatedb'], 'yesno'), '</td>' . \PHP_EOL;
217
            echo "\t<td class=\"data1\">", ('infinity' === $userdata->fields['useexpires'] || null === $userdata->fields['useexpires'] ? $this->lang['strnever'] : $this->misc->printVal($userdata->fields['useexpires'])), '</td>' . \PHP_EOL;
0 ignored issues
show
This line exceeds maximum limit of 190 characters; contains 239 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
218
            echo "\t<td class=\"data1\">", $this->misc->printVal($userdata->fields['useconfig']), '</td>' . \PHP_EOL;
219
            echo "</tr>\n</table>" . \PHP_EOL;
220
        } else {
221
            echo "<p>{$this->lang['strnodata']}</p>" . \PHP_EOL;
222
        }
223
224
        $this->printNavLinks(['changepassword' => [
225
            'attr' => [
226
                'href' => [
227
                    'url' => 'users',
228
                    'urlvars' => [
229
                        'action' => 'confchangepassword',
230
                        'server' => $_REQUEST['server'],
231
                    ],
232
                ],
233
            ],
234
            'content' => $this->lang['strchangepassword'],
235
        ]], 'users-account', \get_defined_vars());
236
    }
237
238
    /**
239
     * Show confirmation of change password and actually change password.
240
     *
241
     * @param mixed $confirm
242
     * @param mixed $msg
243
     */
244
    public function doChangePassword($confirm, $msg = ''): void
245
    {
246
        $data = $this->misc->getDatabaseAccessor();
247
248
        $server_info = $this->misc->getServerInfo();
249
250
        if ($confirm) {
251
            $_REQUEST['user'] = $server_info['username'];
252
            $this->printTrail('user');
253
            $this->printTitle($this->lang['strchangepassword'], 'pg.user.alter');
254
            $this->printMsg($msg);
255
256
            $this->coalesceArr($_POST, 'password', '');
257
258
            $this->coalesceArr($_POST, 'confirm', '');
259
260
            echo '<form action="' . \containerInstance()->subFolder . '/src/views/users" method="post">' . \PHP_EOL;
261
            echo '<table>' . \PHP_EOL;
262
            echo "\t<tr>\n\t\t<th class=\"data left required\">{$this->lang['strpassword']}</th>" . \PHP_EOL;
263
            echo "\t\t<td><input type=\"password\" name=\"password\" size=\"32\" value=\"",
264
            \htmlspecialchars($_POST['password']), "\" /></td>\n\t</tr>" . \PHP_EOL;
265
            echo "\t<tr>\n\t\t<th class=\"data left required\">{$this->lang['strconfirm']}</th>" . \PHP_EOL;
266
            echo "\t\t<td><input type=\"password\" name=\"confirm\" size=\"32\" value=\"\" /></td>\n\t</tr>" . \PHP_EOL;
267
            echo '</table>' . \PHP_EOL;
268
            echo '<p><input type="hidden" name="action" value="changepassword" />' . \PHP_EOL;
269
            echo $this->view->form;
270
            echo "<input type=\"submit\" name=\"ok\" value=\"{$this->lang['strok']}\" />" . \PHP_EOL;
271
            echo "<input type=\"submit\" name=\"cancel\" value=\"{$this->lang['strcancel']}\" />" . \PHP_EOL;
272
            echo '</p></form>' . \PHP_EOL;
273
        } else {
274
            // Check that password is minimum length
275
            if (\mb_strlen($_POST['password']) < $this->conf['min_password_length']) {
276
                $this->doChangePassword(true, $this->lang['strpasswordshort']);
277
            } elseif ($_POST['password'] !== $_POST['confirm']) {
278
                // Check that password matches confirmation password
279
                $this->doChangePassword(true, $this->lang['strpasswordconfirm']);
280
            } else {
281
                $status = $data->changePassword(
282
                    $server_info['username'],
283
                    $_POST['password']
284
                );
285
286
                if (0 === $status) {
287
                    $this->doAccount($this->lang['strpasswordchanged']);
288
                } else {
289
                    $this->doAccount($this->lang['strpasswordchangedbad']);
290
                }
291
            }
292
        }
293
    }
294
295
    /**
296
     * Function to allow editing of a user.
297
     *
298
     * @param mixed $msg
299
     */
300
    public function doEdit($msg = ''): void
301
    {
302
        $data = $this->misc->getDatabaseAccessor();
303
304
        $this->printTrail('user');
305
        $this->printTitle($this->lang['stralter'], 'pg.user.alter');
306
        $this->printMsg($msg);
307
308
        $userdata = $data->getUser($_REQUEST['username']);
309
310
        if (0 < $userdata->recordCount()) {
311
            $server_info = $this->misc->getServerInfo();
312
            $canRename = $data->hasUserRename() && ($_REQUEST['username'] !== $server_info['username']);
313
            $userdata->fields['usesuper'] = $data->phpBool($userdata->fields['usesuper']);
314
            $userdata->fields['usecreatedb'] = $data->phpBool($userdata->fields['usecreatedb']);
315
316
            if (!isset($_POST['formExpires'])) {
317
                if ($canRename) {
318
                    $_POST['newname'] = $userdata->fields['usename'];
319
                }
320
321
                if ($userdata->fields['usesuper']) {
322
                    $_POST['formSuper'] = '';
323
                }
324
325
                if ($userdata->fields['usecreatedb']) {
326
                    $_POST['formCreateDB'] = '';
327
                }
328
329
                $_POST['formExpires'] = 'infinity' === $userdata->fields['useexpires'] ? '' : $userdata->fields['useexpires'];
330
                $_POST['formPassword'] = '';
331
            }
332
333
            echo '<form action="' . \containerInstance()->subFolder . '/src/views/users" method="post">' . \PHP_EOL;
334
            echo '<table>' . \PHP_EOL;
335
            echo "\t<tr>\n\t\t<th class=\"data left\">{$this->lang['strusername']}</th>" . \PHP_EOL;
336
            echo "\t\t<td class=\"data1\">", ($canRename ? "<input name=\"newname\" size=\"15\" maxlength=\"{$data->_maxNameLen}\" value=\"" . \htmlspecialchars($_POST['newname']) . '" />' : $this->misc->printVal($userdata->fields['usename'])), "</td>\n\t</tr>" . \PHP_EOL;
0 ignored issues
show
This line exceeds maximum limit of 190 characters; contains 273 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
337
            echo "\t<tr>\n\t\t<th class=\"data left\"><label for=\"formSuper\">{$this->lang['strsuper']}</label></th>" . \PHP_EOL;
338
            echo "\t\t<td class=\"data1\"><input type=\"checkbox\" id=\"formSuper\" name=\"formSuper\"",
339
            (isset($_POST['formSuper'])) ? ' checked="checked"' : '', " /></td>\n\t</tr>" . \PHP_EOL;
340
            echo "\t<tr>\n\t\t<th class=\"data left\"><label for=\"formCreateDB\">{$this->lang['strcreatedb']}</label></th>" . \PHP_EOL;
341
            echo "\t\t<td class=\"data1\"><input type=\"checkbox\" id=\"formCreateDB\" name=\"formCreateDB\"",
342
            (isset($_POST['formCreateDB'])) ? ' checked="checked"' : '', " /></td>\n\t</tr>" . \PHP_EOL;
343
            echo "\t<tr>\n\t\t<th class=\"data left\">{$this->lang['strexpires']}</th>" . \PHP_EOL;
344
            echo "\t\t<td class=\"data1\"><input size=\"16\" name=\"formExpires\" value=\"", \htmlspecialchars($_POST['formExpires']), "\" /></td>\n\t</tr>" . \PHP_EOL;
345
            echo "\t<tr>\n\t\t<th class=\"data left\">{$this->lang['strpassword']}</th>" . \PHP_EOL;
346
            echo "\t\t<td class=\"data1\"><input type=\"password\" size=\"16\" name=\"formPassword\" value=\"", \htmlspecialchars($_POST['formPassword']), "\" /></td>\n\t</tr>" . \PHP_EOL;
347
            echo "\t<tr>\n\t\t<th class=\"data left\">{$this->lang['strconfirm']}</th>" . \PHP_EOL;
348
            echo "\t\t<td class=\"data1\"><input type=\"password\" size=\"16\" name=\"formConfirm\" value=\"\" /></td>\n\t</tr>" . \PHP_EOL;
349
            echo '</table>' . \PHP_EOL;
350
            echo '<p><input type="hidden" name="action" value="save_edit" />' . \PHP_EOL;
351
            echo '<input type="hidden" name="username" value="', \htmlspecialchars($_REQUEST['username']), '" />' . \PHP_EOL;
352
            echo $this->view->form;
353
            echo "<input type=\"submit\" name=\"alter\" value=\"{$this->lang['stralter']}\" />" . \PHP_EOL;
354
            echo \sprintf('<input type="submit" name="cancel" value="%s"  /></p>%s', $this->lang['strcancel'], \PHP_EOL);
355
            echo '</form>' . \PHP_EOL;
356
        } else {
357
            echo "<p>{$this->lang['strnodata']}</p>" . \PHP_EOL;
358
        }
359
    }
360
361
    /**
362
     * Function to save after editing a user.
363
     */
364
    public function doSaveEdit(): void
365
    {
366
        $data = $this->misc->getDatabaseAccessor();
367
368
        // Check name and password
369
        if (isset($_POST['newname']) && '' === $_POST['newname']) {
370
            $this->doEdit($this->lang['struserneedsname']);
371
        } elseif ($_POST['formPassword'] !== $_POST['formConfirm']) {
372
            $this->doEdit($this->lang['strpasswordconfirm']);
373
        } else {
374
            if (isset($_POST['newname'])) {
375
                $status = $data->setRenameUser($_POST['username'], $_POST['formPassword'], isset($_POST['formCreateDB']), isset($_POST['formSuper']), $_POST['formExpires'], $_POST['newname']);
0 ignored issues
show
This line exceeds maximum limit of 190 characters; contains 192 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
376
            } else {
377
                $status = $data->setUser($_POST['username'], $_POST['formPassword'], isset($_POST['formCreateDB']), isset($_POST['formSuper']), $_POST['formExpires']);
378
            }
379
380
            if (0 === $status) {
381
                $this->doDefault($this->lang['struserupdated']);
382
            } else {
383
                $this->doEdit($this->lang['struserupdatedbad']);
384
            }
385
        }
386
    }
387
388
    /**
389
     * Show confirmation of drop and perform actual drop.
390
     *
391
     * @param mixed $confirm
392
     */
393
    public function doDrop($confirm): void
394
    {
395
        $data = $this->misc->getDatabaseAccessor();
396
397
        if ($confirm) {
398
            $this->printTrail('user');
399
            $this->printTitle($this->lang['strdrop'], 'pg.user.drop');
400
401
            echo '<p>', \sprintf($this->lang['strconfdropuser'], $this->misc->printVal($_REQUEST['username'])), '</p>' . \PHP_EOL;
402
403
            echo '<form action="' . \containerInstance()->subFolder . '/src/views/users" method="post">' . \PHP_EOL;
404
            echo '<p><input type="hidden" name="action" value="drop" />' . \PHP_EOL;
405
            echo '<input type="hidden" name="username" value="', \htmlspecialchars($_REQUEST['username']), '" />' . \PHP_EOL;
406
            echo $this->view->form;
407
            echo "<input type=\"submit\" name=\"drop\" value=\"{$this->lang['strdrop']}\" />" . \PHP_EOL;
408
            echo \sprintf('<input type="submit" name="cancel" value="%s"  /></p>%s', $this->lang['strcancel'], \PHP_EOL);
409
            echo '</form>' . \PHP_EOL;
410
        } else {
411
            $status = $data->dropUser($_REQUEST['username']);
412
413
            if (0 === $status) {
414
                $this->doDefault($this->lang['struserdropped']);
415
            } else {
416
                $this->doDefault($this->lang['struserdroppedbad']);
417
            }
418
        }
419
    }
420
421
    /**
422
     * Displays a screen where they can enter a new user.
423
     *
424
     * @param mixed $msg
425
     */
426
    public function doCreate($msg = ''): void
427
    {
428
        $data = $this->misc->getDatabaseAccessor();
429
430
        $this->coalesceArr($_POST, 'formUsername', '');
431
432
        $this->coalesceArr($_POST, 'formPassword', '');
433
434
        $this->coalesceArr($_POST, 'formConfirm', '');
435
436
        $this->coalesceArr($_POST, 'formExpires', '');
437
438
        $this->printTrail('server');
439
        $this->printTitle($this->lang['strcreateuser'], 'pg.user.create');
440
        $this->printMsg($msg);
441
442
        echo '<form action="' . \containerInstance()->subFolder . '/src/views/users" method="post">' . \PHP_EOL;
443
        echo '<table>' . \PHP_EOL;
444
        echo "\t<tr>\n\t\t<th class=\"data left required\">{$this->lang['strusername']}</th>" . \PHP_EOL;
445
        echo "\t\t<td class=\"data1\"><input size=\"15\" maxlength=\"{$data->_maxNameLen}\" name=\"formUsername\" value=\"", \htmlspecialchars($_POST['formUsername']), "\" /></td>\n\t</tr>" . \PHP_EOL;
0 ignored issues
show
This line exceeds maximum limit of 190 characters; contains 201 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
446
        echo "\t<tr>\n\t\t<th class=\"data left\">{$this->lang['strpassword']}</th>" . \PHP_EOL;
447
        echo "\t\t<td class=\"data1\"><input size=\"15\" type=\"password\" name=\"formPassword\" value=\"", \htmlspecialchars($_POST['formPassword']), "\" /></td>\n\t</tr>" . \PHP_EOL;
448
        echo "\t<tr>\n\t\t<th class=\"data left\">{$this->lang['strconfirm']}</th>" . \PHP_EOL;
449
        echo "\t\t<td class=\"data1\"><input size=\"15\" type=\"password\" name=\"formConfirm\" value=\"", \htmlspecialchars($_POST['formConfirm']), "\" /></td>\n\t</tr>" . \PHP_EOL;
450
        echo "\t<tr>\n\t\t<th class=\"data left\"><label for=\"formSuper\">{$this->lang['strsuper']}</label></th>" . \PHP_EOL;
451
        echo "\t\t<td class=\"data1\"><input type=\"checkbox\" id=\"formSuper\" name=\"formSuper\"",
452
        (isset($_POST['formSuper'])) ? ' checked="checked"' : '', " /></td>\n\t</tr>" . \PHP_EOL;
453
        echo "\t<tr>\n\t\t<th class=\"data left\"><label for=\"formCreateDB\">{$this->lang['strcreatedb']}</label></th>" . \PHP_EOL;
454
        echo "\t\t<td class=\"data1\"><input type=\"checkbox\" id=\"formCreateDB\" name=\"formCreateDB\"",
455
        (isset($_POST['formCreateDB'])) ? ' checked="checked"' : '', " /></td>\n\t</tr>" . \PHP_EOL;
456
        echo "\t<tr>\n\t\t<th class=\"data left\">{$this->lang['strexpires']}</th>" . \PHP_EOL;
457
        echo "\t\t<td class=\"data1\"><input size=\"30\" name=\"formExpires\" value=\"", \htmlspecialchars($_POST['formExpires']), "\" /></td>\n\t</tr>" . \PHP_EOL;
458
        echo '</table>' . \PHP_EOL;
459
        echo '<p><input type="hidden" name="action" value="save_create" />' . \PHP_EOL;
460
        echo $this->view->form;
461
        echo "<input type=\"submit\" name=\"create\" value=\"{$this->lang['strcreate']}\" />" . \PHP_EOL;
462
        echo \sprintf('<input type="submit" name="cancel" value="%s"  /></p>%s', $this->lang['strcancel'], \PHP_EOL);
463
        echo '</form>' . \PHP_EOL;
464
    }
465
466
    /**
467
     * Actually creates the new user in the database.
468
     */
469
    public function doSaveCreate(): void
470
    {
471
        $data = $this->misc->getDatabaseAccessor();
472
473
        // Check data
474
        if ('' === $_POST['formUsername']) {
475
            $this->doCreate($this->lang['struserneedsname']);
476
        } elseif ($_POST['formPassword'] !== $_POST['formConfirm']) {
477
            $this->doCreate($this->lang['strpasswordconfirm']);
478
        } else {
479
            $status = $data->createUser(
480
                $_POST['formUsername'],
481
                $_POST['formPassword'],
482
                isset($_POST['formCreateDB']),
483
                isset($_POST['formSuper']),
484
                $_POST['formExpires'],
485
                []
486
            );
487
488
            if (0 === $status) {
489
                $this->doDefault($this->lang['strusercreated']);
490
            } else {
491
                $this->doCreate($this->lang['strusercreatedbad']);
492
            }
493
        }
494
    }
495
}
496