Skip to content

Commit 3ca6f71

Browse files
committed
Add CLI commands for 2FA
The following commands are added: - `icingacli twofactor list` lists all users that have 2FA enabled. - `icingacli twofactor disable [<user>]` disables 2FA for a user.
1 parent 7ad2f7e commit 3ca6f71

1 file changed

Lines changed: 116 additions & 0 deletions

File tree

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
<?php
2+
3+
namespace Icinga\Clicommands;
4+
5+
use DateTime;
6+
use Icinga\Cli\Command;
7+
use Icinga\Common\Database;
8+
use ipl\Sql\Delete;
9+
use ipl\Sql\Select;
10+
use Throwable;
11+
12+
class TwofactorCommand extends Command
13+
{
14+
use Database;
15+
16+
/**
17+
* List all users that have 2FA enabled
18+
*
19+
* This command lists all users that have 2FA enabled and when they enabled it.
20+
*
21+
* USAGE
22+
*
23+
* icingacli twofactor list
24+
*/
25+
public function listAction(): void
26+
{
27+
$rows = $this->getDb()->select(
28+
(new Select())
29+
->from('icingaweb_2fa')
30+
->columns(['username', 'ctime'])
31+
)->fetchAll();
32+
33+
if (empty($rows)) {
34+
echo "Currently there are no users that have 2FA enabled.\n";
35+
36+
return;
37+
}
38+
39+
printf("%-20s %-20s\n", 'USER', 'ENABLED 2FA');
40+
41+
foreach ($rows as $row) {
42+
printf(
43+
"%-20s %-20s\n",
44+
$row->username,
45+
DateTime::createFromFormat('U.u', $row->ctime / 1000)->format('Y-m-d H:i:s')
46+
);
47+
}
48+
49+
echo "\n";
50+
}
51+
52+
/**
53+
* Disable 2FA for a specific user
54+
*
55+
* This command disables 2FA for a specific user. It asks for confirmation before deleting the secret. The deletion
56+
* cannot be undone.
57+
*
58+
* USAGE
59+
*
60+
* icingacli twofactor disable [<user>]
61+
*
62+
* OPTIONS
63+
*
64+
* --force Disable 2FA for user without confirmation
65+
*/
66+
public function disableAction(): void
67+
{
68+
$user = $this->params->shift();
69+
70+
if (! $user) {
71+
fwrite(STDERR, "User must be provided!\n");
72+
$this->showUsage('disable');
73+
74+
exit(1);
75+
}
76+
77+
if (! $this->params->shift('force')) {
78+
$input = readline(sprintf(
79+
"Are you sure you want to disable 2FA for user '%s'? This cannot be undone! [y/N] ",
80+
$user
81+
));
82+
83+
if (! $input || ! in_array(strtolower(trim($input)), ['y', 'yes'])) {
84+
echo "No changes made.\n";
85+
86+
return;
87+
}
88+
}
89+
90+
try {
91+
$delete = $this->getDb()->prepexec(
92+
(new Delete())
93+
->from('icingaweb_2fa')
94+
->where(['LOWER(username) = ?' => strtolower($user)])
95+
);
96+
} catch (Throwable $e) {
97+
fprintf(
98+
STDERR,
99+
"%s: Failed to disable 2FA for '%s': %s\n",
100+
$this->screen->colorize('ERROR', 'red'),
101+
$user,
102+
$e->getMessage()
103+
);
104+
105+
exit(1);
106+
}
107+
108+
if ($delete->rowCount() < 1) {
109+
printf("The user '%s' doesn't have 2FA enabled.\n", $user);
110+
111+
return;
112+
}
113+
114+
printf("Successfully disabled 2FA for user '%s'.\n", $user);
115+
}
116+
}

0 commit comments

Comments
 (0)