xref: /plugin/oauth/auth.php (revision caf913c336a95e1c5c68909dd8162b8999b44962)
1<?php
2
3use dokuwiki\plugin\oauth\OAuthManager;
4use dokuwiki\plugin\oauth\Session;
5use dokuwiki\Subscriptions\RegistrationSubscriptionSender;
6use OAuth\Common\Exception\Exception as OAuthException;
7
8/**
9 * DokuWiki Plugin oauth (Auth Component)
10 *
11 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
12 * @author  Andreas Gohr <andi@splitbrain.org>
13 */
14class auth_plugin_oauth extends auth_plugin_authplain
15{
16    /** @var helper_plugin_oauth */
17    protected $hlp;
18
19    /** @var OAuthManager */
20    protected $om;
21
22    // region standard auth methods
23
24    /** @inheritDoc */
25    public function __construct()
26    {
27        parent::__construct();
28        $this->cando['external'] = true;
29        $this->hlp = $this->loadHelper('oauth');
30    }
31
32    /** @inheritDoc */
33    public function trustExternal($user, $pass, $sticky = false)
34    {
35        global $INPUT;
36
37        // handle redirects from farmer to animal wiki instances
38        if ($INPUT->has('state') && plugin_load('helper', 'farmer')) {
39            $this->handleFarmState($INPUT->str('state'));
40        }
41
42        try {
43            // either oauth or "normal" plain auth login via form
44            $this->om = new OAuthManager();
45            if ($this->om->continueFlow()) return true;
46            return null; // triggers the normal auth_login()
47        } catch (OAuthException $e) {
48            $this->hlp->showException($e);
49            auth_logoff(); // clears all session and cookie data
50            return false;
51        }
52    }
53
54    /**
55     * Enhance function to check against duplicate emails
56     *
57     * @inheritdoc
58     */
59    public function createUser($user, $pwd, $name, $mail, $grps = null)
60    {
61        if ($this->getUserByEmail($mail)) {
62            msg($this->getLang('emailduplicate'), -1);
63            return false;
64        }
65
66        return parent::createUser($user, $pwd, $name, $mail, $grps);
67    }
68
69    /**
70     * Enhance function to check against duplicate emails
71     *
72     * @inheritdoc
73     */
74    public function modifyUser($user, $changes)
75    {
76        global $conf;
77
78        if (isset($changes['mail'])) {
79            $found = $this->getUserByEmail($changes['mail']);
80            if ($found && $found != $user) {
81                msg($this->getLang('emailduplicate'), -1);
82                return false;
83            }
84        }
85
86        $ok = parent::modifyUser($user, $changes);
87
88        // refresh session cache
89        touch($conf['cachedir'] . '/sessionpurge');
90        return $ok;
91    }
92
93    /**
94     * Unset additional stuff in session on logout
95     */
96    public function logOff()
97    {
98        parent::logOff();
99        if (isset($this->om)) {
100            $this->om->logout();
101        }
102        (Session::getInstance())->clear();
103    }
104
105    // endregion
106
107    /**
108     * Register a new user logged in by oauth
109     *
110     * It ensures the username is unique, by adding a number if needed.
111     * Default and service name groups are set here.
112     * Registration notifications are triggered.
113     *
114     * @param array $userinfo This will be updated with the new username
115     * @param string $servicename
116     *
117     * @return bool
118     * @todo - should this be part of the OAuthManager class instead?
119     */
120    public function registerOAuthUser(&$userinfo, $servicename)
121    {
122        global $conf;
123        $user = $userinfo['user'];
124        $count = '';
125        while ($this->getUserData($user . $count)) {
126            if ($count) {
127                $count++;
128            } else {
129                $count = 1;
130            }
131        }
132        $user = $user . $count;
133        $userinfo['user'] = $user;
134        $groups_on_creation = [];
135        $groups_on_creation[] = $conf['defaultgroup'];
136        $groups_on_creation[] = $this->cleanGroup($servicename); // add service as group
137        $userinfo['grps'] = array_merge((array)$userinfo['grps'], $groups_on_creation);
138
139        // the password set here will remain unknown to the user
140        $ok = $this->triggerUserMod(
141            'create',
142            [
143                $user,
144                auth_pwgen($user),
145                $userinfo['name'],
146                $userinfo['mail'],
147                $userinfo['grps'],
148            ]
149        );
150        if (!$ok) {
151            return false;
152        }
153
154        // send notification about the new user
155        $subscriptionSender = new RegistrationSubscriptionSender();
156        $subscriptionSender->sendRegister($user, $userinfo['name'], $userinfo['mail']);
157
158        return true;
159    }
160
161    /**
162     * Find a user by email address
163     *
164     * @param $mail
165     * @return bool|string
166     */
167    public function getUserByEmail($mail)
168    {
169        if ($this->users === null) {
170            $this->loadUserData();
171        }
172        $mail = strtolower($mail);
173
174        foreach ($this->users as $user => $userinfo) {
175            if (strtolower($userinfo['mail']) == $mail) return $user;
176        }
177
178        return false;
179    }
180
181    /**
182     * Fall back to plain auth strings
183     *
184     * @inheritdoc
185     */
186    public function getLang($id)
187    {
188        $result = parent::getLang($id);
189        if ($result) return $result;
190
191        $parent = new auth_plugin_authplain();
192        return $parent->getLang($id);
193    }
194
195    /**
196     * Farmer plugin support
197     *
198     * When coming back to farmer instance via OAUTH redirectURI, we need to redirect again
199     * to a proper animal instance detected from $state
200     *
201     * @param $state
202     */
203    protected function handleFarmState($state)
204    {
205        /** @var \helper_plugin_farmer $farmer */
206        $farmer = plugin_load('helper', 'farmer', false, true);
207        $data = json_decode(base64_decode(urldecode($state)));
208        if (empty($data->animal) || $farmer->getAnimal() == $data->animal) {
209            return;
210        }
211        $animal = $data->animal;
212        $allAnimals = $farmer->getAllAnimals();
213        if (!in_array($animal, $allAnimals)) {
214            msg('Animal ' . $animal . ' does not exist!');
215            return;
216        }
217        global $INPUT;
218        $url = $farmer->getAnimalURL($animal) . '/doku.php?' . $INPUT->server->str('QUERY_STRING');
219        send_redirect($url);
220    }
221}
222