xref: /plugin/oauth/OAuthManager.php (revision 74b4d4a4cf1d79813740d8ba18696e5fb2b4089b)
1<?php
2
3namespace dokuwiki\plugin\oauth;
4
5use OAuth\Common\Http\Exception\TokenResponseException;
6
7class OAuthManager
8{
9
10    /**
11     * @throws Exception
12     * @throws TokenResponseException
13     */
14    public function startFlow($servicename)
15    {
16        // generate a new GUID to identify this user
17        $guid = bin2hex(random_bytes(16));
18
19        $session = Session::getInstance();
20        $session->setLoginData($servicename, $guid);
21
22        // fixme store environment
23        $service = $this->loadService($servicename);
24        $service->initOAuthService($guid);
25        $service->login(); // redirects
26
27    }
28
29    /**
30     * @return bool true if the login has been handled
31     * @throws Exception
32     * @throws \OAuth\Common\Exception\Exception
33     * @todo this probably moves over to auth
34     */
35    public function continueFlow()
36    {
37
38        return $this->loginByService() or
39            $this->loginBySession() or
40            $this->loginByCookie();
41
42    }
43
44    /**
45     * @return bool true if successful, false if not applies,
46     * @throws \OAuth\Common\Exception\Exception
47     * @throws Exception
48     */
49    protected function loginByService()
50    {
51        global $INPUT;
52
53        if (!$INPUT->get->has('code') && !$INPUT->get->has('oauth_token')) {
54            return false;
55        }
56
57        $session = Session::getInstance();
58
59        // init service from session
60        $logindata = $session->getLoginData();
61        if (!$logindata) return false;
62        $service = $this->loadService($logindata['servicename']);
63        $service->initOAuthService($logindata['guid']);
64        $session->clearLoginData();
65
66        // oAuth login
67        if (!$service->checkToken()) throw new Exception("Login failed");
68        $userdata = $service->getUser();
69
70        // processing
71        $userdata = $this->validateUserData($userdata, $logindata['servicename']);
72        $userdata = $this->processUserData($userdata, $logindata['servicename']);
73
74        // login
75        $session->setUser($userdata); // log in
76        $session->setCookie($logindata['servicename'], $logindata['guid']); // set cookie
77
78        // fixme restore environment
79
80        return true;
81    }
82
83    /**
84     * Login a user based on their current session data
85     *
86     * This will also log in plainauth users
87     *
88     * @return bool true if successful, false if not applies,
89     * @throws Exception
90     */
91    protected function loginBySession()
92    {
93        $session = Session::getInstance();
94        if (!$session->isValid()) {
95            $session->clear();
96            return false;
97        }
98
99        $userdata = $session->getUser();
100        $session->setUser($userdata, false); // does a login without resetting the time
101
102        return true;
103    }
104
105    /**
106     * Login a user based on their cookie and a previously saved access token
107     *
108     * @return bool true if successful, false if not applies,
109     * @throws Exception
110     */
111    protected function loginByCookie()
112    {
113        $session = Session::getInstance();
114        $cookie = $session->getCookie();
115        if (!$cookie) return false;
116
117        try {
118            $service = $this->loadService($cookie['servicename']);
119            $service->initOAuthService($cookie['guid']);
120        } catch (Exception $e) {
121            return false; // maybe cookie had old service that is no longer available
122        }
123
124        $userdata = $service->getUser(); // this should use a previously saved token
125        $session->setUser($userdata); // log in
126        return true;
127    }
128
129    /**
130     * Clean and validate the user data provided from the service
131     *
132     * @param array $userdata
133     * @param string $servicename
134     * @return array
135     * @throws Exception
136     * @todo test
137     */
138    protected function validateUserData($userdata, $servicename)
139    {
140        /** @var \auth_plugin_oauth */
141        global $auth;
142
143        // mail is required
144        if (empty($userdata['mail'])) {
145            throw new Exception("$servicename did not provide the an email address. Can't log you in");
146        }
147
148        // mail needs to be allowed
149        /** @var \helper_plugin_oauth $hlp */
150        $hlp = plugin_load('helper', 'oauth');
151        $hlp->checkMail($userdata['mail']);
152
153        // make username from mail if empty
154        $userdata['user'] = $auth->cleanUser((string)$userdata['user']);
155        if ($userdata === '') {
156            list($userdata['user']) = explode('@', $userdata['mail']);
157        }
158
159        // make full name from username if empty
160        if (empty($userdata['name'])) {
161            $userdata['name'] = $userdata['user'];
162        }
163
164        return $userdata;
165    }
166
167    /**
168     * Process the userdata, update the user info array and create the user if necessary
169     *
170     * Uses the global $auth object for user management
171     *
172     * @param array $userdata User info received from authentication
173     * @param string $servicename Auth service
174     * @return array the modified user info
175     * @throws Exception
176     */
177    protected function processUserData($userdata, $servicename)
178    {
179        /** @var \auth_plugin_oauth */
180        global $auth;
181
182        // see if the user is known already
183        $localUser = $auth->getUserByEmail($userdata['mail']);
184        if ($localUser) {
185            $localUserInfo = $auth->getUserData($localUser);
186            // check if the user allowed access via this service
187            if (!in_array($auth->cleanGroup($servicename), $localUserInfo['grps'])) {
188                throw new Exception(sprintf($auth->getLang('authnotenabled'), $servicename));
189            }
190            $userdata['user'] = $localUser;
191            $userdata['name'] = $localUserInfo['name'];
192            $userdata['grps'] = array_merge((array)$userdata['grps'], $localUserInfo['grps']);
193        } elseif (actionOK('register') || $auth->getConf('register-on-auth')) {
194            if (!$auth->addUser($userdata, $servicename)) {
195                throw new Exception('something went wrong creating your user account. please try again later.');
196            }
197        } else {
198            throw new Exception($auth->getLang('addUser not possible'));
199        }
200
201        return $userdata;
202    }
203
204    /**
205     * Instantiates a Service by name
206     *
207     * @param string $servicename
208     * @return Service
209     * @throws Exception
210     */
211    protected function loadService($servicename)
212    {
213        /** @var \helper_plugin_oauth $hlp */
214        $hlp = plugin_load('helper', 'oauth');
215        $srv = $hlp->loadService($servicename);
216
217        if ($srv === null) throw new Exception("No such service $servicename");
218        return $srv;
219    }
220
221}
222