xref: /plugin/oauth/auth.php (revision 4a9a2508c732645b704b0d92419148747b070ea7)
180852c15SAndreas Gohr<?php
23e7ac5b1SAndreas Gohr
3e170f465SAndreas Gohruse dokuwiki\plugin\oauth\OAuthManager;
4e170f465SAndreas Gohruse dokuwiki\plugin\oauth\Session;
5e170f465SAndreas Gohruse dokuwiki\Subscriptions\RegistrationSubscriptionSender;
6e170f465SAndreas Gohruse OAuth\Common\Exception\Exception as OAuthException;
7e170f465SAndreas Gohr
880852c15SAndreas Gohr/**
980852c15SAndreas Gohr * DokuWiki Plugin oauth (Auth Component)
1080852c15SAndreas Gohr *
1180852c15SAndreas Gohr * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
1280852c15SAndreas Gohr * @author  Andreas Gohr <andi@splitbrain.org>
1380852c15SAndreas Gohr */
143e7ac5b1SAndreas Gohrclass auth_plugin_oauth extends auth_plugin_authplain
153e7ac5b1SAndreas Gohr{
16e170f465SAndreas Gohr    /** @var helper_plugin_oauth */
17e170f465SAndreas Gohr    protected $hlp;
18e170f465SAndreas Gohr
19a1fa007aSNaoto Kobayashi    /** @var OAuthManager */
20a1fa007aSNaoto Kobayashi    protected $om;
21a1fa007aSNaoto Kobayashi
22e170f465SAndreas Gohr    // region standard auth methods
2380852c15SAndreas Gohr
243e7ac5b1SAndreas Gohr    /** @inheritDoc */
253e7ac5b1SAndreas Gohr    public function __construct()
263e7ac5b1SAndreas Gohr    {
27f10e09e2SAndreas Gohr        parent::__construct();
28f10e09e2SAndreas Gohr        $this->cando['external'] = true;
29e170f465SAndreas Gohr        $this->hlp = $this->loadHelper('oauth');
3080852c15SAndreas Gohr    }
3180852c15SAndreas Gohr
323e7ac5b1SAndreas Gohr    /** @inheritDoc */
33311a6606SAnna Dabrowska    public function trustExternal($user, $pass, $sticky = false)
343e7ac5b1SAndreas Gohr    {
35a02a5d81SAnna Dabrowska        global $INPUT;
36438dcc52SMichael Grosse
378523e9d0SAndreas Gohr        // handle redirects from farmer to animal wiki instances
388523e9d0SAndreas Gohr        if ($INPUT->has('state') && plugin_load('helper', 'farmer')) {
39d9be1cb5SAnna Dabrowska            $this->handleFarmState($INPUT->str('state'));
40438dcc52SMichael Grosse        }
4180852c15SAndreas Gohr
4274b4d4a4SAndreas Gohr        try {
4374b4d4a4SAndreas Gohr            // either oauth or "normal" plain auth login via form
44a1fa007aSNaoto Kobayashi            $this->om = new OAuthManager();
45568565b9SAndreas Gohr            if ($this->om->continueFlow()) return true;
461cdd3f90SAndreas Gohr            if ($this->getConf('singleService')) {
471cdd3f90SAndreas Gohr                return false; // no normal login in singleService mode
481cdd3f90SAndreas Gohr            }
49568565b9SAndreas Gohr            return null; // triggers the normal auth_login()
50e170f465SAndreas Gohr        } catch (OAuthException $e) {
51e170f465SAndreas Gohr            $this->hlp->showException($e);
5228002081SAndreas Gohr            auth_logoff(); // clears all session and cookie data
5374b4d4a4SAndreas Gohr            return false;
54a7a8f46aSAndreas Gohr        }
55a7a8f46aSAndreas Gohr    }
5680852c15SAndreas Gohr
57f2e164b0SMichael Große    /**
58*fe49fd82SAndreas Gohr     * Enforce oauth login for certain email domains
59*fe49fd82SAndreas Gohr     *
60*fe49fd82SAndreas Gohr     * @inheritdoc
61*fe49fd82SAndreas Gohr     */
62*fe49fd82SAndreas Gohr    public function checkPass($user, $pass)
63*fe49fd82SAndreas Gohr    {
64*fe49fd82SAndreas Gohr        $ok = parent::checkPass($user, $pass);
65*fe49fd82SAndreas Gohr        if(!$ok) return $ok;
66*fe49fd82SAndreas Gohr        $domains = $this->hlp->getEnforcedDomains();
67*fe49fd82SAndreas Gohr        if($domains === []) return $ok;
68*fe49fd82SAndreas Gohr
69*fe49fd82SAndreas Gohr        if($this->hlp->checkMail($this->getUserData($user)['mail'], $domains)) {
70*fe49fd82SAndreas Gohr            global $lang;
71*fe49fd82SAndreas Gohr            // we overwrite the standard bad password message with our own
72*fe49fd82SAndreas Gohr            $lang['badlogin'] = $this->getLang('eMailEnforced');
73*fe49fd82SAndreas Gohr            return false;
74*fe49fd82SAndreas Gohr        }
75*fe49fd82SAndreas Gohr        return $ok;
76*fe49fd82SAndreas Gohr    }
77*fe49fd82SAndreas Gohr
78*fe49fd82SAndreas Gohr
79*fe49fd82SAndreas Gohr    /**
80311a6606SAnna Dabrowska     * Enhance function to check against duplicate emails
81311a6606SAnna Dabrowska     *
82e170f465SAndreas Gohr     * @inheritdoc
83311a6606SAnna Dabrowska     */
84311a6606SAnna Dabrowska    public function createUser($user, $pwd, $name, $mail, $grps = null)
85311a6606SAnna Dabrowska    {
86311a6606SAnna Dabrowska        if ($this->getUserByEmail($mail)) {
87311a6606SAnna Dabrowska            msg($this->getLang('emailduplicate'), -1);
88311a6606SAnna Dabrowska            return false;
89311a6606SAnna Dabrowska        }
90311a6606SAnna Dabrowska
91311a6606SAnna Dabrowska        return parent::createUser($user, $pwd, $name, $mail, $grps);
92311a6606SAnna Dabrowska    }
93311a6606SAnna Dabrowska
94311a6606SAnna Dabrowska    /**
95311a6606SAnna Dabrowska     * Enhance function to check against duplicate emails
96311a6606SAnna Dabrowska     *
97e170f465SAndreas Gohr     * @inheritdoc
98311a6606SAnna Dabrowska     */
99311a6606SAnna Dabrowska    public function modifyUser($user, $changes)
100311a6606SAnna Dabrowska    {
101311a6606SAnna Dabrowska        global $conf;
102311a6606SAnna Dabrowska
103311a6606SAnna Dabrowska        if (isset($changes['mail'])) {
104311a6606SAnna Dabrowska            $found = $this->getUserByEmail($changes['mail']);
105311a6606SAnna Dabrowska            if ($found && $found != $user) {
106311a6606SAnna Dabrowska                msg($this->getLang('emailduplicate'), -1);
107311a6606SAnna Dabrowska                return false;
108311a6606SAnna Dabrowska            }
109311a6606SAnna Dabrowska        }
110311a6606SAnna Dabrowska
111311a6606SAnna Dabrowska        $ok = parent::modifyUser($user, $changes);
112311a6606SAnna Dabrowska
113311a6606SAnna Dabrowska        // refresh session cache
114311a6606SAnna Dabrowska        touch($conf['cachedir'] . '/sessionpurge');
115311a6606SAnna Dabrowska        return $ok;
116311a6606SAnna Dabrowska    }
117311a6606SAnna Dabrowska
118311a6606SAnna Dabrowska    /**
119311a6606SAnna Dabrowska     * Unset additional stuff in session on logout
120311a6606SAnna Dabrowska     */
121311a6606SAnna Dabrowska    public function logOff()
122311a6606SAnna Dabrowska    {
123311a6606SAnna Dabrowska        parent::logOff();
124a1fa007aSNaoto Kobayashi        if (isset($this->om)) {
125a1fa007aSNaoto Kobayashi            $this->om->logout();
126a1fa007aSNaoto Kobayashi        }
127311a6606SAnna Dabrowska    }
128311a6606SAnna Dabrowska
129e170f465SAndreas Gohr    // endregion
130e170f465SAndreas Gohr
131311a6606SAnna Dabrowska    /**
132e170f465SAndreas Gohr     * Register a new user logged in by oauth
133f2e164b0SMichael Große     *
134e170f465SAndreas Gohr     * It ensures the username is unique, by adding a number if needed.
135e170f465SAndreas Gohr     * Default and service name groups are set here.
136e170f465SAndreas Gohr     * Registration notifications are triggered.
137a02a5d81SAnna Dabrowska     *
138e170f465SAndreas Gohr     * @param array $userinfo This will be updated with the new username
139b2b9fbc7SMichael Große     * @param string $servicename
140b2b9fbc7SMichael Große     *
141b2b9fbc7SMichael Große     * @return bool
142e170f465SAndreas Gohr     * @todo - should this be part of the OAuthManager class instead?
143b2b9fbc7SMichael Große     */
144e170f465SAndreas Gohr    public function registerOAuthUser(&$userinfo, $servicename)
1453e7ac5b1SAndreas Gohr    {
146b2b9fbc7SMichael Große        global $conf;
147a02a5d81SAnna Dabrowska        $user = $userinfo['user'];
148b2b9fbc7SMichael Große        $count = '';
149b2b9fbc7SMichael Große        while ($this->getUserData($user . $count)) {
150b2b9fbc7SMichael Große            if ($count) {
151b2b9fbc7SMichael Große                $count++;
152b2b9fbc7SMichael Große            } else {
153b2b9fbc7SMichael Große                $count = 1;
154b2b9fbc7SMichael Große            }
155b2b9fbc7SMichael Große        }
156290e9b1fSAndreas Gohr        $user .= $count;
157a02a5d81SAnna Dabrowska        $userinfo['user'] = $user;
158e170f465SAndreas Gohr        $groups_on_creation = [];
159b2b9fbc7SMichael Große        $groups_on_creation[] = $conf['defaultgroup'];
160b2b9fbc7SMichael Große        $groups_on_creation[] = $this->cleanGroup($servicename); // add service as group
161a02a5d81SAnna Dabrowska        $userinfo['grps'] = array_merge((array)$userinfo['grps'], $groups_on_creation);
162b2b9fbc7SMichael Große
163e170f465SAndreas Gohr        // the password set here will remain unknown to the user
164b2b9fbc7SMichael Große        $ok = $this->triggerUserMod(
165b2b9fbc7SMichael Große            'create',
166e170f465SAndreas Gohr            [
167e170f465SAndreas Gohr                $user,
168e170f465SAndreas Gohr                auth_pwgen($user),
169e170f465SAndreas Gohr                $userinfo['name'],
170e170f465SAndreas Gohr                $userinfo['mail'],
1714928b245SAndreas Gohr                $userinfo['grps'],
172e170f465SAndreas Gohr            ]
173b2b9fbc7SMichael Große        );
174b2b9fbc7SMichael Große        if (!$ok) {
175b2b9fbc7SMichael Große            return false;
176b2b9fbc7SMichael Große        }
177b2b9fbc7SMichael Große
178e170f465SAndreas Gohr        // send notification about the new user
179e170f465SAndreas Gohr        $subscriptionSender = new RegistrationSubscriptionSender();
180e170f465SAndreas Gohr        $subscriptionSender->sendRegister($user, $userinfo['name'], $userinfo['mail']);
181e170f465SAndreas Gohr
182b2b9fbc7SMichael Große        return true;
183b2b9fbc7SMichael Große    }
184b2b9fbc7SMichael Große
185b2b9fbc7SMichael Große    /**
186a02a5d81SAnna Dabrowska     * Find a user by email address
187b2b9fbc7SMichael Große     *
188b2b9fbc7SMichael Große     * @param $mail
189b2b9fbc7SMichael Große     * @return bool|string
190b2b9fbc7SMichael Große     */
19174b4d4a4SAndreas Gohr    public function getUserByEmail($mail)
1923e7ac5b1SAndreas Gohr    {
1938b214edcSAndreas Gohr        if ($this->users === null) {
1948b214edcSAndreas Gohr            $this->loadUserData();
1958b214edcSAndreas Gohr        }
196b2b9fbc7SMichael Große        $mail = strtolower($mail);
197b2b9fbc7SMichael Große
198a02a5d81SAnna Dabrowska        foreach ($this->users as $user => $userinfo) {
199290e9b1fSAndreas Gohr            if (strtolower($userinfo['mail']) === $mail) return $user;
200b2b9fbc7SMichael Große        }
201b2b9fbc7SMichael Große
202b2b9fbc7SMichael Große        return false;
203b2b9fbc7SMichael Große    }
204b2b9fbc7SMichael Große
205b2b9fbc7SMichael Große    /**
2062a8b22d5SAndreas Gohr     * Fall back to plain auth strings
2072a8b22d5SAndreas Gohr     *
2082a8b22d5SAndreas Gohr     * @inheritdoc
2092a8b22d5SAndreas Gohr     */
2102a8b22d5SAndreas Gohr    public function getLang($id)
2112a8b22d5SAndreas Gohr    {
2122a8b22d5SAndreas Gohr        $result = parent::getLang($id);
2132a8b22d5SAndreas Gohr        if ($result) return $result;
2142a8b22d5SAndreas Gohr
2152a8b22d5SAndreas Gohr        $parent = new auth_plugin_authplain();
2162a8b22d5SAndreas Gohr        return $parent->getLang($id);
2172a8b22d5SAndreas Gohr    }
2182a8b22d5SAndreas Gohr
2192a8b22d5SAndreas Gohr    /**
220b8ca6a42SAnna Dabrowska     * Farmer plugin support
221b8ca6a42SAnna Dabrowska     *
222b8ca6a42SAnna Dabrowska     * When coming back to farmer instance via OAUTH redirectURI, we need to redirect again
223b8ca6a42SAnna Dabrowska     * to a proper animal instance detected from $state
224b2b9fbc7SMichael Große     *
225311a6606SAnna Dabrowska     * @param $state
226b2b9fbc7SMichael Große     */
227e170f465SAndreas Gohr    protected function handleFarmState($state)
2283e7ac5b1SAndreas Gohr    {
229311a6606SAnna Dabrowska        /** @var \helper_plugin_farmer $farmer */
230311a6606SAnna Dabrowska        $farmer = plugin_load('helper', 'farmer', false, true);
231311a6606SAnna Dabrowska        $data = json_decode(base64_decode(urldecode($state)));
232311a6606SAnna Dabrowska        if (empty($data->animal) || $farmer->getAnimal() == $data->animal) {
233311a6606SAnna Dabrowska            return;
234827232fcSMichael Große        }
235311a6606SAnna Dabrowska        $animal = $data->animal;
236311a6606SAnna Dabrowska        $allAnimals = $farmer->getAllAnimals();
237311a6606SAnna Dabrowska        if (!in_array($animal, $allAnimals)) {
238311a6606SAnna Dabrowska            msg('Animal ' . $animal . ' does not exist!');
239311a6606SAnna Dabrowska            return;
240827232fcSMichael Große        }
241311a6606SAnna Dabrowska        global $INPUT;
242311a6606SAnna Dabrowska        $url = $farmer->getAnimalURL($animal) . '/doku.php?' . $INPUT->server->str('QUERY_STRING');
243311a6606SAnna Dabrowska        send_redirect($url);
244b2b9fbc7SMichael Große    }
245b2b9fbc7SMichael Große}
246