1<?php
2
3use dokuwiki\Form\Form;
4use dokuwiki\plugin\twofactor\Manager;
5use dokuwiki\plugin\twofactor\Settings;
6
7/**
8 *  Twofactor Manager
9 *
10 *  Allows to reset a user's twofactor data
11 */
12class admin_plugin_twofactor extends DokuWiki_Admin_Plugin
13{
14    /** @var array currently active filters */
15    protected $filter = [];
16    /** @var int index of first user to be displayed */
17    protected $start = 0;
18    /** @var int number of users to list on one page */
19    protected $pagesize = 20;
20    /** @var Manager */
21    protected $manager;
22
23    /**
24     * Constructor
25     */
26    public function __construct()
27    {
28        $this->manager = Manager::getInstance();
29        if (!$this->manager->isReady()) return;
30
31        global $INPUT;
32
33        $this->filter = $INPUT->arr('filter');
34        $this->start = $INPUT->int('start');
35    }
36
37    /** @inheritdoc */
38    public function handle()
39    {
40        global $INPUT;
41
42        if ($INPUT->has('reset') && checkSecurityToken()) {
43            $userdel = $INPUT->extract('reset')->str('reset');
44            if ($userdel == $INPUT->server->str('REMOTE_USER')) {
45                msg($this->getLang('reset_not_self'), -1);
46                return;
47            }
48            foreach ($this->manager->getAllProviders() as $providerID => $provider) {
49                (new Settings($providerID, $userdel))->purge();
50            }
51            (new Settings('twofactor', $userdel))->purge();
52        }
53
54        // when a search is initiated, roll back to first page
55        if ($INPUT->has('search')) {
56            $this->start = 0;
57        }
58    }
59
60    /** @inheritdoc */
61    public function html()
62    {
63        echo $this->locale_xhtml('admin');
64        if (!$this->manager->isReady()) {
65            return true;
66        }
67
68        $users = $this->getUserData($this->filter);
69        $usercount = count($users);
70        $users = $this->applyPagination($users, $this->start, $this->pagesize);
71
72        $form = new Form(['method' => 'POST', 'class' => 'plugin_twofactor_admin']);
73        $form->setHiddenField('do', 'admin');
74        $form->setHiddenField('page', 'twofactor');
75        $form->setHiddenField('start', $this->start);
76
77        $form->addTagOpen('div')->addClass('table');
78        $form->addTagOpen('table')->addClass('inline');
79        $form = $this->addTableHead($form);
80
81        $form->addTagOpen('tbody');
82        foreach ($users as $user => $userinfo) {
83            $form = $this->addTableUser($form, $user, $userinfo);
84        }
85        $form->addTagClose('tbody');
86
87        $form->addTagOpen('tfooter');
88        $form = $this->addTablePagination($form, $usercount, $this->start, $this->pagesize);
89        $form->addTagClose('tfooter');
90
91        $form->addTagClose('table');
92        $form->addTagClose('div');
93
94        echo $form->toHTML();
95
96        return true;
97    }
98
99    /**
100     * Add the table headers to the table in the given form
101     * @param Form $form
102     * @return Form
103     */
104    protected function addTableHead(Form $form)
105    {
106        $form->addTagOpen('thead');
107
108        // header
109        $form->addTagOpen('tr');
110        $form->addTagOpen('th');
111        $form->addHTML($this->getLang('user_id'));
112        $form->addTagClose('th');
113        $form->addTagOpen('th');
114        $form->addHTML($this->getLang('user_name'));
115        $form->addTagClose('th');
116        $form->addTagOpen('th');
117        $form->addHTML($this->getLang('user_mail'));
118        $form->addTagClose('th');
119        $form->addTagOpen('th');
120        $form->addHTML($this->getLang('action'));
121        $form->addTagClose('th');
122        $form->addTagClose('tr');
123
124        // filter
125        $form->addTagOpen('tr');
126        $form->addTagOpen('th');
127        $form->addTextInput('filter[user]');
128        $form->addTagClose('th');
129        $form->addTagOpen('th');
130        $form->addTextInput('filter[name]');
131        $form->addTagClose('th');
132        $form->addTagOpen('th');
133        $form->addTextInput('filter[mail]');
134        $form->addTagClose('th');
135        $form->addTagOpen('th');
136        $form->addButton('search', $this->getLang('search'))->attr('type', 'submit');
137        $form->addTagClose('th');
138        $form->addTagClose('tr');
139
140        $form->addTagClose('thead');
141        return $form;
142    }
143
144    /**
145     * Add
146     *
147     * @param Form $form
148     * @param $user
149     * @param $userinfo
150     * @return Form
151     */
152    protected function addTableUser(Form $form, $user, $userinfo)
153    {
154        $form->addTagOpen('tr');
155        $form->addTagOpen('td');
156        $form->addHTML(hsc($user));
157        $form->addTagClose('td');
158        $form->addTagOpen('td');
159        $form->addHTML(hsc($userinfo['name']));
160        $form->addTagClose('td');
161        $form->addTagOpen('td');
162        $form->addHTML(hsc($userinfo['mail']));
163        $form->addTagClose('td');
164        $form->addTagOpen('td');
165        $form->addButton('reset[' . $user . ']', $this->getLang('reset'))
166             ->attr('type', 'submit')
167             ->addClass('twofactor_resetconfirm');
168        $form->addTagClose('td');
169        $form->addTagClose('tr');
170        return $form;
171    }
172
173    /**
174     * Add the pagination buttons to the form
175     *
176     * @param Form $form
177     * @param int $usercount
178     * @param int $start
179     * @param int $pagesize
180     * @return Form
181     */
182    protected function addTablePagination(Form $form, $usercount, $start, $pagesize)
183    {
184        $form->addTagOpen('tr');
185        $form->addTagOpen('td')->attr('colspan', '4');
186        $form->addTagOpen('div')->addClass('pagination');
187
188        // start
189        $btn = $form->addButton('start', $this->getLang('start'))->val('0');
190        if ($start <= 0) $btn->attr('disabled', 'disabled');
191
192        // prev
193        $btn = $form->addButton('start', $this->getLang('prev'))->val($start - $pagesize);
194        if ($start - $pagesize < 0) $btn->attr('disabled', 'disabled');
195
196        // next
197        $btn = $form->addButton('start', $this->getLang('next'))->val($start + $pagesize);
198        if ($start + $pagesize >= $usercount) $btn->attr('disabled', 'disabled');
199
200        // last
201        $btn = $form->addButton('start', $this->getLang('last'))->val($usercount - $pagesize);
202        if ($usercount - $pagesize <= 0) $btn->attr('disabled', 'disabled');
203        if ($usercount - $pagesize == $start) $btn->attr('disabled', 'disabled');
204
205        $form->addTagClose('div');
206        $form->addTagClose('td');
207        $form->addTagClose('tr');
208
209        return $form;
210    }
211
212    /**
213     * Get the filtered users that have a twofactor provider set
214     *
215     * @param array $filter
216     * @return array
217     */
218    protected function getUserData($filter)
219    {
220        $users = Settings::findUsers('twofactor');
221        return $this->applyFilter($users, $filter);
222    }
223
224    /**
225     * Apply the given filters and return user details
226     *
227     * @param string[] $users simple list of user names
228     * @param array $filter
229     * @return array (user => userdata)
230     */
231    protected function applyFilter($users, $filter)
232    {
233        global $auth;
234        $filtered = [];
235
236        $hasFilter = (bool)array_filter(array_values($filter));
237        foreach ($users as $user) {
238            $userdata = $auth->getUserData($user);
239            if (!$userdata) continue;
240            $userdata['user'] = $user;
241            if ($hasFilter) {
242                foreach ($filter as $key => $value) {
243                    $q = preg_quote($value, '/');
244                    if ($value && preg_match("/$q/iu", $userdata[$key])) {
245                        $filtered[$user] = $userdata;
246                        continue 2;
247                    }
248                }
249            } else {
250                $filtered[$user] = $userdata;
251            }
252        }
253        return $filtered;
254    }
255
256    /**
257     * Get the current page of users
258     *
259     * @param array $users
260     * @param int $start
261     * @param int $pagesize
262     * @return array
263     */
264    protected function applyPagination($users, $start, $pagesize)
265    {
266        return array_slice($users, $start, $pagesize, true);
267    }
268
269}
270