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