Passed
Push — master ( e95921...ddbd1b )
by Dimas
13:28 queued 05:15
created

user::update_password()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 29
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 22
c 1
b 0
f 0
nc 6
nop 2
dl 0
loc 29
rs 8.9457
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
   * Access Management
71
   *
72
   * @return access
73
   */
74
  public function access()
75
  {
76
    return new access($this);
77
  }
78
79
  /**
80
   * Get All Users.
81
   *
82
   * @return void
83
   */
84
  public function getUsers()
85
  {
86
    if (!$this->pdo) {
87
      throw new \MVC\Exception('Database not properly configured', 1);
88
    }
89
    $result = [];
90
    $users = $this->pdo->select($this->dbname)->row_array();
91
    if (!\ArrayHelper\helper::isSequent($users)) {
92
      $result[] = $users;
93
    } else {
94
      $result = $users;
95
    }
96
97
    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...
98
  }
99
100
  /**
101
   * Get PDO Instance.
102
   *
103
   * @param \DB\pdo $pdo set new instance
104
   *
105
   * @return \DB\pdo
106
   */
107
  public function pdo_instance(\DB\pdo $pdo = null)
108
  {
109
    if ($pdo) {
110
      $this->pdo = $pdo;
111
      //$this->db['use'] = $pdo->getDa
112
    }
113
114
    return $this->pdo;
115
  }
116
117
  /**
118
   * Static Chain.
119
   *
120
   * @return $this
121
   */
122
  public static function getInstance()
123
  {
124
    if (null === self::$_instance) {
125
      self::$_instance = new self();
126
    }
127
128
    return self::$_instance;
129
  }
130
131
  /**
132
   * Meta instance.
133
   *
134
   * @var \User\meta
135
   */
136
  private $meta_instance;
137
138
  /**
139
   * Get \User\meta instance.
140
   *
141
   * @return meta
142
   */
143
  public function meta()
144
  {
145
    $this->pdo_required();
146
    if (!$this->meta_instance) {
147
      $this->meta_instance = new meta($this->pdo);
148
    }
149
150
    return $this->meta_instance;
151
  }
152
153
  /**
154
   * Check user can do something.
155
   *
156
   * @return bool
157
   */
158
  public 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

158
  public 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...
159
  {
160
    if ($this->is_login()) {
161
      $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...
162
      if (!empty($check)) {
163
        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...
164
      }
165
    }
166
  }
167
168
  /**
169
   * Check pdo active.
170
   *
171
   * @throws \MVC\Exception
172
   *
173
   * @return void
174
   */
175
  public function pdo_required()
176
  {
177
    if (!$this->pdo) {
178
      throw new \MVC\Exception('Active PDO Required');
179
    }
180
  }
181
182
  public static function get_admin_pattern()
183
  {
184
    return self::getInstance()->admin_pattern;
185
  }
186
187
  /**
188
   * Check current user is superadmin.
189
   *
190
   * @return bool
191
   */
192
  public function is_admin()
193
  {
194
    if ($this->is_login()) {
195
      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...
196
    }
197
  }
198
199
  /**
200
   * If not admin auto redirect.
201
   *
202
   * @return void
203
   */
204
  public function admin_required(string $redirect = '/signin')
205
  {
206
    if (!$this->is_admin()) {
207
      \MVC\router::safe_redirect($redirect);
208
      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...
209
    }
210
  }
211
212
  /**
213
   * Get Current user role.
214
   *
215
   * @return string
216
   */
217
  public function get_role()
218
  {
219
    return isset($_SESSION['login']['role']) ? $_SESSION['login']['role'] : 'UNAUTHORIZED';
220
  }
221
222
  /**
223
   * Get All Available Roles.
224
   *
225
   * @return array
226
   */
227
  public function get_roles()
228
  {
229
    if (!$this->pdo) {
230
      throw new \MVC\Exception('PDO instance required', 1);
231
    }
232
    $list = \DB\schema::get_enumset_values($this->pdo, $this->dbname, 'role');
233
234
    return $list;
235
  }
236
237
  /**
238
   * Get user data.
239
   *
240
   * @return array|int|string|null if empty not logged in
241
   */
242
  public function userdata(string $what)
243
  {
244
    if ($this->is_login()) {
245
      if (class_exists('\Indosat\api')) {
246
        $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...
247
        $m3->setdata('login', \ArrayHelper\helper::unset($_SESSION['login'], ['password']));
248
      }
249
      if ('all' == $what) {
250
        return \ArrayHelper\helper::unset($_SESSION['login'], ['password']);
251
      }
252
      if (isset($_SESSION['login'][$what])) {
253
        return $_SESSION['login'][$what];
254
      }
255
    }
256
257
    return null;
258
  }
259
260
  /**
261
   * Get user data.
262
   *
263
   * @see \User\user::userdata
264
   */
265
  public function data(string $name)
266
  {
267
    return $this->userdata($name);
268
  }
269
270
  public function update_last_seen()
271
  {
272
    if ($this->is_login()) {
273
      $currentUsername = $this->userdata('username');
274
      $run = $this
275
        ->pdo
276
        ->query("UPDATE `userdata` SET `last_seen` = NOW() WHERE `username` = '{$currentUsername}';")
277
        ->exec();
278
279
      return $run;
280
    }
281
  }
282
283
  /**
284
   * Change password.
285
   *
286
   * @param int    $id   user id want to update
287
   * @param string $pass new password
288
   */
289
  public function update_password($id, $pass)
290
  {
291
    if (!$this->pdo) {
292
      throw new \MVC\Exception('Database not properly configured', 1);
293
    }
294
    if (!is_numeric($id)) {
0 ignored issues
show
introduced by
The condition is_numeric($id) is always true.
Loading history...
295
      throw new \MVC\Exception('User ID must be instance of integer', 1);
296
    }
297
    $db = $this->dbname;
298
    $crypt = new crypt();
299
    $pass = $crypt->encrypt('dimaslanjaka', $pass);
300
    if (!$this->is_admin()) {
301
      $q = "UPDATE `$db` SET `password` = :pass WHERE `$db`.`id` = :id AND `$db`.`role` <> 'superadmin' AND `$db`.`role` <> 'admin';";
302
    } else {
303
      $q = "UPDATE `$db` SET `password` = :pass WHERE `$db`.`id` = :id;";
304
    }
305
    $this->pdo->SQL_Exec($q, ['id' => $id, 'pass' => $pass]);
306
    $result = [];
307
    $chk = $this->pdo->SQL_Fetch("SELECT * FROM `$db` WHERE `$db`.`id` = :id AND `$db`.`password` = :pass", ['id' => $id, 'pass' => $pass]);
308
    if (!empty($chk) && isset($chk['id'])) {
309
      $result['error'] = false;
310
      $result['message'] = 'Password changed successfully';
311
      $result['title'] = 'User information';
312
    } else {
313
      $result['error'] = true;
314
    }
315
    $result = $this->hide_additional(array_merge($result, $chk));
316
317
    return $result;
318
  }
319
320
  public function delete_user_by_id($id)
321
  {
322
    $db = $this->dbname;
323
    $q = "DELETE FROM `$db` WHERE `$db`.`id` = :id";
324
    $this->pdo->SQL_Exec($q, ['id' => $id]);
325
  }
326
327
  public function login_required(string $redirect = '/signin')
328
  {
329
    if (!$this->is_login()) {
330
      \MVC\router::safe_redirect($redirect);
331
    }
332
  }
333
334
  /**
335
   * Get current database name.
336
   *
337
   * @return array
338
   */
339
  public function get_dbname()
340
  {
341
    return $this->pdo->query('select database()')->row_array();
342
  }
343
344
  public function login($username, $password)
345
  {
346
    if (!$this->pdo) {
347
      throw new \MVC\Exception('Database not properly configured', 1);
348
    }
349
    if ('GET' == $_SERVER['REQUEST_METHOD']) {
350
      $username = urldecode($username);
351
      $password = urldecode($password);
352
    }
353
    $username = addslashes($username);
354
    $db = $this->dbname;
355
    $password = addslashes($password);
356
    $crypt = new crypt();
357
    $password = $crypt->encrypt('dimaslanjaka', $password);
358
359
    $query = "SELECT * FROM `$db` WHERE username=:username AND password=:password";
360
    //var_dump($username, $password, $query);
361
    $exec = $this->pdo->SQL_Fetch($query, ['username' => $username, 'password' => $password]);
362
    //var_dump($this->pdo, $exec);
363
    $result = [];
364
    if (!empty($exec)) {
365
      if (isset($exec['id'])) {
366
        $result['error'] = false;
367
        $result['success'] = true;
368
        $result['message'] = 'login successful';
369
        $result = array_merge($exec, $result);
370
        $id = $exec['id'];
371
        $query = "UPDATE `$db` SET `last_login` = now() WHERE `$db`.`id` = $id;";
372
        foreach ($exec as $key => $value) {
373
          $_SESSION['login'][$key] = $value;
374
        }
375
        $this->pdo->SQL_Exec($query);
376
      } else {
377
        //var_dump($exec);
378
        $result['error'] = true;
379
      }
380
    } else {
381
      $result['error'] = true;
382
      $result['message'] = 'Empty response from database';
383
    }
384
    $result = $this->hide_additional($result);
385
386
    $this->user = $result;
387
    $this->pdo->resetQuery();
388
    $result['title'] = 'login information';
389
390
    return $result;
391
  }
392
393
  /**
394
   * Hidding additional information into backend response.
395
   *
396
   * @param array $result
397
   *
398
   * @author DimasLanjaka <[email protected]>
399
   *
400
   * @return array
401
   */
402
  public function hide_additional($result)
403
  {
404
    if (isset($result['role'])) {
405
      unset($result['role']);
406
    }
407
    if (isset($result['number'])) {
408
      unset($result['number']);
409
    }
410
    if (isset($result['password'])) {
411
      unset($result['password']);
412
    }
413
    if (isset($result['id'])) {
414
      unset($result['id']);
415
    }
416
417
    return $result;
418
  }
419
420
  /**
421
   * Check Login.
422
   *
423
   * @author Dimaslanjaka <[email protected]>
424
   *
425
   * @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...
426
   */
427
  public function check_login($callback)
428
  {
429
    if (isset($_SESSION['login'])) {
430
      if (isset($_SESSION['login']['id']) && is_numeric($_SESSION['login']['id'])) {
431
        if (is_callable($callback)) {
432
          call_user_func($callback, \ArrayHelper\helper::unset($_SESSION['login'], ['password']));
433
        }
434
      }
435
    } else {
436
      if (is_callable($callback)) {
437
        call_user_func($callback, [
438
          'error' => true,
439
          'message' => 'User Login required. Please relogin from login page',
440
          'unauthorized' => true,
441
        ]);
442
      }
443
    }
444
  }
445
446
  /**
447
   * Check user is login.
448
   *
449
   * @return bool
450
   */
451
  public function is_login()
452
  {
453
    $isLogin = isset($_SESSION['login']['username']) && !empty(trim($_SESSION['login']['username'])) ? true : false;
454
    if ($isLogin) {
455
      $this->username = $_SESSION['login']['username'];
456
      $check = $this->pdo->select('userdata')->where(['username' => $this->username])->row_array();
457
      if (!empty($check)) {
458
        foreach ($check as $key => $value) {
459
          $this->{$key} = $value;
460
        }
461
      }
462
    }
463
464
    return $isLogin;
465
  }
466
467
  /**
468
   * Update Display Name.
469
   *
470
   * @param Number $id
471
   * @param string $display
472
   */
473
  public function update_display_name($id, $display)
474
  {
475
    if (!$this->pdo) {
476
      throw new \MVC\Exception('Database not properly configured', 1);
477
    }
478
    $db = $this->dbname;
479
    $q = "UPDATE `$db` SET `display_name` = :name WHERE `$db`.`id` = :id;";
480
    $this->pdo->SQL_Exec($q, ['name' => $display, 'id' => $id]);
481
    $chk = $this->pdo->SQL_Fetch("SELECT `$db` WHERE `$db`.`id` = :id AND `display_name` = :name;", ['id' => $id, 'name' => $display]);
482
    $this->json($chk);
483
  }
484
485
  public function update_role($id, $role, $rgx = '/^(admin|client|superadmin)$/s')
486
  {
487
    if (!$this->pdo) {
488
      throw new \MVC\Exception('Database not properly configured', 1);
489
    }
490
    if (!preg_match($rgx, $role)) {
491
      throw new \MVC\Exception('Error Processing Change Role Request', 1);
492
    }
493
    $chk = [
494
      'error' => true,
495
      'message' => 'insufficient privileges',
496
    ];
497
    if ($this->is_admin()) {
498
      $db = $this->dbname;
499
      $q = "UPDATE `$db` SET `role` = :role WHERE `$db`.`id` = :id;";
500
      $this->pdo->SQL_Exec($q, ['role' => $role, 'id' => $id]);
501
      $chk = $this->pdo->SQL_Fetch("SELECT * FROM `$db` WHERE `$db`.`id` = :id AND `role` = :role;", ['id' => $id, 'role' => $role]);
502
      if (isset($chk['id'])) {
503
        $chk['error'] = false;
504
      }
505
    }
506
507
    return $chk;
508
  }
509
510
  public function register($username, $password, $name = 'user', $role = 'client')
511
  {
512
    if ('GET' == $_SERVER['REQUEST_METHOD']) {
513
      $username = urldecode($username);
514
      $password = urldecode($password);
515
    }
516
    $username = addslashes($username);
517
    $password = addslashes($password);
518
    $crypt = new crypt();
519
    $password = $crypt->encrypt('dimaslanjaka', $password);
520
    $db = $this->dbname;
521
    $q = "INSERT INTO `$db` (`display_name`, `username`, `password`, `role`) VALUES (:name, :username, :password, :role);";
522
    $exec = $this->pdo->SQL_Exec($q, ['name' => $name, 'username' => $username, 'password' => $password, 'role' => $role]);
523
    $result = [];
524
    if (isset($exec['id'])) {
525
      $chk = $this->pdo->SQL_Fetch("SELECT * FROM `$db` WHERE `$db`.`username` = :username AND `$db`.`password` = :password", ['username' => $username, 'password' => $password]);
526
      $result['success'] = true;
527
      $result = $this->hide_additional(array_merge($result, $chk));
528
    }
529
530
    return $result;
531
  }
532
533
  /**
534
   * JSON formatter.
535
   *
536
   * @param array $data
537
   * @param bool  $header
538
   * @param bool  $print
539
   */
540
  public function json($data = [], $header = true, $print = true)
541
  {
542
    return json::json($data, $header, $print);
543
  }
544
}
545