Completed
Push — master ( f22a46...046041 )
by Patrick
02:27 queued 12s
created

SQLAuthenticator::getTempUserByHash()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
nc 2
nop 1
dl 0
loc 9
rs 9.9666
c 0
b 0
f 0
1
<?php
2
namespace Flipside\Auth;
3
4
class SQLAuthenticator extends Authenticator
5
{
6
    public $dataSet = null;
7
    public $pendingDataSet = null;
8
    private $dataTables = array();
9
    private $params;
10
11
    public function __construct($params)
12
    {
13
        parent::__construct($params);
14
        $this->params = $params;
15
        if($this->current)
16
        {
17
            $this->dataSet = $this->getCurrentDataSet();
18
        }
19
        if($this->pending)
20
        {
21
            $this->pendingDataSet = $this->getPendingDataSet();
22
        }
23
    }
24
25
    /**
26
     * @SuppressWarnings("StaticAccess")
27
     */
28
    private function getCurrentDataSet()
29
    {
30
        if(isset($this->params['current_data_set']))
31
        {
32
            return \Flipside\DataSetFactory::getDataSetByName($this->params['current_data_set']);
33
        }
34
        return \Flipside\DataSetFactory::getDataSetByName('authentication');
35
    }
36
37
    /**
38
     * @SuppressWarnings("StaticAccess")
39
     */
40
    private function getPendingDataSet()
41
    {
42
        if(isset($this->params['pending_data_set']))
43
        {
44
            return \Flipside\DataSetFactory::getDataSetByName($this->params['pending_data_set']);
45
        }
46
        return \Flipside\DataSetFactory::getDataSetByName('pending_authentication');
47
    }
48
49
    private function getDataTable($name, $pending = false)
50
    {
51
        if(isset($this->dataTables[$name]) && isset($this->dataTables[$name][$pending]))
52
        {
53
            return $this->dataTables[$name][$pending];
54
        }
55
        $dataSet = $this->dataSet;
56
        if($pending)
57
        {
58
            $dataSet = $this->pendingDataSet;
59
        }
60
        if($dataSet === null)
61
        {
62
            throw new \Exception('Unable to obtain dataset for SQL Authentication!');
63
        }
64
        $dataTable = $dataSet[$name];
65
        if(!isset($this->dataTables[$name]))
66
        {
67
            $this->dataTables[$name] = array();
68
        }
69
        $this->dataTables[$name][$pending] = $dataTable;
70
        return $dataTable;
71
    }
72
73
    /**
74
     * Get the data table for Pending Users
75
     *
76
     * @return boolean|\Data\DataTable The Pending User Data Table
77
     */
78
    private function getPendingUserDataTable()
79
    {
80
        if(isset($this->params['pending_user_table']))
81
        {
82
            return $this->getDataTable($this->params['pending_user_table'], true);
83
        }
84
        return $this->getDataTable('users', true);
85
    }
86
87
    public function login($username, $password)
88
    {
89
        if($this->current === false)
90
        {
91
            return false;
92
        }
93
        $userDataTable = $this->getDataTable('user');
94
        $filter = new \Flipside\Data\Filter("uid eq '$username'");
95
        $users = $userDataTable->read($filter);
96
        if($users === false || !isset($users[0]))
97
        {
98
            return false;
99
        }
100
        if(password_verify($password, $users[0]['pass']))
101
        {
102
            return array('res'=>true, 'extended'=>$users[0]);
103
        }
104
        return false;
105
    }
106
107
    public function isLoggedIn($data)
108
    {
109
        if(isset($data['res']))
110
        {
111
            return $data['res'];
112
        }
113
        return false;
114
    }
115
116
    public function getUser($data)
117
    {
118
        if(isset($this->params['current_data_set']))
119
        {
120
            $data['current_data_set'] = $this->params['current_data_set'];
121
        }
122
        return new SQLUser($data, $this);
0 ignored issues
show
Documentation introduced by
$this is of type this<Flipside\Auth\SQLAuthenticator>, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Bug Best Practice introduced by
The return type of return new \Flipside\Auth\SQLUser($data, $this); (Flipside\Auth\SQLUser) is incompatible with the return type of the parent method Flipside\Auth\Authenticator::getUser of type null|Auth\User.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
123
    }
124
125
    /**
126
     * Get the specified entity from the specified database table
127
     *
128
     * @param string $tableName The name of the table to obtain data from
129
     * @param string $filterStr The filter string to use to obtain the data
130
     * @param string $className The class name to pass the data to
131
     *
132
     * @return stdClass The data as an object or null if not found
133
     */
134
    private function getEntityByFilter($tableName, $filterStr, $className)
135
    {
136
        $dataTable = $this->getDataTable($tableName);
137
        $filter = new \Flipside\Data\Filter($filterStr);
138
        $entities = $dataTable->read($filter);
139
        if(empty($entities))
140
        {
141
            return null;
142
        }
143
        return new $className($entities[0], $this);
144
    }
145
146
    public function getGroupByName($name)
147
    {
148
        return $this->getEntityByFilter('group', "gid eq '$name'", '\Flipside\Auth\SQLGroup');
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->getEntityByFilter...side\\Auth\\SQLGroup'); of type Flipside\Auth\stdClass|null adds the type Flipside\Auth\stdClass to the return on line 148 which is incompatible with the return type of the parent method Flipside\Auth\Authenticator::getGroupByName of type null|Auth\Group.
Loading history...
149
    }
150
151
    public function getUserByName($name)
152
    {
153
        return $this->getEntityByFilter('user', "uid eq '$name'", '\Flipside\Auth\SQLUser');
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->getEntityByFilter...pside\\Auth\\SQLUser'); of type Flipside\Auth\stdClass|null adds the type Flipside\Auth\stdClass to the return on line 153 which is incompatible with the return type of the parent method Flipside\Auth\Authenticator::getUserByName of type Flipside\Auth\Auth\User|null.
Loading history...
154
    }
155
156
    /**
157
     * Get the specified entities from the specified database table
158
     *
159
     * @param string $dataTableName The name of the table to obtain data from
160
     * @param boolean|\Data\Filter $filter The filter to use while searching the table
161
     * @param boolean|array $select The array of properties to read
162
     * @param boolean|integer $top The number of records to read
163
     * @param boolean|integer $skip The number of records to skip
164
     * @param boolean|array $orderby The properties to sort on
165
     *
166
     * @return array The SQL data returned by the filter
167
     */
168
    private function getDataByFilter($dataTableName, $filter, $select, $top, $skip, $orderby)
169
    {
170
        $dataTable = $this->getDataTable($dataTableName);
171
        return $dataTable->read($filter, $select, $top, $skip, $orderby);
172
    }
173
174
    /**
175
     * @param string $dataTableName The Data Table to serach
176
     * @param string $className The class to obtain data in
177
     * @param boolean|array $select The fields to read
178
     * @param boolean|integer $top The number of entities to read
179
     * @param boolean|integer $skip The number of entities to skip
180
     * @param boolean|array $orderby The fields to sort by
181
     */
182
    private function convertDataToClass($dataTableName, $className, $filter, $select, $top, $skip, $orderby)
183
    {
184
        $data = $this->getDataByFilter($dataTableName, $filter, $select, $top, $skip, $orderby);
185
        if($data === false)
186
        {
187
            return false;
188
        }
189
        $count = count($data);
190
        for($i = 0; $i < $count; $i++)
191
        {
192
            $data[$i] = new $className($data[$i], $this);
193
        }
194
        return $data;
195
    }
196
197
    /**
198
     * @param boolean|array $select The fields to read
199
     * @param boolean|integer $top The number of entities to read
200
     * @param boolean|integer $skip The number of entities to skip
201
     * @param boolean|array $orderby The fields to sort by
202
     */
203
    public function getGroupsByFilter($filter, $select = false, $top = false, $skip = false, $orderby = false)
204
    {
205
        return $this->convertDataToClass('group', 'Flipside\Auth\SQLGroup', $filter, $select, $top, $skip, $orderby);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->convertDataToClas...$top, $skip, $orderby); of type false|array adds the type array to the return on line 205 which is incompatible with the return type of the parent method Flipside\Auth\Authenticator::getGroupsByFilter of type boolean.
Loading history...
206
    }
207
208
    /**
209
     * @param boolean|array $select The fields to read
210
     * @param boolean|integer $top The number of entities to read
211
     * @param boolean|integer $skip The number of entities to skip
212
     * @param boolean|array $orderby The fields to sort by
213
     */
214
    public function getUsersByFilter($filter, $select = false, $top = false, $skip = false, $orderby = false)
215
    {
216
        return $this->convertDataToClass('user', 'Flipside\Auth\SQLUser', $filter, $select, $top, $skip, $orderby);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->convertDataToClas...$top, $skip, $orderby); of type false|array adds the type array to the return on line 216 which is incompatible with the return type of the parent method Flipside\Auth\Authenticator::getUsersByFilter of type boolean.
Loading history...
217
    }
218
219
    public function getPendingUserCount()
220
    {
221
        if($this->pending === false)
222
        {
223
            return 0;
224
        }
225
        $dataTable = $this->getPendingUserDataTable();
226
        if($dataTable === null)
227
        {
228
            return 0;
229
        }
230
        return $dataTable->count();
231
    }
232
233
    /**
234
     * Search all the pending users
235
     *
236
     * @param boolean|\Data\Filter $filter The filter to use while searching the table
237
     * @param boolean|array $select The array of properties to read
238
     * @param boolean|integer $top The number of records to read
239
     * @param boolean|integer $skip The number of records to skip
240
     * @param boolean|array $orderby The properties to sort on
241
     *
242
     * @return array The SQL data returned by the filter
243
     */
244
    private function searchPendingUsers($filter, $select, $top, $skip, $orderby)
245
    {
246
        $userDataTable = $this->getPendingUserDataTable();
247
        $clause = $filter->getClause('time');
248
        $fieldData = false;
249
        if($clause === false)
250
        {
251
            $fieldData = $filter->to_mongo_filter();
252
            $filter = new \Flipside\Data\Filter('substringof(data,"'.implode($fieldData, ' ').'")');
253
        }
254
        $users = $userDataTable->read($filter, $select, $top, $skip, $orderby);
255
        if($users === false)
256
        {
257
            return false;
258
        }
259
        $ret = array();
260
        $count = count($users);
261
        for($i = 0; $i < $count; $i++)
262
        {
263
            $user = new SQLPendingUser($users[$i], $userDataTable);
264
            $err = false;
265
            if($fieldData !== false)
266
            {
267
                foreach($fieldData as $field=>$data)
268
                {
269
                    if(strcasecmp($user[$field], $data) !== 0)
270
                    {
271
                        $err = true; break;
272
                    }
273
                }
274
            }
275
            if(!$err)
276
            {
277
                array_push($ret, $user);
278
            }
279
        }
280
        return $ret;
281
    }
282
283
    /**
284
     * @param \Data\Filter $filter The filter to read with
285
     * @param boolean|array $select The fields to read
286
     * @param boolean|integer $top The number of entities to read
287
     * @param boolean|integer $skip The number of entities to skip
288
     * @param boolean|array $orderby The fields to sort by
289
     */
290
    public function getPendingUsersByFilter($filter, $select = false, $top = false, $skip = false, $orderby = false)
291
    {
292
        if($this->pending === false)
293
        {
294
            return false;
295
        }
296
        if($filter !== false && !$filter->contains('hash'))
297
        {
298
            return $this->searchPendingUsers($filter, $select, $top, $skip, $orderby);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->searchPendingUser...$top, $skip, $orderby); of type false|array adds the type array to the return on line 298 which is incompatible with the return type of the parent method Flipside\Auth\Authentica...getPendingUsersByFilter of type boolean.
Loading history...
299
        }
300
        $userDataTable = $this->getPendingUserDataTable();
301
        $users = $userDataTable->read($filter, $select, $top, $skip, $orderby);
302
        if($users === false)
303
        {
304
            return false;
305
        }
306
        $count = count($users);
307
        for($i = 0; $i < $count; $i++)
308
        {
309
            $users[$i] = new SQLPendingUser($users[$i], $userDataTable);
310
        }
311
        return $users;
312
    }
313
314
    public function createPendingUser($user)
315
    {
316
        if($this->pending === false)
317
        {
318
            return false;
319
        }
320
        $userDataTable = $this->getPendingUserDataTable();
321
        if(isset($user->password2))
322
        {
323
            unset($user->password2);
324
        }
325
        $json = json_encode($user);
326
        $hash = hash('sha512', $json);
327
        $array = array('hash'=>$hash, 'data'=>$json);
328
        $ret = $userDataTable->create($array);
329
        if($ret !== false)
330
        {
331
            $users = $this->getPendingUsersByFilter(new \Data\Filter("hash eq '$hash'"));
332
            if($users === false || !isset($users[0]))
333
            {
334
                throw new \Exception('Error retreiving user object after successful create!');
335
            }
336
            $users[0]->sendEmail();
337
        }
338
        return $ret;
339
    }
340
341
    public function getTempUserByHash($hash)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
342
    {
343
        $users = $this->getPendingUsersByFilter(new \Data\Filter("hash eq '$hash'"));
344
        if($users === false || !isset($users[0]))
345
        {
346
            return false;
347
        }
348
        return $users[0];
349
    }
350
}
351
/* vim: set tabstop=4 shiftwidth=4 expandtab: */
352