1<?php
2/**
3 * django auth backend
4 *
5 * @author    Andreas Gohr <andi@splitbrain.org>
6 * @author    Michael Luggen <michael.luggen at unifr.ch>
7 * @author    Robert Czechowski <zgtm at zgtm.de>
8 */
9
10define('DOKU_AUTH', dirname(__FILE__));
11define('AUTH_USERFILE',DOKU_CONF.'users.auth.php');
12
13class auth_plugin_authdjango extends DokuWiki_Auth_Plugin  {
14
15    var $dbh = null; // db handle
16
17    /**
18     * Constructor.
19     *
20     * Sets additional capabilities and config strings
21     * @author    Michael Luggen <michael.luggen at rhone.ch>
22     * @author    Robert Czechowski <zgtm at zgtm.de>
23     */
24    public function __construct()
25    {
26        parent::__construct();
27
28        global $config_cascade;
29        global $dbh;
30
31        $this->cando['external'] = true;
32        $this->cando['getGroups'] = true;
33
34        $this->cando['logout'] = !empty($this->getConf('logoff_uri'));
35
36        try {
37            // Connecting, selecting database
38            if ($this->getConf('protocol') == 'sqlite') {
39                $this->dbh = new PDO('sqlite:' . $this->getConf('server'));
40            }
41            else {
42                $this->dbh = new PDO($this->getConf('protocol') . ':host=' . $this->getConf('server') . ';dbname=' . $this->getConf('db'), $this->getConf('user'), $this->getConf('password'));
43            }
44        } catch (PDOException $e) {
45            msg("Can not connect to database!", -1);
46            dbg($e);
47            $this->success = false;
48        }
49        $this->success = true;
50    }
51
52
53    function trustExternal($user,$pass,$sticky=false){
54        global $USERINFO;
55        global $conf;
56        global $dbh;
57
58        $sticky ? $sticky = true : $sticky = false; //sanity check
59
60        /**
61         * Just checks against the django sessionid variable,
62         * gets user info from django-database
63         */
64
65        if (isset($_COOKIE['sessionid']) && $this->dbh) {
66
67            $s_id =  $_COOKIE['sessionid'];
68
69            // Look the cookie up in the db
70            $query = 'SELECT session_data FROM django_session WHERE session_key=' . $this->dbh->quote($s_id) . ' LIMIT 1;';
71            $result = $this->dbh->query($query) or die('Query failed1: ' . $this->dbh->errorInfo());
72            $ar = $result->fetch(PDO::FETCH_ASSOC);
73            $session_data = $ar['session_data'];
74
75            // TODO: $session_data can now be empty if the session does not exist in database, handle correctly instead of just dying
76            if (strlen($session_data) == 0) {
77                return false;
78            }
79
80            $compressed = false;
81
82            if (str_contains($session_data, ":")) {
83                // New django session encoding since django 4
84                if ($session_data[0] == '.') {
85                    $compressed = true;
86                    $session_data = substr($session_data, 1);
87                }
88
89                $session_json = base64_decode(strtr(preg_split('/:/', $session_data, 2)[0], "-_", "+/"), true);
90
91                if ($compressed) {
92                       $session_json = zlib_decode($session_json);
93                }
94
95            } else {
96                // Old django session enconding until django 3
97                // Decoding the session data:
98
99                $session_json = preg_split('/:/', base64_decode($session_data), 2)[1];
100            }
101            $userid = json_decode($session_json, true)['_auth_user_id'];
102            $query2 = 'SELECT username, first_name, last_name, email, is_superuser, is_staff FROM auth_user WHERE id=' . $this->dbh->quote($userid) . ' LIMIT 1;';
103
104            $result2 = $this->dbh->query($query2) or die('Query failed2: ' . print_r($this->dbh->errorInfo()));
105            $user = $result2->fetch(PDO::FETCH_ASSOC);
106
107            $username =  $user['username'];
108            $userfullname = $user['first_name'] . " " . $user['last_name'];
109            $useremail = $user['email'];
110
111            // okay we're logged in - set the globals
112            $groups = $this->_getUserGroups($username);
113
114            $USERINFO['name'] = $username;
115            $USERINFO['pass'] = '';
116            $USERINFO['mail'] = $useremail;
117
118            if (($user['is_superuser'] && $this->getConf('admin_admin') == 1)
119                || ($user['is_staff'] && $this->getConf('staff_admin') == 1))
120            {
121                $groups[] = 'admin';
122            } else {
123                foreach ($this->getConf('groups_admin') as $admin_group) {
124                    foreach ($groups as $group) {
125                        if ($group == $admin_group && $group != "") {
126                            $groups[] = 'admin';
127                            break 2; // break both for loops
128                        }
129                    }
130                }
131            }
132            $USERINFO['grps'] = $groups;
133
134            $_SERVER['REMOTE_USER'] = $username;
135
136            $_SESSION[DOKU_COOKIE]['auth']['user'] = $username;
137            $_SESSION[DOKU_COOKIE]['auth']['info'] = $USERINFO;
138
139            return true;
140        }
141        return false;
142    }
143
144    function _getUserGroups($user){
145        $query = 'SELECT auth_group.name FROM auth_user, auth_user_groups, auth_group where auth_user.username = ' . $this->dbh->quote($user) . ' AND auth_user.id = auth_user_groups.user_id AND auth_user_groups.group_id = auth_group.id;';
146
147        $result = $this->dbh->query($query) or die('Query failed3: ' . $this->dbh->errorInfo());
148
149        $groups = [];
150        foreach ($result as $row) {
151            $groups[] = $row[0];
152        };
153
154        if (!in_array("user", $groups)) {
155            $groups[] = "user";
156        }
157
158        return $groups;
159    }
160
161
162    function retrieveGroups($start=0,$limit=0){
163        $query = 'SELECT auth_group.name FROM auth_group';
164
165        $result = $this->dbh->query($query) or die('Query failed4: ' . $this->dbh->errorInfo());
166
167        $groups = [];
168        foreach ($result as $row) {
169            $groups[] = $row[0];
170        };
171
172        if (!in_array("user", $groups)) {
173            $groups[] = "user";
174        }
175
176        if (!in_array("admin", $groups)) {
177            $groups[] = "admin";
178        }
179
180        return $groups;
181    }
182
183
184    function logOff() {
185        header("Location: " . $this->getConf('logoff_uri'));
186        die();
187    }
188
189
190    function __destruct() {
191        $this->dbh = null;
192    }
193}
194