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