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