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