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