User::createUser()   B
last analyzed

Complexity

Conditions 6
Paths 2

Size

Total Lines 36
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 24
nc 2
nop 13
dl 0
loc 36
c 0
b 0
f 0
cc 6
rs 8.9137

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/**
3
 Copyright (C) 2018-2020 KANOUN Salim
4
 This program is free software; you can redistribute it and/or modify
5
 it under the terms of the Affero GNU General Public v.3 License as published by
6
 the Free Software Foundation;
7
 This program is distributed in the hope that it will be useful,
8
 but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
 Affero GNU General Public Public for more details.
11
 You should have received a copy of the Affero GNU General Public Public along
12
 with this program; if not, write to the Free Software Foundation, Inc.,
13
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
14
 */
15
16
/**
17
 * Check User's permissions status : Handles Connexion credential and ressource permissions check for scripts / form / apis...
18
 */
19
20
class User {
21
22
	private $linkpdo;
23
    
24
	//user details
25
	public $username;
26
	public $lastName;
27
	public $firstName;
28
	public $userEmail;
29
	public $userPhone;
30
	public $creationDateUser;
31
	public $lastConnexionDate;
32
    
33
	//Public variables to retrieve status account for the index page
34
	public $isAdministrator;
35
	public $userStatus;
36
	public $passwordDateValide;
37
	public $loginAttempt;
38
	public $isExistingUser;
39
	public $passwordCorrect;
40
    
41
	public $creationDatePassword;
42
	public $tempPassword;
43
	public $password;
44
    
45
	public $previousPassword1;
46
	public $previousPassword2;
47
	public $mainCenter;
48
	public $userJob;
49
    
50
	public $orthancAddress;
51
	public $orthancLogin;
52
	public $orthancPassword;
53
    
54
	//Constants roles available
55
	const ADMINISTRATOR="Administrator";
56
	const SUPERVISOR="Supervisor";
57
	const CONTROLLER="Controller";
58
	const MONITOR="Monitor";
59
	const INVESTIGATOR="Investigator";
60
	const REVIEWER="Reviewer";
61
    
62
	//Constants user status available
63
	const ACTIVATED="Activated";
64
	const DEACTIVATED="Deactivated";
65
	const BLOCKED="Blocked";
66
	const UNCONFIRMED="Unconfirmed";
67
    
68
    
69
70
	public function __construct(string $username, PDO $linkpdo) {
71
		$this->linkpdo=$linkpdo;
72
		//Get the username from DB to get the case sensitive username
73
		$connecter=$this->linkpdo->prepare('SELECT * FROM users WHERE username = :username');
74
		$connecter->execute(array("username" => $username));
75
		$queryResults=$connecter->fetch(PDO::FETCH_ASSOC);
76
		//If no match in database => User doesn't exist
77
		if (empty($queryResults)) {
78
			$this->isExistingUser=false;
79
		}
80
		else {
81
			$this->isExistingUser=true;
82
		}
83
        
84
		$this->username=$queryResults['username'];
85
		$this->lastName=$queryResults['last_name'];
86
		$this->firstName=$queryResults['first_name'];
87
		$this->userStatus=$queryResults['status'];
88
		$this->isAdministrator=$queryResults['is_administrator'];
89
		$this->userEmail=$queryResults['email'];
90
		$this->userPhone=$queryResults['phone'];
91
		$this->creationDateUser=$queryResults['creation_date'];
92
		$this->lastConnexionDate=$queryResults['connexion_date'];
93
		$this->creationDatePassword=$queryResults['creation_date_password'];
94
		$this->tempPassword=$queryResults['temp_password'];
95
		$this->password=$queryResults['password'];
96
		$this->previousPassword1=$queryResults['previous_password_1'];
97
		$this->previousPassword2=$queryResults['previous_password_2'];
98
		$this->loginAttempt=$queryResults['number_attempts'];
99
		$this->mainCenter=$queryResults['center'];
100
		$this->userJob=$queryResults['job'];
101
        
102
		$this->orthancAddress=$queryResults['orthanc_address'];
103
		$this->orthancLogin=$queryResults['orthanc_login'];
104
		$this->orthancPassword=$queryResults['orthanc_password'];
105
        
106
       
107
	}
108
    
109
	public static function getUserByEmail(String $email, PDO $linkpdo) {
110
        
111
		$connecter=$linkpdo->prepare('SELECT username FROM users WHERE email = :email');
112
		$connecter->execute(array("email" => $email));
113
		$username=$connecter->fetch(PDO::FETCH_COLUMN);
114
        
115
		return new User($username, $linkpdo);
116
        
117
	}
118
119
	/**
120
	 * Check connexion credential, number of tentatives and update status account if needed
121
	 * @param String $password
122
	 * @return boolean
123
	 */
124
	public function isPasswordCorrectAndActivitedAccount(string $password) {
125
        
126
		$date=new DateTime($this->creationDatePassword);
127
		$now=new DateTime();
128
		$delayDay=$date->diff($now)->format("%a");
129
        
130
		//If password delay over 90 => out dated password, need to be changed
131
		if (intVal($delayDay) <= 90) {
132
			$this->passwordDateValide=true;
133
		}
134
		else {
135
			$this->passwordDateValide=false;
136
		}
137
138
		//Check password correct
139
		if ($this->userStatus == User::UNCONFIRMED) {
140
			//Use the temp password for check
141
			$this->passwordCorrect=password_verify($password, $this->tempPassword);
142
		}else {
143
			//use the current password for check
144
			$this->passwordCorrect=password_verify($password, $this->password);
145
		}
146
        
147
		// If password OK, password date OK, and Account status active return OK for connexion
148
		if ($this->passwordCorrect && $this->passwordDateValide && $this->userStatus == User::ACTIVATED) {
149
			//Update the last connexion date in DB and number attempt account to zero
150
			$now=date("Y-m-d H:i:s");
151
			$reset_tentatives=$this->linkpdo->prepare('UPDATE users SET number_attempts = 0, connexion_date=:datenow WHERE username = :username');
152
			//Exécution
153
			$reset_tentatives->execute(array('username' => $this->username, 'datenow' =>$now));
154
			$this->loginAttempt=0;
155
            
156
			Session::logInfo('Connected Login : '.$this->username.' Admin '.(($this->isAdministrator) ? 'true' : 'false'));
157
            
158
			return true;
159
		}
160
		//Else, return false, add +1 to attempt account and block account if over 3
161
		else if (!$this->passwordCorrect) {
162
            
163
			Session::logInfo('Wrong Password : '.$this->username.' Admin '.(($this->isAdministrator) ? 'true' : 'false'));
164
            
165
			//Add +1 to attempt account
166
			$res=$this->linkpdo->prepare('UPDATE users SET number_attempts = number_attempts+1 WHERE username = :username');
167
			$res->execute(array('username' => $this->username));
168
			//Look at the new value
169
			$tentatives=$this->linkpdo->prepare('SELECT number_attempts FROM users WHERE username = :username');
170
			$tentatives->execute(array("username" => $this->username));
171
			$nb_tentatives=$tentatives->fetch(PDO::FETCH_ASSOC);
172
			$this->loginAttempt=$nb_tentatives['number_attempts'];
173
			//If over three block account
174
			if ($this->loginAttempt > 2) {
175
				$bloquer=$this->linkpdo->prepare('UPDATE users SET status = "Blocked" WHERE username = :username');
176
				$bloquer->execute(array('username' => $this->username));
177
				$this->userStatus="Blocked";
178
				//Log login event
179
				$log['message']="Blocked";
0 ignored issues
show
Comprehensibility Best Practice introduced by
$log was never initialized. Although not strictly required by PHP, it is generally a good practice to add $log = array(); before regardless.
Loading history...
180
				Tracker::logActivity($this->username, "User", null, null, "Account Blocked", $log);
181
				//Send email notification
182
				$this->sendBlockedEmail();
183
                
184
				return false;
185
186
			}
187
		    
188
		}else {
189
			//If blocked status, re-send email notification
190
			if ($this->userStatus == "Blocked") {
191
				$this->sendBlockedEmail();
192
			}
193
	        
194
			return false;
195
		        
196
		}
197
198
	}
199
    
200
	/**
201
	 * Return all studies available for the users (no matter it's role)
202
	 * @return array
203
	 */
204
	public function getAllStudiesWithRole() {
205
    	
206
		$connecter=$this->linkpdo->prepare('SELECT DISTINCT roles.study FROM roles, studies WHERE roles.username =:username
207
                                    AND studies.name=roles.study AND studies.active=1 ORDER BY roles.study');
208
		$connecter->execute(array(
209
				"username" => $this->username,
210
		));
211
    	
212
		$AvailableStudies=$connecter->fetchall(PDO::FETCH_COLUMN);
213
    	
214
		return $AvailableStudies;
215
	}
216
    
217
	/**
218
	 * Return if the requested Role in the requested study is allowed for the current user
219
	 * @param string $study
220
	 * @param string $role
221
	 * @param $job
222
	 * @return boolean
223
	 */
224
	public function isRoleAllowed(string $study, string $role) {
225
        
226
		$query='SELECT * FROM roles, studies, users WHERE roles.username = :username 
227
                                                            AND roles.name=:role 
228
                                                            AND studies.name=roles.study 
229
                                                            AND studies.active=1 
230
                                                            AND studies.name=:study 
231
                                                            AND users.username=roles.username 
232
                                                            AND users.status="Activated" ';
233
		$executeArray=array(
234
				"username" => $this->username,
235
				"role"=>$role,
236
				"study"=>$study
237
		);
238
    	
239
    	
240
		$connecter=$this->linkpdo->prepare($query);
241
		$connecter->execute($executeArray);
242
        
243
		$rownb=$connecter->rowCount();
244
       
245
		if ($rownb == 0) {
246
			return false;
247
		}
248
		else {
249
			return true;
250
		}
251
252
	}
253
254
	/**
255
	 * Return main and affiliated centers for the current user
256
	 * @return Array  : main centers in array
257
	 */
258
	public function getInvestigatorsCenters() {
259
		$centers=$this->getAffiliatedCenters();
260
		$centers[]=$this->mainCenter;
261
		return $centers;
262
	}
263
    
264
	/**
265
	 * Return main center object of this user
266
	 * @return Center
267
	 */
268
	public function getMainCenter() {
269
		return new Center($this->linkpdo, $this->mainCenter);
270
	}
271
    
272
	/**
273
	 * Return affiliated center of the user
274
	 */
275
	public function getAffiliatedCenters() {
276
		$result_center=$this->linkpdo->prepare('SELECT center FROM affiliated_centers WHERE affiliated_centers.username = :username ORDER BY center');
277
		$result_center->execute(array('username' => $this->username));
278
    	
279
		$affiliatedCenterBdd=$result_center->fetchAll(PDO::FETCH_COLUMN);
280
    	
281
		return $affiliatedCenterBdd;
282
	}
283
    
284
	/**
285
	 * Return affiliated center of the user as Object
286
	 */
287
	public function getAffiliatedCentersAsObjects() {
288
		$centersCode=$this->getAffiliatedCenters();
289
		$centersObjects=[];
290
		foreach ($centersCode as $code) {
291
			$centersObjects[]=new Center($this->linkpdo, $code);
292
		}
293
		return $centersObjects;
294
	}
295
    
296
	/**
297
	 * Return all available role in the called study for an user
298
	 * @param $study
299
	 * @return array of available roles
300
	 */
301
	public function getRolesInStudy(string $study) {
302
		$role=$this->linkpdo->prepare('SELECT name FROM roles WHERE roles.username = :username AND roles.study = :study ORDER BY roles.name');
303
		$role->execute(array('username' => $this->username, 'study' => $study));
304
		$data_role=$role->fetchall(PDO::FETCH_COLUMN);
305
		return $data_role;
306
	}
307
    
308
	/**
309
	 * Retrun role map of users  : each study as key with an array of available roles
310
	 * @return array[]
311
	 */
312
	public function getRolesMap() {
313
		$studies=$this->getAllStudiesWithRole();
314
		$map=[];
315
		foreach ($studies as $study) {
316
			$map[$study]=$this->getRolesInStudy($study);
317
		}
318
		return $map;
319
	}
320
    
321
	/**
322
	 * Check permission for a patient according to role
323
	 * @param number $patientNumber
324
	 * @param string $role
325
	 * @return boolean
326
	 */
327
	public function isPatientAllowed($patientCode, string $role) {
328
        
329
		if (empty($role)) return false;
330
331
		$patientObject=new Patient($patientCode, $this->linkpdo);
332
        
333
		//If Investigator check the current patient is from one of the centers of the user
334
		if ($role == $this::INVESTIGATOR) {
335
			$userCenters=$this->getInvestigatorsCenters();
336
			if (in_array($patientObject->patientCenter, $userCenters) && $this->isRoleAllowed($patientObject->patientStudy, $role)) {
337
				return true;
338
			}
339
        
340
		//For other patient's permission is defined by patient's study availabilty
341
		}else {
342
			if ($this->isRoleAllowed($patientObject->patientStudy, $role)) {
343
				return true;
344
			}
345
		}
346
        
347
		return false;
348
        
349
	}
350
    
351
	/**
352
	 * Check if the Visit and Role is available for the current user
353
	 * Checks for 
354
	 * - All : Check Role availability and Only access to non deleted visits
355
	 * - Investigator : Check Patient is allowed for this user
356
	 * - Reviewer : Check Review is availabl
357
	 * @param $id_visit
358
	 * @param string $role
359
	 * @return boolean
360
	 */
361
	public function isVisitAllowed($id_visit, string $role) {
362
        
363
		if (empty($role)) return false;
364
        
365
		$visitData=new Visit($id_visit, $this->linkpdo);
366
        
367
		//Check that called Role exists for users and visit is not deleted
368
		if ($this->isRoleAllowed($visitData->study, $role) && !$visitData->deleted) {
369
			if ($role == $this::INVESTIGATOR) { 
370
				if ($this->isPatientAllowed($visitData->patientCode, $role)) return true;
371
			}else if ($role == $this::REVIEWER) {
372
				//For reviewer the visit access is allowed if one of the created visits is still awaiting review
373
				//This is made to allow access to references scans
374
				$patientObject=$visitData->getPatient();
375
				$isAwaitingReview=$patientObject->getPatientStudy()->isHavingAwaitingReviewImagingVisit();
376
				return $isAwaitingReview;
377
			}else {
378
				//Controller, Supervisor, Admin, Monitor simply accept when role is available in patient's study (no specific rules)
379
				return true;
380
			}
381
            
382
		}
383
        
384
		return false;
385
        
386
	}
387
    
388
	/**
389
	 * Send warning emails to notify that the account is blocked (to administrators + user)
390
	 */
391
	private function sendBlockedEmail() {
392
		//Get all studies assosciated with account
393
		$linkedStudies=$this->getAllStudiesWithRole();
394
		//Send Email notification
395
		$sendEmail=new Send_Email($this->linkpdo);
396
		$sendEmail->addAminEmails()->addEmail($this->userEmail);
397
		$sendEmail->sendBlockedAccountNotification($this->username, $linkedStudies);
398
399
	}
400
    
401
	/**
402
	 * Add a role to the user
403
	 * @param string $role
404
	 * @param string $study
405
	 */
406
	public function addRole(string $role, string $study) {
407
		$addRole=$this->linkpdo->prepare('INSERT INTO roles(name, username, study)
408
                                        VALUES(:role, :username, :study)');
409
		//Exécution
410
		$addRole->execute(array('role' => $role,
411
								'username' => $this->username,
412
								'study' => $study));
413
        
414
	}
415
    
416
	/**
417
	 * Add Affiliated center to the user
418
	 * @param $centerCode
419
	 */
420
	public function addAffiliatedCenter($centerCode) {
421
		$addCenter=$this->linkpdo->prepare('INSERT INTO affiliated_centers(username, center)
422
                                                  VALUES(:username, :centerCode)');
423
		$addCenter->execute(array('username' => $this->username, 'centerCode' => $centerCode));
424
        
425
	}
426
    
427
	/**
428
	 * Remove an affiliated center to the user
429
	 * @param $centerCode
430
	 */
431
	public function removeAffiliatedCenter($centerCode) {
432
		$res=$this->linkpdo->prepare('DELETE FROM affiliated_centers WHERE username = :username
433
                  AND center = :centerCode');
434
		$res->execute(array('username' => $this->username,
435
				'centerCode' => $centerCode));
436
	}
437
    
438
	/**
439
	 * Delete a role in a study to the user
440
	 * @param string $study
441
	 * @param string $role
442
	 */
443
	public function deleteRole(string $study, string $role) {
444
		$res=$this->linkpdo->prepare('DELETE FROM roles WHERE username = :username
445
                  AND study = :study
446
                  AND name = :role');
447
		$res->execute(array('username' => $this->username,
448
				'study' => $study,
449
				'role' => $role));
450
	}
451
    
452
    
453
	/**
454
	 * Update password and account status of an user
455
	 * @param $password
456
	 * @param $status
457
	 */
458
	public function updateUserPassword(string $password, string $status) {
459
		//Update the database with new password and switch old passwords
460
		$req=$this->linkpdo->prepare('UPDATE users
461
                                    SET previous_password_2=users.previous_password_1,
462
                                        previous_password_1=users.password,
463
                                        password = :mdp,
464
                                        number_attempts = 0,
465
                                        creation_date_password = :datePassword,
466
                                        status = :status
467
                                    WHERE username = :username');
468
        
469
		$req->execute(array('username' => $this->username,
470
			'mdp' => password_hash($password, PASSWORD_DEFAULT),
471
			'status'=>$status,
472
			'datePassword' => date('Y-m-d')));
473
        
474
	}
475
    
476
	/**
477
	 * Generate a temp password and set account to unconfirmed
478
	 * @param $password
479
	 */
480
	public function setUnconfirmedAccount(string $password) {
481
        
482
		$req=$this->linkpdo->prepare('UPDATE users
483
                                    SET temp_password = :mdp,
484
                                        number_attempts = 0,
485
                                        creation_date_password = :datePassword,
486
                                        status = :status
487
                                    WHERE username = :username');
488
        
489
		$req->execute(array('username' => $this->username,
490
			'mdp' => password_hash($password, PASSWORD_DEFAULT),
491
			'status'=>User::UNCONFIRMED,
492
			'datePassword' => date('Y-m-d')));
493
        
494
	}
495
    
496
	/**
497
	 * Update users data
498
	 * @param $last_name
499
	 * @param $first_name
500
	 * @param $email
501
	 * @param $phone
502
	 * @param $job
503
	 * @param $status
504
	 * @param bool $administrator
505
	 * @param $mainCenterCode
506
	 * @param $orthancAddress
507
	 * @param $orthancLogin
508
	 * @param s$orthancPassword
509
	 */
510
	public function updateUser($last_name, $first_name, $email, $phone, $job, $status, bool $administrator, $mainCenterCode, $orthancAddress, $orthancLogin, $orthancPassword) {
511
        
512
		$req=$this->linkpdo->prepare('UPDATE users SET
513
    								last_name = :nom,
514
    								first_name = :prenom,
515
    								email = :email,
516
    								creation_date_password = :date_Utilisateur,
517
    								phone = :telephone,
518
    								job = :job,
519
    								is_administrator= :admin,
520
    								status = :statut,
521
									center= :numero_centre,
522
                                    orthanc_address= :orthancAddress, 
523
                                    orthanc_login= :orthancLogin, 
524
                                    orthanc_password= :orthancPassword
525
						        WHERE users.username = :username');
526
        
527
		$req->execute(array(
528
			'username'=> $this->username,
529
			'nom' => $last_name,
530
			'prenom' => $first_name,
531
			'email' => $email,
532
			'date_Utilisateur' => date('Y-m-d'),
533
			'telephone' => empty($phone) ? null : $phone,
534
			'job' => $job,
535
			'statut' => $status,
536
			'admin'=>intval($administrator),
537
			'numero_centre' => $mainCenterCode,
538
			'orthancAddress'=>empty($orthancAddress) ? null : $orthancAddress,
539
			'orthancLogin'=>empty($orthancLogin) ? null : $orthancLogin,
540
			'orthancPassword'=>empty($orthancPassword) ? null : $orthancPassword
541
		));
542
	}
543
    
544
	/**
545
	 * Create a new user
546
	 * @param $username
547
	 * @param $last_name
548
	 * @param $first_name
549
	 * @param $email
550
	 * @param $phone
551
	 * @param $mdp
552
	 * @param $job
553
	 * @param $mainCenter
554
	 * @param $administrator
555
	 * @param $orthancAddress
556
	 * @param $orthancLogin
557
	 * @param $orthancPassword
558
	 * @param PDO $linkpdo
559
	 * @throws Exception
560
	 */
561
	public static function createUser($username, $last_name, $first_name, $email, $phone,
562
				$mdp, $job, $mainCenter, $administrator, $orthancAddress, $orthancLogin, $orthancPassword, PDO $linkpdo) {
563
            
564
			//Check that new users is not already existing
565
			$accountQuery=$linkpdo->prepare('SELECT * FROM users WHERE (users.username=:username OR users.email=:email)');
566
			$accountQuery->execute(array(
567
				'username' => $username,
568
				'email' => $email
569
			));
570
			$existingAccount=$accountQuery->fetchAll();
571
            
572
			if (!empty($existingAccount)) {
573
			   throw new Exception("Account already existing");
574
			}
575
            
576
            
577
			// If new user, write it in database        
578
			$req=$linkpdo->prepare('INSERT INTO users(username, last_name, first_name, email, creation_date_password, phone, password, temp_password, job, center, creation_date, is_administrator, orthanc_address, orthanc_login, orthanc_password)
579
                  VALUES(:username, :nom, :prenom, :email, :creation_date_password, :telephone, :password, :tempPassword, :job, :numero_centre, :date_creation_account, :admin, :orthancAddress, :orthancLogin, :orthancPassword)');
580
            
581
			$req->execute(array(
582
				'username' => $username,
583
				'nom' => $last_name,
584
				'prenom' => $first_name,
585
				'email' => $email,
586
				'creation_date_password' => date('Y-m-d'),
587
				'telephone' => empty($phone) ? $phone : null,
588
				'password'=> password_hash($mdp, PASSWORD_DEFAULT),
589
				'tempPassword' => password_hash($mdp, PASSWORD_DEFAULT),
590
				'job' => $job,
591
				'numero_centre' => $mainCenter,
592
				'date_creation_account' => date("Y-m-d H:i:s"),
593
				'admin' => intval($administrator),
594
				'orthancAddress'=>empty($orthancAddress) ? null : $orthancAddress,
595
				'orthancLogin'=>empty($orthancLogin) ? null : $orthancLogin,
596
				'orthancPassword'=>empty($orthancPassword) ? null : $orthancPassword
597
			));
598
	}
599
    
600
}
601