1<?php
2
3use dokuwiki\Extension\CLIPlugin;
4use dokuwiki\Extension\AuthPlugin;
5use splitbrain\phpcli\Options;
6use splitbrain\phpcli\TableFormatter;
7
8/**
9 * Class cli_plugin_usermanager
10 *
11 * Command Line component for the usermanager
12 *
13 * @license GPL2
14 * @author Karsten Kosmala <karsten.kosmala@gmail.com>
15 */
16class cli_plugin_usermanager extends CLIPlugin
17{
18    /** @inheritdoc */
19    protected function setup(Options $options)
20    {
21        // general setup
22        $options->setHelp(
23            "Manage users for this DokuWiki instance\n"
24        );
25
26        // list
27        $options->registerCommand('list', 'List users');
28        $options->registerOption('verbose', 'Show detailed user information', 'v', false, 'list');
29
30        // add
31        $options->registerCommand('add', 'Add an user to auth backend');
32        $options->registerArgument('login', 'Username', true, 'add');
33        $options->registerArgument('mail', 'Email address', true, 'add');
34        $options->registerArgument('name', 'Full name', false, 'add');
35        $options->registerArgument('groups', 'Groups to be added, comma-seperated', false, 'add');
36        $options->registerArgument('password', 'Password to set', false, 'add');
37        $options->registerOption('notify', 'Notify user', 'n', false, 'add');
38
39        // delete
40        $options->registerCommand('delete', 'Deletes user(s) from auth backend');
41        $options->registerArgument('name', 'Username(s), comma-seperated', true, 'delete');
42
43        // add to group
44        $options->registerCommand('addtogroup', 'Add user to group(s)');
45        $options->registerArgument('name', 'Username', true, 'addtogroup');
46        $options->registerArgument('group', 'Group(s), comma-seperated', true, 'addtogroup');
47
48        // remove from group
49        $options->registerCommand('removefromgroup', 'Remove user from group(s)');
50        $options->registerArgument('name', 'Username', true, 'removefromgroup');
51        $options->registerArgument('group', 'Group(s), comma-separated', true, 'removefromgroup');
52    }
53
54    /** @inheritdoc */
55    protected function main(Options $options)
56    {
57        auth_setup();
58
59        /** @var AuthPlugin $auth */
60        global $auth;
61
62        if (!$auth instanceof AuthPlugin) {
63            $this->error($this->getLang('noauth'));
64            return 1;
65        }
66
67        switch ($options->getCmd()) {
68            case 'list':
69                $ret = $this->cmdList($options->getOpt('verbose'));
70                break;
71            case 'add':
72                $ret = $this->cmdAdd($options->getOpt('notify'), $options->getArgs());
73                break;
74            case 'delete':
75                $ret = $this->cmdDelete($options->getArgs());
76                break;
77            case 'addtogroup':
78                $ret = $this->cmdAddToGroup($options->getArgs());
79                break;
80            case 'removefromgroup':
81                $ret = $this->cmdRemoveFromGroup($options->getArgs());
82                break;
83
84            default:
85                echo $options->help();
86                $ret = 0;
87        }
88
89        exit($ret);
90    }
91
92    /**
93     * @param bool $showdetails
94     * @return int
95     */
96    protected function cmdList(bool $showdetails)
97    {
98        /** @var AuthPlugin $auth */
99        global $auth;
100
101        if (!$auth->canDo('getUsers')) {
102            $this->error($this->getLang('nosupport'));
103            return 1;
104        } else {
105            $this->listUsers($showdetails);
106        }
107
108        return 0;
109    }
110
111    /**
112     * List the given users
113     *
114     * @param bool $details display details
115     */
116    protected function listUsers(bool $details = false)
117    {
118        /** @var AuthPlugin $auth */
119        global $auth;
120        $list = $auth->retrieveUsers();
121
122        $tr = new TableFormatter($this->colors);
123
124        foreach ($list as $username => $user) {
125            $content = [$username];
126            if ($details) {
127                $content[] = $user['name'];
128                $content[] = $user['mail'];
129                $content[] = implode(", ", $user['grps']);
130            }
131            echo $tr->format(
132                [15, 25, 25, 15],
133                $content
134            );
135        }
136    }
137
138    /**
139     * Adds an user
140     *
141     * @param bool $notify display details
142     * @param array $args
143     * @return int
144     */
145    protected function cmdAdd(bool $notify, array $args)
146    {
147        /** @var AuthPlugin $auth */
148        global $auth;
149
150        if (!$auth->canDo('addUser')) {
151            $this->error($this->getLang('nosupport'));
152            return 1;
153        }
154
155        [$login, $mail, $name, $grps, $pass] = $args;
156        $grps = array_filter(array_map('trim', explode(',', $grps)));
157
158        if ($auth->canDo('modPass')) {
159            if (empty($pass)) {
160                if ($notify) {
161                    $pass = auth_pwgen($login);
162                } else {
163                    $this->error($this->getLang('add_fail'));
164                    $this->error($this->getLang('addUser_error_missing_pass'));
165                    return 1;
166                }
167            }
168        } elseif (!empty($pass)) {
169            $this->error($this->getLang('add_fail'));
170            $this->error($this->getLang('addUser_error_modPass_disabled'));
171            return 1;
172        }
173
174        if ($auth->triggerUserMod('create', [$login, $pass, $name, $mail, $grps])) {
175            $this->success($this->getLang('add_ok'));
176        } else {
177            $this->printErrorMessages();
178            $this->error($this->getLang('add_fail'));
179            $this->error($this->getLang('addUser_error_create_event_failed'));
180            return 1;
181        }
182
183        return 0;
184    }
185
186    /**
187     * Deletes users
188     * @param array $args
189     * @return int
190     */
191    protected function cmdDelete(array $args)
192    {
193        /** @var AuthPlugin $auth */
194        global $auth;
195
196        if (!$auth->canDo('delUser')) {
197            $this->error($this->getLang('nosupport'));
198            return 1;
199        }
200
201        $users = explode(',', $args[0]);
202        $count = $auth->triggerUserMod('delete', [$users]);
203
204        if ($count != count($users)) {
205            $this->printErrorMessages();
206            $part1 = str_replace('%d', $count, $this->getLang('delete_ok'));
207            $part2 = str_replace('%d', (count($users) - $count), $this->getLang('delete_fail'));
208            $this->error("$part1, $part2");
209            return 1;
210        }
211
212        return 0;
213    }
214
215    /**
216     * Adds an user to group(s)
217     *
218     * @param array $args
219     * @return int
220     */
221    protected function cmdAddToGroup(array $args)
222    {
223        /** @var AuthPlugin $auth */
224        global $auth;
225
226        [$name, $newgrps] = $args;
227        $newgrps = array_filter(array_map('trim', explode(',', $newgrps)));
228        $oldinfo = $auth->getUserData($name);
229        $changes = [];
230
231        if ($newgrps !== [] && $auth->canDo('modGroups')) {
232            $changes['grps'] = $oldinfo['grps'];
233            foreach ($newgrps as $group) {
234                if (!in_array($group, $oldinfo['grps'])) {
235                    $changes['grps'][] = $group;
236                }
237            }
238        }
239
240        if (!empty(array_diff($changes['grps'], $oldinfo['grps']))) {
241            if ($auth->triggerUserMod('modify', [$name, $changes])) {
242                $this->success($this->getLang('update_ok'));
243            } else {
244                $this->printErrorMessages();
245                $this->error($this->getLang('update_fail'));
246                return 1;
247            }
248        }
249
250        return 0;
251    }
252
253    /**
254     * Removes an user from group(s)
255     *
256     * @param array $args
257     * @return int
258     */
259    protected function cmdRemoveFromGroup(array $args)
260    {
261        /** @var AuthPlugin $auth */
262        global $auth;
263
264        [$name, $grps] = $args;
265        $grps = array_filter(array_map('trim', explode(',', $grps)));
266        $oldinfo = $auth->getUserData($name);
267        $changes = [];
268
269        if ($grps !== [] && $auth->canDo('modGroups')) {
270            $changes['grps'] = $oldinfo['grps'];
271            foreach ($grps as $group) {
272                if (($pos = array_search($group, $changes['grps'])) == !false) {
273                    unset($changes['grps'][$pos]);
274                }
275            }
276        }
277
278        if (!empty(array_diff($oldinfo['grps'], $changes['grps']))) {
279            if ($auth->triggerUserMod('modify', [$name, $changes])) {
280                $this->success($this->getLang('update_ok'));
281            } else {
282                $this->printErrorMessages();
283                $this->error($this->getLang('update_fail'));
284                return 1;
285            }
286        }
287
288        return 0;
289    }
290
291    /**
292     * Plugins triggered during user modification may cause failures and output messages via
293     * DokuWiki's msg() function
294     */
295    protected function printErrorMessages()
296    {
297        global $MSG;
298        if (isset($MSG)) {
299            foreach ($MSG as $msg) {
300                if ($msg['lvl'] === 'error') $this->error($msg['msg']);
301            }
302        }
303    }
304}
305