Issues (174)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

console/controllers/UserController.php (3 issues)

Upgrade to new PHP Analysis Engine

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
 *  _   __ __ _____ _____ ___  ____  _____
5
 * | | / // // ___//_  _//   ||  __||_   _|
6
 * | |/ // /(__  )  / / / /| || |     | |
7
 * |___//_//____/  /_/ /_/ |_||_|     |_|
8
 * @link https://vistart.me/
9
 * @copyright Copyright (c) 2016 - 2017 vistart
10
 * @license https://vistart.me/license/
11
 */
12
13
namespace rhosocial\user\console\controllers;
14
15
use Faker\Factory;
16
use rhosocial\base\helpers\Timezone;
17
use rhosocial\user\User;
18
use rhosocial\user\Profile;
19
use yii\console\Controller;
20
use yii\console\Exception;
21
use Yii;
22
use yii\helpers\Console;
23
24
/**
25
 * The simple operations associated with User.
26
 *
27
 * @version 1.0
28
 * @author vistart <[email protected]>
29
 */
30
class UserController extends Controller
31
{
32
    public $userClass;
33
    
34
    public $defaultAction = 'show';
35
    
36
    /**
37
     * Check and get valid User.
38
     * @return User
39
     * @throws Exception throw if User is not an instance inherited from `\rhosocial\user\User`.
40
     */
41
    protected function checkUserClass()
42
    {
43
        $userClass = $this->userClass;
44
        if (!class_exists($userClass)) {
45
            throw new Exception('User Class Invalid.');
46
        }
47
        if (!((new $userClass()) instanceof User)) {
48
            throw new Exception('User Class(' . $userClass . ') does not inherited from `\rhosocial\user\User`.');
49
        }
50
        return $userClass;
51
    }
52
    
53
    /**
54
     * Get user from database.
55
     * @param User|string|integer $user User ID.
56
     * @return User
57
     * @throws Exception
58
     */
59
    protected function getUser($user)
60
    {
61
        $userClass = $this->checkUserClass();
62
        if (is_numeric($user)) {
63
            $user = $userClass::find()->id($user)->one();
64
        } elseif (is_string($user) && strlen($user)) {
65
            $user = $userClass::find()->guid($user)->one();
66
        }
67
        if (!$user || $user->getIsNewRecord()) {
0 ignored issues
show
It seems like $user is not always an object, but can also be of type array|string. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
68
            throw new Exception('User Not Registered.');
69
        }
70
        return $user;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $user; (yii\db\ActiveRecord|array|string) is incompatible with the return type documented by rhosocial\user\console\c...UserController::getUser of type rhosocial\user\User.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
71
    }
72
    
73
    /**
74
     * Register new User.
75
     * @param string $password Password.
76
     * @param string $nickname If profile contains this property, this parameter is required.
77
     * @param string $firstName If profile contains this property, this parameter is required.
78
     * @param string $lastName If profile contains this propery, this parameter is required.
79
     * @return int
80
     * @throws Exception
81
     */
82
    public function actionRegister($password, $nickname = null, $firstName = null, $lastName = null)
83
    {
84
        $userClass = $this->checkUserClass();
85
        
86
        $user = new $userClass(['password' => $password]);
87
        /* @var $user User */
88
        $profile = $user->createProfile([
89
            'nickname' => $nickname,
90
            'first_name' => $firstName,
91
            'last_name' => $lastName,
92
        ]);
93
        /* @var $profile Profile */
94
        try {
95
            is_null($profile) ? $user->register(): $user->register([$profile]);
96
        } catch (\Exception $ex) {
97
            throw new Exception($ex->getMessage());
98
        }
99
        echo "User Registered:\n";
100
        return $this->actionShow($user);
101
    }
102
    
103
    /**
104
     * Deregister user.
105
     * @param User|string|integer $user The user to be deregistered.
106
     * @return int
107
     */
108
    public function actionDeregister($user)
109
    {
110
        $user = $this->getUser($user);
111
        if ($user->deregister()) {
112
            echo "User (" . $user->getID() . ") Deregistered.\n";
113
            return static::EXIT_CODE_NORMAL;
114
        }
115
        return static::EXIT_CODE_ERROR;
116
    }
117
    
118
    /**
119
     * Show User Information.
120
     * @param User|string|integer $user User ID.
121
     * @param boolean $guid Show GUID?
122
     * @param boolean $passHash Show PasswordH Hash?
123
     * @param boolean $accessToken Show Access Token?
124
     * @param boolean $authKey Show Authentication Key?
125
     * @return int
126
     */
127
    public function actionShow($user, $guid = false, $passHash = false, $accessToken = false, $authKey = false)
128
    {
129
        $user = $this->getUser($user);
130
        echo Yii::t('app', 'User') . " (" . $user->getID() . "), " . Yii::t('app', 'registered at') . " (" . $user->getCreatedAt() . ")"
131
                . ($user->getCreatedAt() == $user->getUpdatedAt() ? "" : ", " . Yii::t('app', 'last updated at') . " (" . $user->getUpdatedAt() . ")") .".\n";
132
        if ($guid) {
133
            echo "GUID: " . $user->getGUID() . "\n";
134
        }
135
        if ($passHash) {
136
            echo "Password Hash: " . $user->{$user->passwordHashAttribute} . "\n";
137
        }
138
        if ($accessToken) {
139
            echo "Access Token: " . $user->getAccessToken() . "\n";
140
        }
141
        if ($authKey) {
142
            echo "Authentication Key: " . $user->getAuthKey() . "\n";
143
        }
144
        return static::EXIT_CODE_NORMAL;
145
    }
146
    
147
    /**
148
     * Show statistics.
149
     * @param User|string|integer $user User ID.
150
     * @return int
151
     */
152
    public function actionStat($user = null)
153
    {
154
        if ($user === null) {
155
            $count = User::find()->count();
156
            echo "Total number of user(s): " . $count . "\n";
157
            if ($count == 0) {
158
                return static::EXIT_CODE_NORMAL;
159
            }
160
            $last = User::find()->orderByCreatedAt(SORT_DESC)->one();
161
            /* @var $last User */
162
            echo "Latest user (" . $last->getID() . ") registered at " . $last->getCreatedAt() . "\n";
163
            return static::EXIT_CODE_NORMAL;
164
        }
165
        $user = $this->getUser($user);
0 ignored issues
show
$user is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
166
        return static::EXIT_CODE_NORMAL;
167
    }
168
    
169
    /**
170
     * Assign a role to user or revoke a role.
171
     * @param User|string|integer $user User ID.
172
     * @param string $operation Only `assign` and `revoke` are acceptable.
173
     * @param string $role Role name.
174
     * @return int
175
     */
176
    public function actionRole($user, $operation, $role)
177
    {
178
        $user = $this->getUser($user);
179
        $role = Yii::$app->authManager->getRole($role);
180
        if ($operation == 'assign') {
181
            try {
182
                $assignment = Yii::$app->authManager->assign($role, $user);
183
            } catch (\yii\db\IntegrityException $ex) {
184
                echo "Failed to assign `" . $role->name . "`.\n";
185
                echo "Maybe the role has been assigned.\n";
186
                return static::EXIT_CODE_ERROR;
187
            }
188
            if ($assignment) {
189
                echo "`$role->name`" . " assigned to User (" . $user->getID() . ") successfully.\n";
190
            } else {
191
                echo "Failed to assign `" . $role->name . "`.\n";
192
            }
193
            return static::EXIT_CODE_NORMAL;
194
        }
195
        if ($operation == 'revoke') {
196
            $assignment = Yii::$app->authManager->revoke($role, $user);
197
            if ($assignment) {
198
                echo "`$role->name`" . " revoked from User (" . $user->getID() . ").\n";
199
            } else {
200
                echo "Failed to revoke `" . $role->name . "`.\n";
201
                echo "Maybe the role has not been assigned yet.\n";
202
            }
203
            return static::EXIT_CODE_NORMAL;
204
        }
205
        echo "Unrecognized operation: $operation.\n";
206
        echo "The accepted operations are `assign` and `revoke`.\n";
207
        return static::EXIT_CODE_ERROR;
208
    }
209
    
210
    /**
211
     * Assign a permission to user or revoke a permission.
212
     * @param User|string|integer $user User ID.
213
     * @param string $operation Only `assign` and `revoke` are acceptable.
214
     * @param string $permission Permission name.
215
     * @return int
216
     */
217
    public function actionPermission($user, $operation, $permission)
218
    {
219
        $user = $this->getUser($user);
220
        $permission = Yii::$app->authManager->getPermission($permission);
221
        if ($operation == 'assign') {
222
            try {
223
                $assignment = Yii::$app->authManager->assign($permission, $user);
224
            } catch (\yii\db\IntegrityException $ex) {
225
                echo "Failed to assign `" . $permission->name . "`.\n";
226
                echo "Maybe the permission has been assigned.\n";
227
                return static::EXIT_CODE_ERROR;
228
            }
229
            if ($assignment) {
230
                echo "`$permission->name`" . " assigned to User (" . $user->getID() . ") successfully.\n";
231
            } else {
232
                echo "Failed to assign `" . $permission->name . "`.\n";
233
            }
234
            return static::EXIT_CODE_NORMAL;
235
        }
236
        if ($operation == 'revoke') {
237
            $assignment = Yii::$app->authManager->revoke($permission, $user);
238
            if ($assignment) {
239
                echo "`$permission->name`" . " revoked from User (" . $user->getID() . ").\n";
240
            } else {
241
                echo "Failed to revoke `" . $permission->name . "`.\n";
242
                echo "Maybe the permission has not been assigned yet.\n";
243
            }
244
            return static::EXIT_CODE_NORMAL;
245
        }
246
        echo "Unrecognized operation: $operation.\n";
247
        echo "The accepted operations are `assign` and `revoke`.\n";
248
        return static::EXIT_CODE_ERROR;
249
    }
250
251
    /**
252
     * Validate password.
253
     * @param User|string|integer $user User ID.
254
     * @param password $password Password.
255
     * @return int
256
     */
257
    public function actionValidatePassword($user, $password)
258
    {
259
        $user = $this->getUser($user);
260
        $result = $user->validatePassword($password);
261
        if ($result) {
262
            echo "Correct.\n";
263
        } else {
264
            echo "Incorrect.\n";
265
        }
266
        return static::EXIT_CODE_NORMAL;
267
    }
268
269
    /**
270
     * Change password directly.
271
     * @param User|string|integer $user User ID.
272
     * @param string $password Password.
273
     * @return int
274
     */
275
    public function actionPassword($user, $password)
276
    {
277
        $user = $this->getUser($user);
278
        $user->applyForNewPassword();
279
        $result = $user->resetPassword($password, $user->getPasswordResetToken());
280
        if ($result) {
281
            echo "Password changed.\n";
282
        } else {
283
            echo "Password not changed.\n";
284
        }
285
        return static::EXIT_CODE_NORMAL;
286
    }
287
288
    /**
289
     * Confirm password in history.
290
     * This command will list all matching passwords in reverse order.
291
     * @param User|string|integer $user User ID.
292
     * @param string $password Password.
293
     * @return int
294
     */
295
    public function actionConfirmPasswordHistory($user, $password)
296
    {
297
        $user = $this->getUser($user);
298
        $passwordHistory = $user->passwordHistories;
299
        $passwordInHistory = 0;
300
        foreach ($passwordHistory as $pass) {
301
            if ($pass->validatePassword($password)) {
302
                $passwordInHistory++;
303
                echo "This password was created at " . $pass->getCreatedAt() . ".\n";
304
            }
305
        }
306
        if ($passwordInHistory) {
307
            echo "$passwordInHistory matched.\n";
308
            return static::EXIT_CODE_NORMAL;
309
        }
310
        echo "No password matched.\n";
311
        return static::EXIT_CODE_ERROR;
312
    }
313
314
    /**
315
     * Register users for testing.
316
     * @param int $total
317
     * @param string password
318
     * @return int
319
     * @throws Exception
320
     */
321
    public function actionAddTestUsers($total = 1000, $password = '123456')
322
    {
323
        echo "Registration Start...\n";
324
        $userClass = $this->checkUserClass();
325
326
        $faker = Factory::create(str_replace('-', '_', Yii::$app->language));
327
        $total = (int)$total;
328
        $acc = 0;
329
        $time = time();
330
        $timezones = array_keys(Timezone::generateList());
331
        $genders = [Profile::GENDER_MALE, Profile::GENDER_FEMALE];
332
        for ($i = 1; $i <= $total; $i++) {
333
            $user = new $userClass(['password' => $password]);
334
            $user->source = 'console_test';
335
            /* @var $user User */
336
            $gender = $faker->randomElement($genders);
337
            $profile = null;
338
            if ($gender == Profile::GENDER_MALE) {
339
                $profile = $user->createProfile([
340
                    'nickname' => $faker->titleMale,
341
                    'first_name' => $faker->firstNameMale,
342
                    'last_name' => $faker->lastName,
343
                    'timezone' => $faker->randomElement($timezones),
344
                ]);
345
            } elseif ($gender == Profile::GENDER_FEMALE) {
346
                $profile = $user->createProfile([
347
                    'nickname' => $faker->titleFemale,
348
                    'first_name' => $faker->firstNameFemale,
349
                    'last_name' => $faker->lastName,
350
                    'timezone' => $faker->randomElement($timezones),
351
                ]);
352
            }
353
            $profile->gender = $gender;
354
            /* @var $profile Profile */
355
            try {
356
                $result = (is_null($profile) ? $user->register() : $user->register([$profile]));
357
                if ($result !== true) {
358
                    throw new Exception("User Not Registered.");
359
                }
360
            } catch (\Exception $ex) {
361
                echo $ex->getMessage() . "\n";
362
                continue;
363
            }
364
            $acc++;
365
            if ($acc % 10 == 0) {
366
                $percent = (float)$i / $total * 100;
367
                echo "$acc users registered($percent% finished).\n";
368
            }
369
        }
370
        $consumed = time() - $time;
371
        echo "Totally $acc users registered.\n";
372
        echo "Registration finished($consumed seconds consumed).\n";
373
        return static::EXIT_CODE_NORMAL;
374
    }
375
376
    /**
377
     * Deregister all users for testing.
378
     * @return int
379
     */
380
    public function actionRemoveAllTestUsers()
381
    {
382
        echo "Deregistration Start...\n";
383
384
        $userClass = $this->checkUserClass();
385
        $acc = 0;
386
        $time = time();
387
        $total = (int)$userClass::find()->andWhere(['source' => 'console_test'])->count();
388
        echo "$total users maybe deregistered.\n";
389
        foreach ($userClass::find()->andWhere(['source' => 'console_test'])->each() as $user) {
390
            try {
391
                $user->deregister();
392
            } catch (\Exception $ex) {
393
                echo $ex->getMessage() . "\n";
394
                continue;
395
            }
396
            $acc++;
397
            if ($acc % 10 == 0) {
398
                $percent = (float)$acc / $total * 100;
399
                echo "$acc users deregistered($percent% finished).\n";
400
            }
401
        }
402
        $consumed = time() - $time;
403
        echo "Totally $acc users deregistered.\n";
404
        echo "Deregistration finished($consumed seconds consumed).\n";
405
        return static::EXIT_CODE_NORMAL;
406
    }
407
}
408