1<?php
2
3use dokuwiki\plugin\twofactor\Manager;
4
5/**
6 * DokuWiki Plugin twofactor (Action Component)
7 *
8 * This adds 2fa handling to the resendpwd action. It will interrupt the normal, first step of the
9 * flow and insert our own 2fa form, initialized with the user provided in the reset form. When the user
10 * has successfully authenticated, the normal flow will continue. All within the do?do=resendpwd action.
11 *
12 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
13 * @author  Andreas Gohr <dokuwiki@cosmocode.de>
14 */
15class action_plugin_twofactor_resendpwd extends \dokuwiki\Extension\ActionPlugin
16{
17    /** @inheritDoc */
18    public function register(Doku_Event_Handler $controller)
19    {
20        $controller->register_hook(
21            'ACTION_ACT_PREPROCESS',
22            'BEFORE',
23            $this,
24            'handleActionPreProcess',
25            null,
26            Manager::EVENT_PRIORITY - 1
27        );
28
29        $controller->register_hook(
30            'TPL_ACT_UNKNOWN',
31            'BEFORE',
32            $this,
33            'handleTplActUnknown',
34            null,
35            Manager::EVENT_PRIORITY - 1
36        );
37    }
38
39    /**
40     * Event handler for ACTION_ACT_PREPROCESS
41     *
42     * @see https://www.dokuwiki.org/devel:events:ACTION_ACT_PREPROCESS
43     * @param Doku_Event $event Event object
44     * @param mixed $param optional parameter passed when event was registered
45     * @return void
46     */
47    public function handleActionPreProcess(Doku_Event $event, $param)
48    {
49        if ($event->data !== 'resendpwd') return;
50
51        global $INPUT;
52        if ($INPUT->has('pwauth')) return; // we're already in token phase, don't interrupt
53        if (!$INPUT->str('login')) return; // no user given yet, don't interrupt
54
55        $user = $INPUT->str('login');
56        $manager = Manager::getInstance();
57        $manager->setUser($user);
58
59        if (!$manager->isReady()) return; // no 2fa setup, don't interrupt
60        if (!count($manager->getUserProviders())) return; // no 2fa for this user, don't interrupt
61
62        $code = $INPUT->post->str('2fa_code');
63        $providerID = $INPUT->post->str('2fa_provider');
64        if ($code && $manager->verifyCode($code, $providerID)) {
65            // all is good, don't interrupt
66            Manager::destroyInstance(); // remove our instance so login.php can create a new one
67            return;
68        }
69
70        // we're still here, so we need to interrupt
71        $event->preventDefault();
72        $event->stopPropagation();
73
74        // next, we will overwrite the resendpwd form with our own in TPL_ACT_UNKNOWN
75    }
76
77    /**
78     * Event handler for TPL_ACT_UNKNOWN
79     *
80     * This is executed only when we prevented the default action in handleActionPreProcess()
81     *
82     * @see https://www.dokuwiki.org/devel:events:TPL_ACT_UNKNOWN
83     * @param Doku_Event $event Event object
84     * @param mixed $param optional parameter passed when event was registered
85     * @return void
86     */
87    public function handleTplActUnknown(Doku_Event $event, $param)
88    {
89        if ($event->data !== 'resendpwd') return;
90        $event->stopPropagation();
91        $event->preventDefault();
92
93        global $INPUT;
94
95        $providerID = $INPUT->post->str('2fa_provider');
96
97        $manager = Manager::getInstance();
98        $form = $manager->getCodeForm($providerID);
99
100        // overwrite form defaults, to redo the resendpwd action but with the code supplied
101        $form->setHiddenField('do', 'resendpwd');
102        $form->setHiddenField('login', $INPUT->str('login'));
103        $form->setHiddenField('save', 1);
104
105
106        echo '<div class="plugin_twofactor_login">';
107        echo inlineSVG(__DIR__ . '/../admin.svg');
108        echo $this->locale_xhtml('resendpwd');
109        echo $form->toHTML();
110        echo '</div>';
111    }
112}
113
114