1 <?php
2 
3 use dokuwiki\Extension\CLIPlugin;
4 use dokuwiki\Extension\AuthPlugin;
5 use splitbrain\phpcli\Options;
6 use 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  */
16 class 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