BurningFlipside /
CommonCode
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | namespace Auth; |
||
| 3 | |||
| 4 | if(!function_exists('password_hash') || !function_exists('password_verify')) |
||
| 5 | { |
||
| 6 | define('PASSWORD_BCRYPT', 1); |
||
| 7 | define('PASSWORD_DEFAULT', PASSWORD_BCRYPT); |
||
| 8 | define('PASSWORD_BCRYPT_DEFAULT_COST', 10); |
||
| 9 | |||
| 10 | function password_hash($password, $algo = PASSWORD_DEFAULT) |
||
| 11 | { |
||
| 12 | if(is_null($password) || is_int($password)) |
||
| 13 | { |
||
| 14 | $password = (string)$password; |
||
| 15 | } |
||
| 16 | if(!is_string($password)) |
||
| 17 | { |
||
| 18 | trigger_error("password_hash(): Password must be a string", E_USER_WARNING); |
||
| 19 | return false; |
||
| 20 | } |
||
| 21 | if(!is_int($algo)) |
||
| 22 | { |
||
| 23 | trigger_error("password_hash() expects parameter 2 to be long, ".gettype($algo)." given", E_USER_WARNING); |
||
| 24 | return false; |
||
| 25 | } |
||
| 26 | switch($algo) |
||
| 27 | { |
||
| 28 | case PASSWORD_BCRYPT: |
||
| 29 | $cost = PASSWORD_BCRYPT_DEFAULT_COST; |
||
| 30 | $rawSaltLen = 16; |
||
| 31 | $requiredSaltLen = 22; |
||
| 32 | $hashFormat = sprintf("$2y$%02d$", $cost); |
||
| 33 | $resultLength = 60; |
||
| 34 | break; |
||
| 35 | default: |
||
| 36 | trigger_error(sprintf("password_hash(): Unknown password hashing algorithm: %s", $algo), E_USER_WARNING); |
||
| 37 | return false; |
||
| 38 | } |
||
| 39 | $salt = openssl_random_pseudo_bytes($rawSaltLen); |
||
| 40 | $base64Digits = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; |
||
| 41 | $bcrypt64Digits = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; |
||
| 42 | $base64String = base64_encode($salt); |
||
| 43 | $salt = strtr(rtrim($base64String, '='), $base64Digits, $bcrypt64Digits); |
||
| 44 | $salt = substr($salt, 0, $requiredSaltLen); |
||
| 45 | $hash = $hashFormat.$salt; |
||
| 46 | $ret = crypt($password, $hash); |
||
| 47 | if(!is_string($ret) || strlen($ret) != $resultLength) |
||
| 48 | { |
||
| 49 | return false; |
||
| 50 | } |
||
| 51 | return $ret; |
||
| 52 | } |
||
| 53 | |||
| 54 | function password_verify($password, $hash) |
||
| 55 | { |
||
| 56 | $ret = crypt($password, $hash); |
||
| 57 | if(!is_string($ret) || strlen($ret) != strlen($hash) || strlen($ret) <= 13) |
||
| 58 | { |
||
| 59 | return false; |
||
| 60 | } |
||
| 61 | $status = 0; |
||
| 62 | $count = strlen($ret); |
||
| 63 | for($i = 0; $i < $count; $i++) |
||
| 64 | { |
||
| 65 | $status |= (ord($ret[$i]) ^ ord($hash[$i])); |
||
| 66 | } |
||
| 67 | return $status === 0; |
||
| 68 | } |
||
| 69 | } |
||
| 70 | |||
| 71 | class SQLAuthenticator extends Authenticator |
||
| 72 | { |
||
| 73 | public $dataSet = null; |
||
| 74 | public $pendingDataSet = null; |
||
| 75 | private $dataTables = array(); |
||
| 76 | private $params; |
||
| 77 | |||
| 78 | public function __construct($params) |
||
| 79 | { |
||
| 80 | parent::__construct($params); |
||
| 81 | $this->params = $params; |
||
| 82 | if($this->current) |
||
| 83 | { |
||
| 84 | $this->dataSet = $this->getCurrentDataSet(); |
||
| 85 | } |
||
| 86 | if($this->pending) |
||
| 87 | { |
||
| 88 | $this->pendingDataSet = $this->getPendingDataSet(); |
||
| 89 | } |
||
| 90 | } |
||
| 91 | |||
| 92 | /** |
||
| 93 | * @SuppressWarnings("StaticAccess") |
||
| 94 | */ |
||
| 95 | private function getCurrentDataSet() |
||
| 96 | { |
||
| 97 | if(isset($this->params['current_data_set'])) |
||
| 98 | { |
||
| 99 | return \DataSetFactory::getDataSetByName($this->params['current_data_set']); |
||
| 100 | } |
||
| 101 | return \DataSetFactory::getDataSetByName('authentication'); |
||
| 102 | } |
||
| 103 | |||
| 104 | /** |
||
| 105 | * @SuppressWarnings("StaticAccess") |
||
| 106 | */ |
||
| 107 | private function getPendingDataSet() |
||
| 108 | { |
||
| 109 | if(isset($this->params['pending_data_set'])) |
||
| 110 | { |
||
| 111 | return \DataSetFactory::getDataSetByName($this->params['pending_data_set']); |
||
| 112 | } |
||
| 113 | return \DataSetFactory::getDataSetByName('pending_authentication'); |
||
| 114 | } |
||
| 115 | |||
| 116 | private function getDataTable($name, $pending = false) |
||
| 117 | { |
||
| 118 | if(isset($this->dataTables[$name]) && isset($this->dataTables[$name][$pending])) |
||
| 119 | { |
||
| 120 | return $this->dataTables[$name][$pending]; |
||
| 121 | } |
||
| 122 | $dataSet = $this->dataSet; |
||
| 123 | if($pending) |
||
| 124 | { |
||
| 125 | $dataSet = $this->pendingDataSet; |
||
| 126 | } |
||
| 127 | if($dataSet === null) |
||
| 128 | { |
||
| 129 | throw new \Exception('Unable to obtain dataset for SQL Authentication!'); |
||
| 130 | } |
||
| 131 | $dataTable = $dataSet[$name]; |
||
| 132 | if(!isset($this->dataTables[$name])) |
||
| 133 | { |
||
| 134 | $this->dataTables[$name] = array(); |
||
| 135 | } |
||
| 136 | $this->dataTables[$name][$pending] = $dataTable; |
||
| 137 | return $dataTable; |
||
| 138 | } |
||
| 139 | |||
| 140 | /** |
||
| 141 | * Get the data table for Pending Users |
||
| 142 | * |
||
| 143 | * @return boolean|\Data\DataTable The Pending User Data Table |
||
| 144 | */ |
||
| 145 | private function getPendingUserDataTable() |
||
| 146 | { |
||
| 147 | if(isset($this->params['pending_user_table'])) |
||
| 148 | { |
||
| 149 | return $this->getDataTable($this->params['pending_user_table'], true); |
||
| 150 | } |
||
| 151 | return $this->getDataTable('users', true); |
||
| 152 | } |
||
| 153 | |||
| 154 | public function login($username, $password) |
||
| 155 | { |
||
| 156 | if($this->current === false) |
||
| 157 | { |
||
| 158 | return false; |
||
| 159 | } |
||
| 160 | $userDataTable = $this->getDataTable('user'); |
||
| 161 | $filter = new \Data\Filter("uid eq '$username'"); |
||
| 162 | $users = $userDataTable->read($filter); |
||
| 163 | if($users === false || !isset($users[0])) |
||
| 164 | { |
||
| 165 | return false; |
||
| 166 | } |
||
| 167 | if(password_verify($password, $users[0]['pass'])) |
||
| 168 | { |
||
| 169 | return array('res'=>true, 'extended'=>$users[0]); |
||
| 170 | } |
||
| 171 | return false; |
||
| 172 | } |
||
| 173 | |||
| 174 | public function isLoggedIn($data) |
||
| 175 | { |
||
| 176 | if(isset($data['res'])) |
||
| 177 | { |
||
| 178 | return $data['res']; |
||
| 179 | } |
||
| 180 | return false; |
||
| 181 | } |
||
| 182 | |||
| 183 | public function getUser($data) |
||
| 184 | { |
||
| 185 | if(isset($this->params['current_data_set'])) |
||
| 186 | { |
||
| 187 | $data['current_data_set'] = $this->params['current_data_set']; |
||
| 188 | } |
||
| 189 | return new SQLUser($data, $this); |
||
| 190 | } |
||
| 191 | |||
| 192 | View Code Duplication | public function getGroupByName($name) |
|
| 193 | { |
||
| 194 | $groupDataTable = $this->getDataTable('group'); |
||
| 195 | $filter = new \Data\Filter("gid eq '$name'"); |
||
| 196 | $groups = $groupDataTable->read($filter); |
||
| 197 | if($groups === false || !isset($groups[0])) |
||
| 198 | { |
||
| 199 | return false; |
||
| 200 | } |
||
| 201 | return new SQLGroup($groups[0]); |
||
| 202 | } |
||
| 203 | |||
| 204 | View Code Duplication | public function getUserByName($name) |
|
| 205 | { |
||
| 206 | $userDataTable = $this->getDataTable('user'); |
||
| 207 | $filter = new \Data\Filter("uid eq '$name'"); |
||
| 208 | $users = $userDataTable->read($filter); |
||
| 209 | if($users === false || !isset($users[0])) |
||
| 210 | { |
||
| 211 | return false; |
||
| 212 | } |
||
| 213 | return new SQLUser($users[0], $this); |
||
| 214 | } |
||
| 215 | |||
| 216 | private function getDataByFilter($dataTableName, $filter, $select, $top, $skip, $orderby) |
||
| 217 | { |
||
| 218 | $dataTable = $this->getDataTable($dataTableName); |
||
| 219 | return $dataTable->read($filter, $select, $top, $skip, $orderby); |
||
| 220 | } |
||
| 221 | |||
| 222 | /** |
||
| 223 | * @param string $dataTableName The Data Table to serach |
||
| 224 | * @param string $className The class to obtain data in |
||
| 225 | * @param boolean|array $select The fields to read |
||
| 226 | * @param boolean|integer $top The number of entities to read |
||
| 227 | * @param boolean|integer $skip The number of entities to skip |
||
| 228 | * @param boolean|array $orderby The fields to sort by |
||
| 229 | */ |
||
| 230 | private function convertDataToClass($dataTableName, $className, $filter, $select, $top, $skip, $orderby) |
||
| 231 | { |
||
| 232 | $data = $this->getDataByFilter($dataTableName, $filter, $select, $top, $skip, $orderby); |
||
| 233 | if($data === false) |
||
| 234 | { |
||
| 235 | return false; |
||
| 236 | } |
||
| 237 | $count = count($data); |
||
| 238 | for($i = 0; $i < $count; $i++) |
||
| 239 | { |
||
| 240 | $data[$i] = new $className($groups[$i], $this); |
||
| 241 | } |
||
| 242 | return $data; |
||
| 243 | } |
||
| 244 | |||
| 245 | /** |
||
| 246 | * @param boolean|array $select The fields to read |
||
| 247 | * @param boolean|integer $top The number of entities to read |
||
| 248 | * @param boolean|integer $skip The number of entities to skip |
||
| 249 | * @param boolean|array $orderby The fields to sort by |
||
| 250 | */ |
||
| 251 | public function getGroupsByFilter($filter, $select = false, $top = false, $skip = false, $orderby = false) |
||
| 252 | { |
||
| 253 | return $this->convertDataToClass('group', 'SQLGroup', $filter, $select, $top, $skip, $orderby); |
||
| 254 | } |
||
| 255 | |||
| 256 | /** |
||
| 257 | * @param boolean|array $select The fields to read |
||
| 258 | * @param boolean|integer $top The number of entities to read |
||
| 259 | * @param boolean|integer $skip The number of entities to skip |
||
| 260 | * @param boolean|array $orderby The fields to sort by |
||
| 261 | */ |
||
| 262 | public function getUsersByFilter($filter, $select = false, $top = false, $skip = false, $orderby = false) |
||
| 263 | { |
||
| 264 | return $this->convertDataToClass('group', 'SQLUser', $filter, $select, $top, $skip, $orderby); |
||
| 265 | } |
||
| 266 | |||
| 267 | public function getPendingUserCount() |
||
| 268 | { |
||
| 269 | if($this->pending === false) |
||
| 270 | { |
||
| 271 | return 0; |
||
| 272 | } |
||
| 273 | $dataTable = $this->getPendingUserDataTable(); |
||
| 274 | if($dataTable === null) |
||
| 275 | { |
||
| 276 | return 0; |
||
| 277 | } |
||
| 278 | return $dataTable->count(); |
||
| 279 | } |
||
| 280 | |||
| 281 | private function searchPendingUsers($filter, $select, $top, $skip, $orderby) |
||
| 282 | { |
||
| 283 | $userDataTable = $this->getPendingUserDataTable(); |
||
| 284 | $fieldData = $filter->to_mongo_filter(); |
||
| 285 | $firstFilter = new \Data\Filter('substringof(data,"'.implode($fieldData, ' ').'")'); |
||
| 286 | $users = $userDataTable->read($firstFilter, $select, $top, $skip, $orderby); |
||
|
0 ignored issues
–
show
|
|||
| 287 | if($users === false) |
||
| 288 | { |
||
| 289 | return false; |
||
| 290 | } |
||
| 291 | $ret = array(); |
||
| 292 | $count = count($users); |
||
| 293 | for($i = 0; $i < $count; $i++) |
||
| 294 | { |
||
| 295 | $user = new SQLPendingUser($users[$i], $userDataTable); |
||
|
0 ignored issues
–
show
It seems like
$userDataTable defined by $this->getPendingUserDataTable() on line 283 can also be of type object<Data\DataTable>; however, Auth\SQLPendingUser::__construct() does only seem to accept boolean, maybe add an additional type check?
If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check: /**
* @return array|string
*/
function returnsDifferentValues($x) {
if ($x) {
return 'foo';
}
return array();
}
$x = returnsDifferentValues($y);
if (is_array($x)) {
// $x is an array.
}
If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue. Loading history...
|
|||
| 296 | $err = false; |
||
| 297 | foreach($fieldData as $field=>$data) |
||
| 298 | { |
||
| 299 | if(strcasecmp($user[$field], $data) !== 0) |
||
| 300 | { |
||
| 301 | $err = true; break; |
||
| 302 | } |
||
| 303 | } |
||
| 304 | if(!$err) |
||
| 305 | { |
||
| 306 | array_push($ret, $user); |
||
| 307 | } |
||
| 308 | } |
||
| 309 | return $ret; |
||
| 310 | } |
||
| 311 | |||
| 312 | /** |
||
| 313 | * @param \Data\Filter $filter The filter to read with |
||
| 314 | * @param boolean|array $select The fields to read |
||
| 315 | * @param boolean|integer $top The number of entities to read |
||
| 316 | * @param boolean|integer $skip The number of entities to skip |
||
| 317 | * @param boolean|array $orderby The fields to sort by |
||
| 318 | */ |
||
| 319 | public function getPendingUsersByFilter($filter, $select = false, $top = false, $skip = false, $orderby = false) |
||
| 320 | { |
||
| 321 | if($this->pending === false) |
||
| 322 | { |
||
| 323 | return false; |
||
| 324 | } |
||
| 325 | if($filter !== false && !$filter->contains('hash')) |
||
| 326 | { |
||
| 327 | return $this->searchPendingUsers($filter, $select, $top, $skip, $orderby); |
||
| 328 | } |
||
| 329 | $userDataTable = $this->getPendingUserDataTable(); |
||
| 330 | $users = $userDataTable->read($filter, $select, $top, $skip, $orderby); |
||
|
0 ignored issues
–
show
$filter is of type object<Data\Filter>, 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...
It seems like
$select defined by parameter $select on line 319 can also be of type array; however, Data\DataTable::read() does only seem to accept boolean, maybe add an additional type check?
This check looks at variables that have been passed in as parameters and are passed out again to other methods. If the outgoing method call has stricter type requirements than the method itself, an issue is raised. An additional type check may prevent trouble. Loading history...
It seems like
$top defined by parameter $top on line 319 can also be of type integer; however, Data\DataTable::read() does only seem to accept boolean, maybe add an additional type check?
This check looks at variables that have been passed in as parameters and are passed out again to other methods. If the outgoing method call has stricter type requirements than the method itself, an issue is raised. An additional type check may prevent trouble. Loading history...
It seems like
$skip defined by parameter $skip on line 319 can also be of type integer; however, Data\DataTable::read() does only seem to accept boolean, maybe add an additional type check?
This check looks at variables that have been passed in as parameters and are passed out again to other methods. If the outgoing method call has stricter type requirements than the method itself, an issue is raised. An additional type check may prevent trouble. Loading history...
It seems like
$orderby defined by parameter $orderby on line 319 can also be of type array; however, Data\DataTable::read() does only seem to accept boolean, maybe add an additional type check?
This check looks at variables that have been passed in as parameters and are passed out again to other methods. If the outgoing method call has stricter type requirements than the method itself, an issue is raised. An additional type check may prevent trouble. Loading history...
|
|||
| 331 | if($users === false) |
||
| 332 | { |
||
| 333 | return false; |
||
| 334 | } |
||
| 335 | $count = count($users); |
||
| 336 | for($i = 0; $i < $count; $i++) |
||
| 337 | { |
||
| 338 | $users[$i] = new SQLPendingUser($users[$i], $userDataTable); |
||
|
0 ignored issues
–
show
It seems like
$userDataTable defined by $this->getPendingUserDataTable() on line 329 can also be of type object<Data\DataTable>; however, Auth\SQLPendingUser::__construct() does only seem to accept boolean, maybe add an additional type check?
If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check: /**
* @return array|string
*/
function returnsDifferentValues($x) {
if ($x) {
return 'foo';
}
return array();
}
$x = returnsDifferentValues($y);
if (is_array($x)) {
// $x is an array.
}
If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue. Loading history...
|
|||
| 339 | } |
||
| 340 | return $users; |
||
| 341 | } |
||
| 342 | |||
| 343 | public function createPendingUser($user) |
||
| 344 | { |
||
| 345 | if($this->pending === false) |
||
| 346 | { |
||
| 347 | return false; |
||
| 348 | } |
||
| 349 | $userDataTable = $this->getPendingUserDataTable(); |
||
| 350 | if(isset($user->password2)) |
||
| 351 | { |
||
| 352 | unset($user->password2); |
||
| 353 | } |
||
| 354 | $json = json_encode($user); |
||
| 355 | $hash = hash('sha512', $json); |
||
| 356 | $array = array('hash'=>$hash, 'data'=>$json); |
||
| 357 | $ret = $userDataTable->create($array); |
||
| 358 | if($ret !== false) |
||
| 359 | { |
||
| 360 | $users = $this->getPendingUsersByFilter(new \Data\Filter("hash eq '$hash'")); |
||
| 361 | if($users === false || !isset($users[0])) |
||
| 362 | { |
||
| 363 | throw new \Exception('Error retreiving user object after successful create!'); |
||
| 364 | } |
||
| 365 | $users[0]->sendEmail(); |
||
| 366 | } |
||
| 367 | return $ret; |
||
| 368 | } |
||
| 369 | |||
| 370 | public function getTempUserByHash($hash) |
||
| 371 | { |
||
| 372 | $users = $this->getPendingUsersByFilter(new \Data\Filter("hash eq '$hash'")); |
||
| 373 | if($users === false || !isset($users[0])) |
||
| 374 | { |
||
| 375 | return false; |
||
| 376 | } |
||
| 377 | return $users[0]; |
||
| 378 | } |
||
| 379 | } |
||
| 380 | /* vim: set tabstop=4 shiftwidth=4 expandtab: */ |
||
| 381 |
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: