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