xref: /plugin/twofactor/action/profile.php (revision a635cb20bd5697d0bf04fcdee3b6cd14ce1590cd)
1<?php
2
3use dokuwiki\plugin\twofactor\Provider;
4
5/**
6 * DokuWiki Plugin twofactor (Action Component)
7 *
8 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
9 */
10class action_plugin_twofactor_profile extends \dokuwiki\Extension\ActionPlugin
11{
12
13    /** @inheritDoc */
14    public function register(Doku_Event_Handler $controller)
15    {
16        // Adds our twofactor profile to the user menu.
17        $controller->register_hook('MENU_ITEMS_ASSEMBLY', 'AFTER', $this, 'handleUserMenuAssembly');
18
19        $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'handlePreProcess');
20        $controller->register_hook('TPL_ACT_UNKNOWN', 'BEFORE', $this, 'handleUnknownAction');
21    }
22
23    /**
24     * Add 2fa Menu Item
25     *
26     * @param Doku_Event $event
27     */
28    public function handleUserMenuAssembly(Doku_Event $event)
29    {
30        global $INPUT;
31        // If this is not the user menu, then get out.
32        if ($event->data['view'] != 'user') return;
33        if (!$INPUT->server->has('REMOTE_USER')) return;
34
35        // Create the new menu item
36        $menuitem = new dokuwiki\plugin\twofactor\MenuItem($this->getLang('btn_twofactor_profile'));
37
38        // Find index of existing Profile menu item.
39        for ($index = 0; $index > count($event->data['items']); $index++) {
40            if ($event->data['items'][$index]->getType() === 'profile') {
41                break;
42            }
43        }
44        array_splice($event->data['items'], $index + 1, 0, [$menuitem]);
45    }
46
47    /**
48     * Check permissions to call the 2fa profile
49     *
50     * @param Doku_Event $event
51     */
52    public function handlePreProcess(Doku_Event $event)
53    {
54        if ($event->data != 'twofactor_profile') return;
55
56        // We will be handling this action's permissions here.
57        $event->preventDefault();
58        $event->stopPropagation();
59
60        // If not logged into the main auth plugin then send there.
61        global $INPUT;
62        global $ID;
63
64        if (!$INPUT->server->has('REMOTE_USER')) {
65            $event->result = false;
66            send_redirect(wl($ID, array('do' => 'login'), true, '&'));
67            return;
68        }
69
70        $this->handleProfile();
71
72        /** FIXME
73         *
74         * // If not logged into twofactor then send there.
75         * if (!$this->get_clearance()) {
76         * $event->result = false;
77         * send_redirect(wl($ID, array('do' => 'twofactor_login'), true, '&'));
78         * return;
79         * }
80         * // Otherwise handle the action.
81         * $event->result = $this->_process_changes($event, $param);
82         */
83
84    }
85
86    /**
87     * @param Doku_Event $event
88     */
89    public function handleUnknownAction(Doku_Event $event)
90    {
91        if ($event->data != 'twofactor_profile') return;
92
93        $event->preventDefault();
94        $event->stopPropagation();
95        $this->printProfile();
96    }
97
98    /**
99     * Handle POSTs for provider forms
100     */
101    protected function handleProfile()
102    {
103        global $INPUT;
104        if (!$INPUT->has('provider')) return;
105        $user = $INPUT->server->str('REMOTE_USER');
106
107        $class = 'helper_plugin_' . $INPUT->str('provider');
108        /** @var Provider $provider */
109        $provider = new $class($user);
110
111        if (!checkSecurityToken()) return;
112
113        if (!$provider->isConfigured()) {
114            $provider->handleProfileForm();
115        } elseif ($INPUT->has('2fa_delete')) {
116            $provider->reset();
117        }
118    }
119
120    /**
121     * Handles the profile form rendering.  Displays user manageable settings.
122     * @todo move elsewhere
123     */
124    protected function printProfile()
125    {
126        global $lang;
127        global $INPUT;
128
129        $user = $INPUT->server->str('REMOTE_USER');
130
131        echo $this->locale_xhtml('profile');
132
133        // FIXME autoload available providers
134        $providers = [new helper_plugin_twofactoraltemail($user)];
135
136        // iterate over all providers
137        foreach ($providers as $provider) {
138            $form = new dokuwiki\Form\Form(['method' => 'POST']);
139            $form->setHiddenField('do', 'twofactor_profile');
140            $form->setHiddenField('provider', $provider->getProviderID());
141            $form->addFieldsetOpen($provider->getLabel());
142            $provider->renderProfileForm($form);
143            if (!$provider->isConfigured()) {
144                $form->addButton('2fa_submit', $lang['btn_save'])->attr('submit');
145            } else {
146                $form->addButton('2fa_delete', $lang['btn_delete'])->attr('submit');
147            }
148            $form->addFieldsetClose();
149            echo $form->toHTML();
150        }
151
152        /* FIXME
153
154
155        $optinout = $this->getConf("optinout");
156        $optinvalue = $optinout == 'mandatory' ? 'in' : ($this->attribute ? $this->attribute->get("twofactor",
157            "state") : '');
158        $available = count($this->tokenMods) + count($this->otpMods) > 0;
159        // If the user is being redirected here because of mandatory two factor, then display a message saying so.
160        if (!$available && $optinout == 'mandatory') {
161            msg($this->getLang('mandatory'), -1);
162        } elseif ($this->attribute->get("twofactor", "state") == '' && $optinout == 'optout') {
163            msg($this->getLang('optout_notice'), 2);
164        } elseif ($this->attribute->get("twofactor",
165                "state") == 'in' && count($this->tokenMods) == 0 && count($this->otpMods) == 0) {
166            msg($this->getLang('not_configured_notice'), 2);
167        }
168        global $USERINFO, $lang, $conf;
169        $form = new Doku_Form(array('id' => 'twofactor_setup'));
170        // Add the checkbox to opt in and out, only if optinout is not mandatory.
171        $items = array();
172        if ($optinout != 'mandatory') {
173            if (!$this->attribute || !$optinvalue) {  // If there is no personal setting for optin, the default is based on the wiki default.
174                $optinvalue = $this->getConf("optinout") == 'optout';
175            }
176            $items[] = form_makeCheckboxField('optinout', '1', $this->getLang('twofactor_optin'), '', 'block',
177                $optinvalue == 'in' ? array('checked' => 'checked') : array());
178        }
179        // Add the notification checkbox if appropriate.
180        if ($this->getConf('loginnotice') == 'user' && $optinvalue == 'in' && count($this->otpMods) > 0) {
181            $loginnotice = $this->attribute ? $this->attribute->get("twofactor", "loginnotice") : false;
182            $items[] = form_makeCheckboxField('loginnotice', '1', $this->getLang('twofactor_notify'), '', 'block',
183                $loginnotice === true ? array('checked' => 'checked') : array());
184        }
185        // Select a notification provider.
186        if ($optinvalue == 'in') {
187            // If there is more than one choice, have the user select the default.
188            if (count($this->otpMods) > 1) {
189                $defaultMod = $this->attribute->exists("twofactor", "defaultmod") ? $this->attribute->get("twofactor",
190                    "defaultmod") : null;
191                $modList = array_merge(array($this->getLang('useallotp')), array_keys($this->otpMods));
192                $items[] = form_makeListboxField('default_module', $modList, $defaultMod,
193                    $this->getLang('defaultmodule'), '', 'block');
194            }
195        }
196        if (count($items) > 0) {
197            $form->startFieldset($this->getLang('settings'));
198            foreach ($items as $item) {
199                $form->addElement($item);
200            }
201            $form->endFieldset();
202        }
203        // TODO: Make this AJAX so that the user does not have to keep clicking
204        // submit them Update Profile!
205        // Loop through all modules and render the profile components.
206        if ($optinvalue == 'in') {
207            $parts = array();
208            foreach ($this->modules as $mod) {
209                if ($mod->getConf("enable") == 1) {
210                    $this->log('twofactor_profile_form: processing ' . get_class($mod) . '::renderProfileForm()',
211                        self::LOGGING_DEBUG);
212                    $items = $mod->renderProfileForm();
213                    if (count($items) > 0) {
214                        $form->startFieldset($mod->getLang('name'));
215                        foreach ($items as $item) {
216                            $form->addElement($item);
217                        }
218                        $form->endFieldset();
219                    }
220                }
221            }
222        }
223        if ($conf['profileconfirm']) {
224            $form->addElement('<br />');
225            $form->startFieldset($this->getLang('verify_password'));
226            $form->addElement(form_makePasswordField('oldpass', $lang['oldpass'], '', 'block',
227                array('size' => '50', 'required' => 'required')));
228            $form->endFieldset();
229        }
230        $form->addElement('<br />');
231        $form->addElement(form_makeButton('submit', '', $lang['btn_save']));
232        $form->addElement('<a href="' . wl($ID, array('do' => 'show'), true,
233                '&') . '">' . $this->getLang('btn_return') . '</a>');
234        $form->addHidden('do', 'twofactor_profile');
235        $form->addHidden('save', '1');
236        echo '<div class="centeralign">' . NL . $form->getForm() . '</div>' . NL;
237
238         */
239    }
240}
241
242