xref: /plugin/twofactor/action/profile.php (revision 4b9cff8a870f8684b87bcaa2b3aecd09387ffb2e)
1<?php
2
3use dokuwiki\Form\Form;
4use dokuwiki\plugin\twofactor\Manager;
5use dokuwiki\plugin\twofactor\MenuItem;
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    /** @var Manager */
15    protected $manager;
16
17    /**
18     * Constructor
19     */
20    public function __construct()
21    {
22        $this->manager = Manager::getInstance();
23    }
24
25    /** @inheritDoc */
26    public function register(Doku_Event_Handler $controller)
27    {
28        if (!$this->manager->isReady()) return;
29
30        // Adds our twofactor profile to the user menu.
31        $controller->register_hook('MENU_ITEMS_ASSEMBLY', 'AFTER', $this, 'handleUserMenuAssembly');
32
33        $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'handlePreProcess');
34        $controller->register_hook('TPL_ACT_UNKNOWN', 'BEFORE', $this, 'handleUnknownAction');
35    }
36
37    /**
38     * Add 2fa Menu Item
39     *
40     * @param Doku_Event $event
41     */
42    public function handleUserMenuAssembly(Doku_Event $event)
43    {
44        global $INPUT;
45        // If this is not the user menu, then get out.
46        if ($event->data['view'] != 'user') return;
47        if (!$INPUT->server->has('REMOTE_USER')) return;
48
49        // Create the new menu item
50        $menuitem = new MenuItem($this->getLang('btn_twofactor_profile'));
51
52        // Find index of existing Profile menu item.
53        for ($index = 0; $index > count($event->data['items']); $index++) {
54            if ($event->data['items'][$index]->getType() === 'profile') {
55                break;
56            }
57        }
58        array_splice($event->data['items'], $index + 1, 0, [$menuitem]);
59    }
60
61    /**
62     * Check permissions to call the 2fa profile
63     *
64     * @param Doku_Event $event
65     */
66    public function handlePreProcess(Doku_Event $event)
67    {
68        if ($event->data != 'twofactor_profile') return;
69
70        // We will be handling this action's permissions here.
71        $event->preventDefault();
72        $event->stopPropagation();
73
74        // If not logged into the main auth plugin then send there.
75        global $INPUT;
76        global $ID;
77
78        if (!$INPUT->server->has('REMOTE_USER')) {
79            $event->result = false;
80            send_redirect(wl($ID, array('do' => 'login'), true, '&'));
81            return;
82        }
83
84        if (strtolower($INPUT->server->str('REQUEST_METHOD')) == 'post') {
85            $this->handleProfile();
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 (!checkSecurityToken()) return;
109
110        if ($INPUT->has('2fa_optout') && $this->getConf('optinout') === 'optout') {
111            $this->manager->userOptOutState($INPUT->bool('optout'));
112            return;
113        }
114
115        if (!$INPUT->has('provider')) return;
116        $providers = $this->manager->getAllProviders();
117        if (!isset($providers[$INPUT->str('provider')])) return;
118        $provider = $providers[$INPUT->str('provider')];
119
120        if (!$provider->isConfigured()) {
121            $provider->handleProfileForm();
122        } elseif ($INPUT->has('2fa_delete')) {
123            $provider->reset();
124            $this->manager->getUserDefaultProvider(); // resets the default to the next available
125        } elseif ($INPUT->has('2fa_default')) {
126            $this->manager->setUserDefaultProvider($provider);
127        }
128    }
129
130    /**
131     * Handles the profile form rendering.  Displays user manageable settings.
132     *
133     * @todo split up in smaller methods
134     */
135    protected function printProfile()
136    {
137        global $lang;
138
139        echo $this->locale_xhtml('profile');
140
141        switch ($this->getConf('optinout')) {
142            case 'optout':
143                $form = new Form(['method' => 'post']);
144                $form->addFieldsetOpen('Opt Out');
145                $form->addHTML('<p>This wiki highly recomends 2fa, if you don\'t want it opt out here...</p>');
146                $cb = $form->addCheckbox('optout', 'I understand my account is less secure. I opt out');
147                if ($this->manager->userOptOutState()) {
148                    $cb->attr('checked', 'checked');
149                }
150                $form->addButton('2fa_optout', 'Save');
151                $form->addFieldsetClose();
152                echo $form->toHTML();
153
154                // when user opted out, don't show the rest of the form
155                if ($this->manager->userOptOutState()) {
156                    return;
157                }
158                break;
159            case 'optin':
160                echo '<p>Using 2fa is not mandatory but recommended. Please configure at least one of the providers...</p>';
161                break;
162            case 'mandatory':
163                echo '<p>The use of 2fa is mandatory you must configure at least one provider before using the wiki...</p>';
164                break;
165        }
166
167        // default provider selection
168        $userproviders = $this->manager->getUserProviders();
169        $default = $this->manager->getUserDefaultProvider();
170        if (count($userproviders)) {
171            $form = new Form(['method' => 'POST']);
172            $form->addFieldsetOpen('Default Provider');
173            foreach ($userproviders as $provider) {
174                $form->addRadioButton('provider', $provider->getLabel())
175                     ->val($provider->getProviderID())
176                     ->attr('checked', $provider->getProviderID() === $default->getProviderID());
177            }
178            $form->addButton('2fa_default', $lang['btn_save'])->attr('submit');
179            $form->addFieldsetClose();
180            echo $form->toHTML();
181        }
182
183        // iterate over all providers
184        $providers = $this->manager->getAllProviders();
185        foreach ($providers as $provider) {
186            $form = new dokuwiki\Form\Form(['method' => 'POST']);
187            $form->setHiddenField('do', 'twofactor_profile');
188            $form->setHiddenField('provider', $provider->getProviderID());
189            $form->addFieldsetOpen($provider->getLabel());
190            $provider->renderProfileForm($form);
191            if (!$provider->isConfigured()) {
192                $form->addButton('2fa_submit', $lang['btn_save'])->attr('submit');
193            } else {
194                $form->addButton('2fa_delete', $lang['btn_delete'])->attr('submit');
195            }
196            $form->addFieldsetClose();
197            echo $form->toHTML();
198        }
199    }
200}
201
202