1<?php
2
3use dokuwiki\Form\Form;
4use dokuwiki\plugin\oauth\OAuthManager;
5use OAuth\Common\Http\Exception\TokenResponseException;
6
7/**
8 * DokuWiki Plugin oauth (Action Component)
9 *
10 * This adds buttons to the login page and initializes the oAuth flow by redirecting the user
11 * to the third party service
12 *
13 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
14 * @author  Andreas Gohr <andi@splitbrain.org>
15 */
16class action_plugin_oauth_login extends DokuWiki_Action_Plugin
17{
18    /** @var helper_plugin_oauth */
19    protected $hlp;
20
21    /**
22     * Constructor
23     *
24     * Initializes the helper
25     */
26    public function __construct()
27    {
28        $this->hlp = plugin_load('helper', 'oauth');
29    }
30
31    /**
32     * Registers a callback function for a given event
33     *
34     * @param Doku_Event_Handler $controller DokuWiki's event controller object
35     * @return void
36     */
37    public function register(Doku_Event_Handler $controller)
38    {
39        global $conf;
40        if ($conf['authtype'] != 'oauth') return;
41
42        $conf['profileconfirm'] = false; // password confirmation doesn't work with oauth only users
43
44        $controller->register_hook('DOKUWIKI_STARTED', 'BEFORE', $this, 'handleStart');
45        $controller->register_hook('HTML_LOGINFORM_OUTPUT', 'BEFORE', $this, 'handleOldLoginForm'); // @deprecated
46        $controller->register_hook('FORM_LOGIN_OUTPUT', 'BEFORE', $this, 'handleLoginForm');
47        $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'handleDoLogin');
48    }
49
50    /**
51     * Start an oAuth login or restore  environment after successful login
52     *
53     * @param Doku_Event $event
54     * @return void
55     */
56    public function handleStart(Doku_Event $event)
57    {
58        global $INPUT;
59
60        // see if a login needs to be started
61        $servicename = $INPUT->str('oauthlogin');
62        if (!$servicename) return;
63
64        try {
65            $om = new OAuthManager();
66            $om->startFlow($servicename);
67        } catch (TokenResponseException|Exception $e) {
68            $this->hlp->showException($e, 'login failed');
69        }
70    }
71
72    /**
73     * Add the oAuth login links to login form
74     *
75     * @param Doku_Event $event event object by reference
76     * @return void
77     * @deprecated can be removed in the future
78     */
79    public function handleOldLoginForm(Doku_Event $event)
80    {
81        /** @var Doku_Form $form */
82        $form = $event->data;
83        $html = $this->prepareLoginButtons();
84        if (!$html) return;
85
86        // remove login form if single service is set
87        $singleService = $this->getConf('singleService');
88        if ($singleService) {
89            $form->_content = [];
90        }
91
92        $form->_content[] = form_openfieldset(
93            [
94                '_legend' => $this->getLang('loginwith'),
95                'class' => 'plugin_oauth',
96            ]
97        );
98        $form->_content[] = $html;
99        $form->_content[] = form_closefieldset();
100    }
101
102    /**
103     * Add the oAuth login links to login form
104     *
105     * @param Doku_Event $event event object by reference
106     * @return void
107     * @deprecated can be removed in the future
108     */
109    public function handleLoginForm(Doku_Event $event)
110    {
111        /** @var Form $form */
112        $form = $event->data;
113        $html = $this->prepareLoginButtons();
114        if (!$html) return;
115
116        // remove login form if single service is set
117        $singleService = $this->getConf('singleService');
118        if ($singleService) {
119            do {
120                $form->removeElement(0);
121            } while ($form->elementCount() > 0);
122        }
123
124        $form->addFieldsetOpen($this->getLang('loginwith'))->addClass('plugin_oauth');
125        $form->addHTML($html);
126        $form->addFieldsetClose();
127    }
128
129    /**
130     * Create HTML for the various login buttons
131     *
132     * @return string the HTML
133     */
134    protected function prepareLoginButtons()
135    {
136        $html = '';
137
138        $validDomains = $this->hlp->getValidDomains();
139
140        if (count($validDomains) > 0) {
141            $html .= '<p class="plugin-oauth-emailrestriction">' . sprintf(
142                    $this->getLang('eMailRestricted'),
143                    '<b>' . join(', ', $validDomains) . '</b>'
144                ) . '</p>';
145        }
146
147        $html .= '<div>';
148        foreach ($this->hlp->listServices() as $service) {
149            $html .= $service->loginButton();
150        }
151        $html .= '</div>';
152
153        return $html;
154    }
155
156    /**
157     * When singleservice is wanted, do not show login, but execute login right away
158     *
159     * @param Doku_Event $event
160     * @return bool
161     */
162    public function handleDoLogin(Doku_Event $event)
163    {
164        global $ID;
165
166        if ($event->data != 'login') return true;
167
168        $singleService = $this->getConf('singleService');
169        if (!$singleService) return true;
170
171        $enabledServices = $this->hlp->listServices();
172        if (count($enabledServices) !== 1) {
173            msg($this->getLang('wrongConfig'), -1);
174            return false;
175        }
176
177        $service = array_shift($enabledServices);
178
179        $url = wl($ID, ['oauthlogin' => $service->getServiceID()], true, '&');
180        send_redirect($url);
181        return true; // never reached
182    }
183
184}
185