This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | /** |
||
4 | * User Class |
||
5 | * |
||
6 | * @license http://opensource.org/licenses/MIT The MIT License (MIT) |
||
7 | * @author Omar El Gabry <[email protected]> |
||
8 | */ |
||
9 | |||
10 | class User extends Model{ |
||
0 ignored issues
–
show
|
|||
11 | |||
12 | /** |
||
13 | * Table name for this & extending classes. |
||
14 | * |
||
15 | * @var string |
||
16 | */ |
||
17 | public $table = "users"; |
||
18 | |||
19 | /** |
||
20 | * returns an associative array holds the user info(image, name, id, ...etc.) |
||
21 | * |
||
22 | * @access public |
||
23 | * @param integer $userId |
||
24 | * @return array Associative array of current user info/data. |
||
25 | * @throws Exception if $userId is invalid. |
||
26 | */ |
||
27 | public function getProfileInfo($userId){ |
||
28 | |||
29 | $database = Database::openConnection(); |
||
30 | $database->getById("users", $userId); |
||
31 | |||
32 | if($database->countRows() !== 1){ |
||
33 | throw new Exception("User ID " . $userId . " doesn't exists"); |
||
34 | } |
||
35 | |||
36 | $user = $database->fetchAssociative(); |
||
37 | |||
38 | $user["id"] = (int)$user["id"]; |
||
39 | $user["image"] = PUBLIC_ROOT . "img/profile_pictures/" . $user['profile_picture']; |
||
40 | // $user["email"] = empty($user['is_email_activated'])? null: $user['email']; |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
76% of this comment could be valid code. Did you maybe forget this after debugging?
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it. The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production. This check looks for comments that seem to be mostly valid code and reports them. ![]() |
|||
41 | |||
42 | return $user; |
||
43 | } |
||
44 | |||
45 | /** |
||
46 | * Update the current profile |
||
47 | * |
||
48 | * @access public |
||
49 | * @param integer $userId |
||
50 | * @param string $name |
||
51 | * @param string $password |
||
52 | * @param string $email |
||
53 | * @param string $confirmEmail |
||
54 | * @return bool|array |
||
55 | * @throws Exception If profile couldn't be updated |
||
56 | * |
||
57 | */ |
||
58 | public function updateProfileInfo($userId, $name, $password, $email, $confirmEmail){ |
||
59 | |||
60 | $database = Database::openConnection(); |
||
61 | $curUser = $this->getProfileInfo($userId); |
||
62 | |||
63 | $name = (!empty($name) && $name !== $curUser["name"])? $name: null; |
||
64 | $email = (!empty($confirmEmail) || (!empty($email) && $email !== $curUser["email"]))? $email: null; |
||
65 | |||
66 | // if new email === old email, this shouldn't return any errors for email, |
||
67 | // because they are not 'required', same for name. |
||
68 | $validation = new Validation(); |
||
69 | View Code Duplication | if(!$validation->validate([ |
|
0 ignored issues
–
show
This code seems to be duplicated across 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. ![]() |
|||
70 | "Name" => [$name, "alphaNumWithSpaces|minLen(4)|maxLen(30)"], |
||
71 | "Password" => [$password, "minLen(6)|password"], |
||
72 | "Email" => [$email, "email|emailUnique|maxLen(50)|equals(".$confirmEmail.")"]])){ |
||
73 | $this->errors = $validation->errors(); |
||
74 | return false; |
||
75 | } |
||
76 | |||
77 | $profileUpdated = ($password || $name || $email)? true: false; |
||
0 ignored issues
–
show
The expression
$name of type string|null is loosely compared to true ; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.
In PHP, under loose comparison (like For '' == false // true
'' == null // true
'ab' == false // false
'ab' == null // false
// It is often better to use strict comparison
'' === false // false
'' === null // false
![]() The expression
$email of type string|null is loosely compared to true ; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.
In PHP, under loose comparison (like For '' == false // true
'' == null // true
'ab' == false // false
'ab' == null // false
// It is often better to use strict comparison
'' === false // false
'' === null // false
![]() |
|||
78 | if($profileUpdated) { |
||
79 | |||
80 | $options = [ |
||
81 | $name => "name = :name ", |
||
82 | $password => "hashed_password = :hashed_password ", |
||
83 | $email => "pending_email = :pending_email, pending_email_token = :pending_email_token, email_token = :email_token " |
||
84 | ]; |
||
85 | |||
86 | $database->beginTransaction(); |
||
87 | $query = "UPDATE users SET "; |
||
88 | $query .= $this->applyOptions($options, ", "); |
||
89 | $query .= "WHERE id = :id LIMIT 1 "; |
||
90 | $database->prepare($query); |
||
91 | |||
92 | if($name) { |
||
0 ignored issues
–
show
The expression
$name of type string|null is loosely compared to true ; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.
In PHP, under loose comparison (like For '' == false // true
'' == null // true
'ab' == false // false
'ab' == null // false
// It is often better to use strict comparison
'' === false // false
'' === null // false
![]() |
|||
93 | $database->bindValue(':name', $name); |
||
94 | } |
||
95 | View Code Duplication | if($password) { |
|
0 ignored issues
–
show
This code seems to be duplicated across 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. ![]() |
|||
96 | $database->bindValue(':hashed_password', password_hash($password, PASSWORD_DEFAULT, array('cost' => Config::get('HASH_COST_FACTOR')))); |
||
97 | } |
||
98 | if($email) { |
||
0 ignored issues
–
show
The expression
$email of type string|null is loosely compared to true ; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.
In PHP, under loose comparison (like For '' == false // true
'' == null // true
'ab' == false // false
'ab' == null // false
// It is often better to use strict comparison
'' === false // false
'' === null // false
![]() |
|||
99 | $emailToken = sha1(uniqid(mt_rand(), true)); |
||
100 | $pendingEmailToken = sha1(uniqid(mt_rand(), true)); |
||
101 | $database->bindValue(':pending_email', $email); |
||
102 | $database->bindValue(':pending_email_token', $pendingEmailToken); |
||
103 | $database->bindValue(':email_token', $emailToken); |
||
104 | } |
||
105 | |||
106 | $database->bindValue(':id', $userId); |
||
107 | $result = $database->execute(); |
||
108 | |||
109 | if(!$result){ |
||
110 | $database->rollBack(); |
||
111 | throw new Exception("Couldn't update profile"); |
||
112 | } |
||
113 | |||
114 | // If email was updated, then send two emails, |
||
115 | // one for the current one asking user optionally to revoke, |
||
116 | // and another one for the new email asking user to confirm changes. |
||
117 | if($email){ |
||
0 ignored issues
–
show
The expression
$email of type string|null is loosely compared to true ; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.
In PHP, under loose comparison (like For '' == false // true
'' == null // true
'ab' == false // false
'ab' == null // false
// It is often better to use strict comparison
'' === false // false
'' === null // false
![]() |
|||
118 | $name = ($name)? $name: $curUser["name"]; |
||
119 | Email::sendEmail(Config::get('EMAIL_REVOKE_EMAIL'), $curUser["email"], ["name" => $name, "id" => $curUser["id"]], ["email_token" => $emailToken]); |
||
0 ignored issues
–
show
The variable
$emailToken does not seem to be defined for all execution paths leading up to this point.
If you define a variable conditionally, it can happen that it is not defined for all execution paths. Let’s take a look at an example: function myFunction($a) {
switch ($a) {
case 'foo':
$x = 1;
break;
case 'bar':
$x = 2;
break;
}
// $x is potentially undefined here.
echo $x;
}
In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined. Available Fixes
![]() |
|||
120 | Email::sendEmail(Config::get('EMAIL_UPDATE_EMAIL'), $email, ["name" => $name, "id" => $curUser["id"]], ["pending_email_token" => $pendingEmailToken]); |
||
0 ignored issues
–
show
The variable
$pendingEmailToken does not seem to be defined for all execution paths leading up to this point.
If you define a variable conditionally, it can happen that it is not defined for all execution paths. Let’s take a look at an example: function myFunction($a) {
switch ($a) {
case 'foo':
$x = 1;
break;
case 'bar':
$x = 2;
break;
}
// $x is potentially undefined here.
echo $x;
}
In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined. Available Fixes
![]() |
|||
121 | } |
||
122 | |||
123 | $database->commit(); |
||
124 | } |
||
125 | |||
126 | return ["emailUpdated" => (($email)? true: false)]; |
||
127 | } |
||
128 | |||
129 | /** |
||
130 | * Update Profile Picture. |
||
131 | * |
||
132 | * @access public |
||
133 | * @param integer $userId |
||
134 | * @param array $fileData |
||
135 | * @return mixed |
||
136 | * @throws Exception If failed to update profile picture. |
||
137 | */ |
||
138 | public function updateProfilePicture($userId, $fileData){ |
||
139 | |||
140 | $image = Uploader::uploadPicture($fileData, $userId); |
||
141 | |||
142 | if(!$image) { |
||
143 | $this->errors = Uploader::errors(); |
||
144 | return false; |
||
145 | } |
||
146 | |||
147 | $database = Database::openConnection(); |
||
148 | $query = "UPDATE users SET profile_picture = :profile_picture WHERE id = :id LIMIT 1"; |
||
149 | |||
150 | $database->prepare($query); |
||
151 | $database->bindValue(':profile_picture', $image["basename"]); |
||
152 | $database->bindValue(':id', $userId); |
||
153 | $result = $database->execute(); |
||
154 | |||
155 | // if update failed, then delete the user picture |
||
156 | if(!$result){ |
||
157 | Uploader::deleteFile(IMAGES . "profile_pictures/" . $image["basename"]); |
||
158 | throw new Exception("Profile Picture ". $image["basename"] . " couldn't be updated"); |
||
159 | } |
||
160 | |||
161 | return $image; |
||
162 | } |
||
163 | |||
164 | /** |
||
165 | * revoke Email updates |
||
166 | * |
||
167 | * @access public |
||
168 | * @param integer $userId |
||
169 | * @param string $emailToken |
||
170 | * @return mixed |
||
171 | * @throws Exception If failed to revoke email updates. |
||
172 | */ |
||
173 | public function revokeEmail($userId, $emailToken){ |
||
174 | |||
175 | if (empty($userId) || empty($emailToken)) { |
||
176 | return false; |
||
177 | } |
||
178 | |||
179 | $database = Database::openConnection(); |
||
180 | $database->prepare("SELECT * FROM users WHERE id = :id AND email_token = :email_token AND is_email_activated = 1 LIMIT 1"); |
||
181 | $database->bindValue(':id', $userId); |
||
182 | $database->bindValue(':email_token', $emailToken); |
||
183 | $database->execute(); |
||
184 | $users = $database->countRows(); |
||
185 | |||
186 | $query = "UPDATE users SET email_token = NULL, pending_email = NULL, pending_email_token = NULL WHERE id = :id LIMIT 1"; |
||
187 | $database->prepare($query); |
||
188 | $database->bindValue(':id', $userId); |
||
189 | $result = $database->execute(); |
||
190 | |||
191 | if(!$result){ |
||
192 | throw new Exception("Couldn't revoke email updates"); |
||
193 | } |
||
194 | |||
195 | View Code Duplication | if ($users === 1){ |
|
0 ignored issues
–
show
This code seems to be duplicated across 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. ![]() |
|||
196 | return true; |
||
197 | }else{ |
||
198 | Logger::log("REVOKE EMAIL", "User ID ". $userId . " is trying to revoke email using wrong token " . $emailToken, __FILE__, __LINE__); |
||
199 | return false; |
||
200 | } |
||
201 | } |
||
202 | |||
203 | /** |
||
204 | * update Email |
||
205 | * |
||
206 | * @access public |
||
207 | * @param integer $userId |
||
208 | * @param string $emailToken |
||
209 | * @return mixed |
||
210 | * @throws Exception If failed to update current email. |
||
211 | */ |
||
212 | public function updateEmail($userId, $emailToken){ |
||
213 | |||
214 | if (empty($userId) || empty($emailToken)) { |
||
215 | return false; |
||
216 | } |
||
217 | |||
218 | $database = Database::openConnection(); |
||
219 | $database->prepare("SELECT * FROM users WHERE id = :id AND pending_email_token = :pending_email_token AND is_email_activated = 1 LIMIT 1"); |
||
220 | $database->bindValue(':id', $userId); |
||
221 | $database->bindValue(':pending_email_token', $emailToken); |
||
222 | $database->execute(); |
||
223 | |||
224 | if($database->countRows() === 1){ |
||
225 | |||
226 | $user = $database->fetchAssociative(); |
||
227 | $validation = new Validation(); |
||
228 | $validation->addRuleMessage("emailUnique", "We can't change your email because it has been already taken!"); |
||
229 | |||
230 | if(!$validation->validate(["Email" => [$user["pending_email"], "emailUnique"]])){ |
||
231 | |||
232 | $query = "UPDATE users SET email_token = NULL, pending_email = NULL, pending_email_token = NULL WHERE id = :id LIMIT 1"; |
||
233 | $database->prepare($query); |
||
234 | $database->bindValue(':id', $userId); |
||
235 | $database->execute(); |
||
236 | |||
237 | $this->errors = $validation->errors(); |
||
238 | |||
239 | return false; |
||
240 | |||
241 | }else{ |
||
242 | |||
243 | $query = "UPDATE users SET email = :email, email_token = NULL, pending_email = NULL, pending_email_token = NULL WHERE id = :id LIMIT 1"; |
||
244 | $database->prepare($query); |
||
245 | $database->bindValue(':id', $userId); |
||
246 | $database->bindValue(':email', $user["pending_email"]); |
||
247 | $result = $database->execute(); |
||
248 | |||
249 | if(!$result){ |
||
250 | throw new Exception("Couldn't update current email"); |
||
251 | } |
||
252 | |||
253 | return true; |
||
254 | } |
||
255 | }else { |
||
256 | |||
257 | $query = "UPDATE users SET email_token = NULL, pending_email = NULL, pending_email_token = NULL WHERE id = :id LIMIT 1"; |
||
258 | $database->prepare($query); |
||
259 | $database->bindValue(':id', $userId); |
||
260 | $database->execute(); |
||
261 | |||
262 | Logger::log("UPDATE EMAIL", "User ID ". $userId . " is trying to update email using wrong token " . $emailToken, __FILE__, __LINE__); |
||
263 | return false; |
||
264 | } |
||
265 | |||
266 | } |
||
267 | |||
268 | /** |
||
269 | * Get Notifications for newsfeed, posts & files. |
||
270 | * |
||
271 | * @access public |
||
272 | * @param integer $userId |
||
273 | * @return array |
||
274 | */ |
||
275 | View Code Duplication | public function getNotifications($userId){ |
|
0 ignored issues
–
show
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. ![]() |
|||
276 | |||
277 | $database = Database::openConnection(); |
||
278 | $query = "SELECT target, count FROM notifications WHERE user_id = :user_id"; |
||
279 | |||
280 | $database->prepare($query); |
||
281 | $database->bindValue(":user_id", $userId); |
||
282 | $database->execute(); |
||
283 | |||
284 | $notifications = $database->fetchAllAssociative(); |
||
285 | return $notifications; |
||
286 | } |
||
287 | |||
288 | /** |
||
289 | * Clear Notifications for a specific target |
||
290 | * |
||
291 | * @access public |
||
292 | * @param integer $userId |
||
293 | * @param string $table |
||
294 | */ |
||
295 | public function clearNotifications($userId, $table){ |
||
296 | |||
297 | $database = Database::openConnection(); |
||
298 | $query = "UPDATE notifications SET count = 0 WHERE user_id = :user_id AND target = :target"; |
||
299 | |||
300 | $database->prepare($query); |
||
301 | $database->bindValue(":user_id", $userId); |
||
302 | $database->bindValue(":target", $table); |
||
303 | $result = $database->execute(); |
||
304 | |||
305 | if(!$result) { |
||
306 | Logger::log("NOTIFICATIONS", "Couldn't clear notifications", __FILE__, __LINE__); |
||
307 | } |
||
308 | } |
||
309 | |||
310 | /** |
||
311 | * Returns an overview about the current system: |
||
312 | * 1. counts of newsfeed, posts, files, users |
||
313 | * 2. latest updates by using "UNION" |
||
314 | * |
||
315 | * @access public |
||
316 | * @return array |
||
317 | * |
||
318 | */ |
||
319 | public function dashboard(){ |
||
320 | |||
321 | $database = Database::openConnection(); |
||
322 | |||
323 | // 1. count |
||
324 | $tables = ["newsfeed", "posts", "files", "users"]; |
||
325 | $stats = []; |
||
326 | |||
327 | foreach($tables as $table){ |
||
328 | $stats[$table] = $database->countAll($table); |
||
329 | } |
||
330 | |||
331 | // 2. latest updates |
||
332 | // Using UNION to union the data fetched from different tables. |
||
333 | // @see http://www.w3schools.com/sql/sql_union.asp |
||
334 | // @see (mikeY) http://stackoverflow.com/questions/6849063/selecting-data-from-two-tables-and-ordering-by-date |
||
335 | |||
336 | // Sub Query: In SELECT, The outer SELECT must have alias, like "updates" here. |
||
337 | // NOTE: The outer SELECT is not needed; You don't need to wrap the union-ed select statements. |
||
338 | // @see http://stackoverflow.com/questions/1888779/every-derived-table-must-have-its-own-alias |
||
339 | |||
340 | $query = "SELECT * FROM ("; |
||
341 | $query .= "SELECT 'newsfeed' AS target, content AS title, date, users.name FROM newsfeed, users WHERE user_id = users.id UNION "; |
||
342 | $query .= "SELECT 'posts' AS target, title, date, users.name FROM posts, users WHERE user_id = users.id UNION "; |
||
343 | $query .= "SELECT 'files' AS target, filename AS title, date, users.name FROM files, users WHERE user_id = users.id "; |
||
344 | $query .= ") AS updates ORDER BY date DESC LIMIT 10"; |
||
345 | $database->prepare($query); |
||
346 | $database->execute(); |
||
347 | $updates = $database->fetchAllAssociative(); |
||
348 | |||
349 | $data = array("stats" => $stats, "updates" => $updates); |
||
350 | return $data; |
||
351 | } |
||
352 | |||
353 | /** |
||
354 | * Reporting Bug, Feature, or Enhancement. |
||
355 | * |
||
356 | * @access public |
||
357 | * @param integer $userId |
||
358 | * @param string $subject |
||
359 | * @param string $label |
||
360 | * @param string $message |
||
361 | * @return bool |
||
362 | * |
||
363 | */ |
||
364 | public function reportBug($userId, $subject, $label, $message){ |
||
365 | |||
366 | $validation = new Validation(); |
||
367 | if(!$validation->validate([ |
||
368 | "Subject" => [$subject, "required|minLen(4)|maxLen(80)"], |
||
369 | "Label" => [$label, "required|inArray(".Utility::commas(["bug", "feature", "enhancement"]).")"], |
||
370 | "Message" => [$message, "required|minLen(4)|maxLen(1800)"]])){ |
||
371 | |||
372 | $this->errors = $validation->errors(); |
||
373 | return false; |
||
374 | } |
||
375 | |||
376 | $curUser = $this->getProfileInfo($userId); |
||
377 | $data = ["subject" => $subject, "label" => $label, "message" => $message]; |
||
378 | |||
379 | // email will be sent to the admin |
||
380 | Email::sendEmail(Config::get('EMAIL_REPORT_BUG'), Config::get('ADMIN_EMAIL'), ["id" => $userId, "name" => $curUser["name"]], $data); |
||
381 | |||
382 | return true; |
||
383 | } |
||
384 | } |
||
385 |
You can fix this by adding a namespace to your class:
When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.