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