Completed
Push — master ( d88391...473b86 )
by Dimas
221:13 queued 206:00
created

user::can()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 4
nc 3
nop 1
dl 0
loc 6
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace User;
4
5
use Crypto\crypt;
6
use DB\pdo;
7
use JSON\json;
8
9
$GLOBALS['user_instance'] = null;
10
11
/**
12
 * Universal Framework User Management.
13
 *
14
 * @author Dimas Lanjaka <[email protected]>
15
 */
16
class user
17
{
18
  public $admin_pattern = '/^superadmin$/s';
19
  private static $_instance = null;
20
  public $dbname = 'userdata';
21
  private $id;
22
  private $username;
23
  private $password;
24
  private $role;
25
  private $user;
26
  /**
27
   * PDO instance.
28
   *
29
   * @var \DB\pdo
30
   */
31
  private $pdo = null;
32
  private $db = ['user', 'pass', 'dbname', 'host', 'charset'];
33
34
  public function __construct($user = 'root', $pass = '', $db = 'darkit', $host = 'localhost', $charset = 'utf8mb4')
35
  {
36
    if (!empty($user) && !empty($db)) {
37
      $this->db = [
38
        'user' => $user,
39
        'pass' => $pass,
40
        'dbname' => $db,
41
        'host' => $host,
42
        'charset' => $charset,
43
      ];
44
      $this->pdo = new \DB\pdo($user, $pass, $db, $host, $charset);
45
      $this->pdo->connect($user, $pass, $db, $host, $charset);
46
    }
47
    if (!$GLOBALS['user_instance']) {
48
      $GLOBALS['user_instance'] = $this;
49
    }
50
  }
51
52
  /**
53
   * Current instance.
54
   *
55
   * @return user
56
   */
57
  public static function instance()
58
  {
59
    return $GLOBALS['user_instance'];
60
  }
61
62
  public function db($data)
63
  {
64
    if (isset($this->db[$data])) {
65
      return $this->db[$data];
66
    }
67
  }
68
69
  /**
70
   * Get All Users.
71
   *
72
   * @return void
73
   */
74
  public function getUsers()
75
  {
76
    if (!$this->pdo) {
77
      throw new \MVC\Exception('Database not properly configured', 1);
78
    }
79
    $result = [];
80
    $users = $this->pdo->select($this->dbname)->row_array();
81
    if (!\ArrayHelper\helper::isSequent($users)) {
82
      $result[] = $users;
83
    } else {
84
      $result = $users;
85
    }
86
87
    return $result;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $result returns the type array|array<mixed,array> which is incompatible with the documented return type void.
Loading history...
88
  }
89
90
  /**
91
   * Get PDO Instance.
92
   *
93
   * @param \DB\pdo $pdo set new instance
94
   *
95
   * @return \DB\pdo
96
   */
97
  public function pdo_instance(\DB\pdo $pdo = null)
98
  {
99
    if ($pdo) {
100
      $this->pdo = $pdo;
101
      //$this->db['use'] = $pdo->getDa
102
    }
103
104
    return $this->pdo;
105
  }
106
107
  /**
108
   * Static Chain.
109
   *
110
   * @return $this
111
   */
112
  public static function getInstance()
113
  {
114
    if (null === self::$_instance) {
115
      self::$_instance = new self();
116
    }
117
118
    return self::$_instance;
119
  }
120
  /**
121
   * Meta instance
122
   *
123
   * @var \User\meta
124
   */
125
  private $meta_instance;
126
  /**
127
   * Get \User\meta instance
128
   *
129
   * @return meta
130
   */
131
  public function meta()
132
  {
133
    $this->pdo_required();
134
    if (!$this->meta_instance) {
135
      $this->meta_instance = new meta($this->pdo);
136
    }
137
    return $this->meta_instance;
138
  }
139
140
  /**
141
   * Check user can do something
142
   *
143
   * @param string $what
144
   * @return boolean
145
   */
146
  function can(string $what)
0 ignored issues
show
Unused Code introduced by
The parameter $what is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

146
  function can(/** @scrutinizer ignore-unused */ string $what)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
147
  {
148
    if ($this->is_login()) {
149
      $check = $this->meta()->get($this->userdata('id'), 'can');
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $check is correct as $this->meta()->get($this->userdata('id'), 'can') targeting User\meta::get() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
150
      if (!empty($check)) {
151
        return $check;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $check returns the type void which is incompatible with the documented return type boolean.
Loading history...
152
      }
153
    }
154
  }
155
156
  /**
157
   * Check pdo active.
158
   * @throws \MVC\Exception
159
   * @return void
160
   */
161
  public function pdo_required()
162
  {
163
    if (!$this->pdo) {
164
      throw new \MVC\Exception('Active PDO Required');
165
    }
166
  }
167
168
  public static function get_admin_pattern()
169
  {
170
    return self::getInstance()->admin_pattern;
171
  }
172
173
  /**
174
   * Check current user is superadmin.
175
   *
176
   * @return bool
177
   */
178
  public function is_admin()
179
  {
180
    if ($this->is_login()) {
181
      return preg_match($this->admin_pattern, $this->get_role());
0 ignored issues
show
Bug Best Practice introduced by
The expression return preg_match($this-...ern, $this->get_role()) returns the type integer which is incompatible with the documented return type boolean.
Loading history...
182
    }
183
  }
184
185
  /**
186
   * If not admin auto redirect.
187
   *
188
   * @return void
189
   */
190
  public function admin_required(string $redirect = '/signin')
191
  {
192
    if (!$this->is_admin()) {
193
      \MVC\router::safe_redirect($redirect);
194
      exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
195
    }
196
  }
197
198
  /**
199
   * Get Current user role.
200
   *
201
   * @return string
202
   */
203
  public function get_role()
204
  {
205
    return isset($_SESSION['login']['role']) ? $_SESSION['login']['role'] : 'UNAUTHORIZED';
206
  }
207
208
  /**
209
   * Get user data.
210
   *
211
   * @return array|int|string|null if empty not logged in
212
   */
213
  public function userdata(string $what)
214
  {
215
    if ($this->is_login()) {
216
      if (class_exists('\Indosat\api')) {
217
        $m3 = new \Indosat\api();
0 ignored issues
show
Bug introduced by
The type Indosat\api was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
218
        $m3->setdata('login', \ArrayHelper\helper::unset($_SESSION['login'], ['password']));
219
      }
220
      if ('all' == $what) {
221
        return \ArrayHelper\helper::unset($_SESSION['login'], ['password']);
222
      }
223
      if (isset($_SESSION['login'][$what])) {
224
        return $_SESSION['login'][$what];
225
      }
226
    }
227
228
    return null;
229
  }
230
231
  /**
232
   * Get user data.
233
   *
234
   * @see \User\user::userdata
235
   */
236
  public function data(string $name)
237
  {
238
    return $this->userdata($name);
239
  }
240
241
  public function update_last_seen()
242
  {
243
    if ($this->is_login()) {
244
      $currentUsername = $this->userdata('username');
245
      $run = $this
246
        ->pdo
247
        ->query("UPDATE `userdata` SET `last_seen` = NOW() WHERE `username` = '{$currentUsername}';")
248
        ->exec();
249
250
      return $run;
251
    }
252
  }
253
254
  public function update_password($id, $pass)
255
  {
256
    if (!$this->pdo) {
257
      throw new \MVC\Exception('Database not properly configured', 1);
258
    }
259
    $db = $this->dbname;
260
    $crypt = new crypt();
261
    $pass = $crypt->encrypt('dimaslanjaka', $pass);
262
    if (!$this->is_admin()) {
263
      $q = "UPDATE `$db` SET `password` = :pass WHERE `$db`.`id` = :id AND `$db`.`role` <> 'superadmin' AND `$db`.`role` <> 'admin';";
264
    } else {
265
      $q = "UPDATE `$db` SET `password` = :pass WHERE `$db`.`id` = :id;";
266
    }
267
    $this->pdo->SQL_Exec($q, ['id' => $id, 'pass' => $pass]);
268
    $result = [];
269
    $chk = $this->pdo->SQL_Fetch("SELECT * FROM `$db` WHERE `$db`.`id` = :id AND `$db`.`password` = :pass", ['id' => $id, 'pass' => $pass]);
270
    if (!empty($chk) && isset($chk['id'])) {
271
      $result['error'] = false;
272
      $result['message'] = 'Password changed successfully';
273
      $result['title'] = 'User information';
274
    } else {
275
      $result['error'] = true;
276
    }
277
    $result = $this->hide_additional(array_merge($result, $chk));
278
279
    return $result;
280
  }
281
282
  public function delete_user_by_id($id)
283
  {
284
    $db = $this->dbname;
285
    $q = "DELETE FROM `$db` WHERE `$db`.`id` = :id";
286
    $this->pdo->SQL_Exec($q, ['id' => $id]);
287
  }
288
289
  public function login_required(string $redirect = '/signin')
290
  {
291
    if (!$this->is_login()) {
292
      \MVC\router::safe_redirect($redirect);
293
    }
294
  }
295
296
  /**
297
   * Get current database name.
298
   *
299
   * @return array
300
   */
301
  public function get_dbname()
302
  {
303
    return $this->pdo->query('select database()')->row_array();
304
  }
305
306
  public function login($username, $password)
307
  {
308
    if (!$this->pdo) {
309
      throw new \MVC\Exception('Database not properly configured', 1);
310
    }
311
    if ('GET' == $_SERVER['REQUEST_METHOD']) {
312
      $username = urldecode($username);
313
      $password = urldecode($password);
314
    }
315
    $username = addslashes($username);
316
    $db = $this->dbname;
317
    $password = addslashes($password);
318
    $crypt = new crypt();
319
    $password = $crypt->encrypt('dimaslanjaka', $password);
320
321
    $query = "SELECT * FROM `$db` WHERE username=:username AND password=:password";
322
    //var_dump($username, $password, $query);
323
    $exec = $this->pdo->SQL_Fetch($query, ['username' => $username, 'password' => $password]);
324
    //var_dump($this->pdo, $exec);
325
    $result = [];
326
    if (!empty($exec)) {
327
      if (isset($exec['id'])) {
328
        $result['error'] = false;
329
        $result['success'] = true;
330
        $result['message'] = 'login successful';
331
        $result = array_merge($exec, $result);
332
        $id = $exec['id'];
333
        $query = "UPDATE `$db` SET `last_login` = now() WHERE `$db`.`id` = $id;";
334
        foreach ($exec as $key => $value) {
335
          $_SESSION['login'][$key] = $value;
336
        }
337
        $this->pdo->SQL_Exec($query);
338
      } else {
339
        //var_dump($exec);
340
        $result['error'] = true;
341
      }
342
    } else {
343
      $result['error'] = true;
344
      $result['message'] = 'Empty response from database';
345
    }
346
    $result = $this->hide_additional($result);
347
348
    $this->user = $result;
349
    $this->pdo->resetQuery();
350
    $result['title'] = 'login information';
351
352
    return $result;
353
  }
354
355
  /**
356
   * Hidding additional information into backend response.
357
   *
358
   * @param array $result
359
   *
360
   * @author DimasLanjaka <[email protected]>
361
   *
362
   * @return array
363
   */
364
  public function hide_additional($result)
365
  {
366
    if (isset($result['role'])) {
367
      unset($result['role']);
368
    }
369
    if (isset($result['number'])) {
370
      unset($result['number']);
371
    }
372
    if (isset($result['password'])) {
373
      unset($result['password']);
374
    }
375
    if (isset($result['id'])) {
376
      unset($result['id']);
377
    }
378
379
    return $result;
380
  }
381
382
  /**
383
   * Check Login.
384
   *
385
   * @author Dimaslanjaka <[email protected]>
386
   *
387
   * @param Function $callback
0 ignored issues
show
Bug introduced by
The type User\Function was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
388
   */
389
  public function check_login($callback)
390
  {
391
    if (isset($_SESSION['login'])) {
392
      if (isset($_SESSION['login']['id']) && is_numeric($_SESSION['login']['id'])) {
393
        if (is_callable($callback)) {
394
          call_user_func($callback, \ArrayHelper\helper::unset($_SESSION['login'], ['password']));
395
        }
396
      }
397
    } else {
398
      if (is_callable($callback)) {
399
        call_user_func($callback, [
400
          'error' => true,
401
          'message' => 'User Login required. Please relogin from login page',
402
          'unauthorized' => true,
403
        ]);
404
      }
405
    }
406
  }
407
408
  /**
409
   * Check user is login.
410
   *
411
   * @return bool
412
   */
413
  public function is_login()
414
  {
415
    $isLogin = isset($_SESSION['login']['username']) && !empty(trim($_SESSION['login']['username'])) ? true : false;
416
    if ($isLogin) {
417
      $this->username = $_SESSION['login']['username'];
418
      $check = $this->pdo->select('userdata')->where(['username' => $this->username])->row_array();
419
      if (!empty($check)) {
420
        foreach ($check as $key => $value) {
421
          $this->{$key} = $value;
422
        }
423
      }
424
    }
425
426
    return $isLogin;
427
  }
428
429
  /**
430
   * Update Display Name.
431
   *
432
   * @param Number $id
433
   * @param string $display
434
   */
435
  public function update_display_name($id, $display)
436
  {
437
    if (!$this->pdo) {
438
      throw new \MVC\Exception('Database not properly configured', 1);
439
    }
440
    $db = $this->dbname;
441
    $q = "UPDATE `$db` SET `display_name` = :name WHERE `$db`.`id` = :id;";
442
    $this->pdo->SQL_Exec($q, ['name' => $display, 'id' => $id]);
443
    $chk = $this->pdo->SQL_Fetch("SELECT `$db` WHERE `$db`.`id` = :id AND `display_name` = :name;", ['id' => $id, 'name' => $display]);
444
    $this->json($chk);
445
  }
446
447
  public function update_role($id, $role, $rgx = '/^(admin|client|superadmin)$/s')
448
  {
449
    if (!$this->pdo) {
450
      throw new \MVC\Exception('Database not properly configured', 1);
451
    }
452
    if (!preg_match($rgx, $role)) {
453
      throw new \MVC\Exception('Error Processing Change Role Request', 1);
454
    }
455
    $chk = [
456
      'error' => true,
457
      'message' => 'insufficient privileges',
458
    ];
459
    if ($this->is_admin()) {
460
      $db = $this->dbname;
461
      $q = "UPDATE `$db` SET `role` = :role WHERE `$db`.`id` = :id;";
462
      $this->pdo->SQL_Exec($q, ['role' => $role, 'id' => $id]);
463
      $chk = $this->pdo->SQL_Fetch("SELECT * FROM `$db` WHERE `$db`.`id` = :id AND `role` = :role;", ['id' => $id, 'role' => $role]);
464
      if (isset($chk['id'])) {
465
        $chk['error'] = false;
466
      }
467
    }
468
469
    return $chk;
470
  }
471
472
  public function register($username, $password, $name = 'user', $role = 'client')
473
  {
474
    if ('GET' == $_SERVER['REQUEST_METHOD']) {
475
      $username = urldecode($username);
476
      $password = urldecode($password);
477
    }
478
    $username = addslashes($username);
479
    $password = addslashes($password);
480
    $crypt = new crypt();
481
    $password = $crypt->encrypt('dimaslanjaka', $password);
482
    $db = $this->dbname;
483
    $q = "INSERT INTO `$db` (`display_name`, `username`, `password`, `role`) VALUES (:name, :username, :password, :role);";
484
    $exec = $this->pdo->SQL_Exec($q, ['name' => $name, 'username' => $username, 'password' => $password, 'role' => $role]);
485
    $result = [];
486
    if (isset($exec['id'])) {
487
      $chk = $this->pdo->SQL_Fetch("SELECT * FROM `$db` WHERE `$db`.`username` = :username AND `$db`.`password` = :password", ['username' => $username, 'password' => $password]);
488
      $result['success'] = true;
489
      $result = $this->hide_additional(array_merge($result, $chk));
490
    }
491
492
    return $result;
493
  }
494
495
  /**
496
   * JSON formatter.
497
   *
498
   * @param array $data
499
   * @param bool  $header
500
   * @param bool  $print
501
   */
502
  public function json($data = [], $header = true, $print = true)
503
  {
504
    return json::json($data, $header, $print);
505
  }
506
}
507