Completed
Push — master ( f874ae...dbfc96 )
by vistart
02:44
created

UserController::actionConfirmPasswordHistory()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 17
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 17
ccs 0
cts 12
cp 0
rs 9.2
cc 4
eloc 12
nc 6
nop 2
crap 20
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 rhosocial\user\User;
16
use rhosocial\user\Profile;
17
use yii\console\Controller;
18
use yii\console\Exception;
19
use Yii;
20
21
/**
22
 * The simple operations associated with User.
23
 *
24
 * @version 1.0
25
 * @author vistart <[email protected]>
26
 */
27
class UserController extends Controller
28
{
29
    public $userClass;
30
    
31
    public $defaultAction = 'show';
32
    
33
    /**
34
     * Check and get valid User.
35
     * @return User
36
     * @throws Exception throw if User is not an instance inherited from `\rhosocial\user\User`.
37
     */
38
    protected function checkUserClass()
39
    {
40
        $userClass = $this->userClass;
41
        if (!class_exists($userClass)) {
42
            throw new Exception('User Class Invalid.');
43
        }
44
        if (!((new $userClass()) instanceof User)) {
45
            throw new Exception('User Class(' . $userClass . ') does not inherited from `\rhosocial\user\User`.');
46
        }
47
        return $userClass;
48
    }
49
    
50
    /**
51
     * Get user from database.
52
     * @param User|string|integer $user User ID.
53
     * @return User
54
     */
55
    protected function getUser($user)
56
    {
57
        $userClass = $this->checkUserClass();
58
        if (is_numeric($user)) {
59
            $user = $userClass::find()->id($user)->one();
60
        } elseif (is_string($user) && strlen($user)) {
61
            $user = $userClass::find()->guid($user)->one();
62
        }
63
        if (!$user || $user->getIsNewRecord()) {
0 ignored issues
show
Bug introduced by
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...
64
            throw new Exception('User Not Registered.');
65
        }
66
        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...
67
    }
68
    
69
    /**
70
     * Register new User.
71
     * @param string $password Password.
72
     * @param string $nickname If profile contains this property, this parameter is required.
73
     * @param string $firstName If profile contains this property, this parameter is required.
74
     * @param string $lastName If profile contains this propery, this parameter is required.
75
     */
76
    public function actionRegister($password, $nickname = null, $firstName = null, $lastName = null)
77
    {
78
        $userClass = $this->checkUserClass();
79
        
80
        $user = new $userClass(['password' => $password]);
81
        /* @var $user User */
82
        $profile = $user->createProfile([
83
            'nickname' => $nickname,
84
            'first_name' => $firstName,
85
            'last_name' => $lastName,
86
        ]);
87
        /* @var $profile Profile */
88
        try {
89
            is_null($profile) ? $user->register(): $user->register([$profile]);
90
        } catch (\Exception $ex) {
91
            throw new Exception($ex->getMessage());
92
        }
93
        echo "User Registered:\n";
94
        return $this->actionShow($user);
95
    }
96
    
97
    /**
98
     * Deregister user.
99
     * @param User|string|integer $user The user to be deregistered.
100
     */
101
    public function actionDeregister($user)
102
    {
103
        $user = $this->getUser($user);
104
        if ($user->deregister()) {
105
            echo "User (" . $user->getID() . ") Deregistered.\n";
106
            return true;
107
        }
108
        return false;
109
    }
110
    
111
    /**
112
     * Show User Information.
113
     * @param User|string|integer $user User ID.
114
     * @param boolean $guid Show GUID?
115
     * @param boolean $passHash Show PasswordH Hash?
116
     * @param boolean $accessToken Show Access Token?
117
     * @param boolean $authKey Show Authentication Key?
118
     */
119
    public function actionShow($user, $guid = false, $passHash = false, $accessToken = false, $authKey = false)
120
    {
121
        $user = $this->getUser($user);
122
        echo Yii::t('app', 'User') . " (" . $user->getID() . "), " . Yii::t('app', 'registered at') . " (" . $user->getCreatedAt() . ")"
123
                . ($user->getCreatedAt() == $user->getUpdatedAt() ? "" : ", " . Yii::t('app', 'last updated at') . " (" . $user->getUpdatedAt() . ")") .".\n";
124
        if ($guid) {
125
            echo "GUID: " . $user->getGUID() . "\n";
126
        }
127
        if ($passHash) {
128
            echo "Password Hash: " . $user->{$user->passwordHashAttribute} . "\n";
129
        }
130
        if ($accessToken) {
131
            echo "Access Token: " . $user->getAccessToken() . "\n";
132
        }
133
        if ($authKey) {
134
            echo "Authentication Key: " . $user->getAuthKey() . "\n";
135
        }
136
        return true;
137
    }
138
    
139
    /**
140
     * Show statistics.
141
     * @param User|string|integer $user User ID.
142
     */
143
    public function actionStat($user = null)
144
    {
145
        if ($user === null) {
146
            $count = User::find()->count();
147
            echo "Total number of user(s): " . $count . "\n";
148
            if ($count == 0) {
149
                return true;
150
            }
151
            $last = User::find()->orderByCreatedAt(SORT_DESC)->one();
152
            /* @var $last User */
153
            echo "Latest user (" . $last->getID() . ") registered at " . $last->getCreatedAt() . "\n";
154
            return true;
155
        }
156
        $user = $this->getUser($user);
0 ignored issues
show
Unused Code introduced by
$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...
157
        return true;
158
    }
159
    
160
    /**
161
     * Assign a role to user or revoke a role.
162
     * @param User|string|integer $user User ID.
163
     * @param string $operation Only `assign` and `revoke` are acceptable.
164
     * @param string $role Role name.
165
     */
166
    public function actionRole($user, $operation, $role)
167
    {
168
        $user = $this->getUser($user);
169
        $role = Yii::$app->authManager->getRole($role);
170
        if ($operation == 'assign') {
171
            try {
172
                $assignment = Yii::$app->authManager->assign($role, $user);
173
            } catch (\yii\db\IntegrityException $ex) {
174
                echo "Failed to assign `" . $role->name . "`.\n";
175
                echo "Maybe the role has been assigned.\n";
176
                return false;
177
            }
178
            if ($assignment) {
179
                echo "`$role->name`" . " assigned to User (" . $user->getID() . ") successfully.\n";
180
            } else {
181
                echo "Failed to assign `" . $role->name . "`.\n";
182
            }
183
            return true;
184
        }
185
        if ($operation == 'revoke') {
186
            $assignment = Yii::$app->authManager->revoke($role, $user);
187
            if ($assignment) {
188
                echo "`$role->name`" . " revoked from User (" . $user->getID() . ").\n";
189
            } else {
190
                echo "Failed to revoke `" . $role->name . "`.\n";
191
                echo "Maybe the role has not been assigned yet.\n";
192
            }
193
            return true;
194
        }
195
        echo "Unrecognized operation: $operation.\n";
196
        echo "The accepted operations are `assign` and `revoke`.\n";
197
        return false;
198
    }
199
    
200
    /**
201
     * Assign a permission to user or revoke a permission.
202
     * @param User|string|integer $user User ID.
203
     * @param string $operation Only `assign` and `revoke` are acceptable.
204
     * @param string $permission Permission name.
205
     */
206
    public function actionPermission($user, $operation, $permission)
207
    {
208
        $user = $this->getUser($user);
209
        $permission = Yii::$app->authManager->getPermission($permission);
210
        if ($operation == 'assign') {
211
            try {
212
                $assignment = Yii::$app->authManager->assign($permission, $user);
213
            } catch (\yii\db\IntegrityException $ex) {
214
                echo "Failed to assign `" . $role->name . "`.\n";
0 ignored issues
show
Bug introduced by
The variable $role does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
215
                echo "Maybe the permission has been assigned.\n";
216
                return false;
217
            }
218
            if ($assignment) {
219
                echo "`$permission->name`" . " assigned to User (" . $user->getID() . ") successfully.\n";
220
            } else {
221
                echo "Failed to assign `" . $permission->name . "`.\n";
222
            }
223
            return true;
224
        }
225
        if ($operation == 'revoke') {
226
            $assignment = Yii::$app->authManager->revoke($permission, $user);
227
            if ($assignment) {
228
                echo "`$permission->name`" . " revoked from User (" . $user->getID() . ").\n";
229
            } else {
230
                echo "Failed to revoke `" . $permission->name . "`.\n";
231
                echo "Maybe the permission has not been assigned yet.\n";
232
            }
233
            return true;
234
        }
235
        echo "Unrecognized operation: $operation.\n";
236
        echo "The accepted operations are `assign` and `revoke`.\n";
237
        return false;
238
    }
239
240
    /**
241
     * Validate password.
242
     * @param User|string|integer $user User ID.
243
     * @param password $password Password.
244
     */
245
    public function actionValidatePassword($user, $password)
246
    {
247
        $user = $this->getUser($user);
248
        $result = $user->validatePassword($password);
249
        if ($result) {
250
            echo "Correct.\n";
251
        } else {
252
            echo "Incorrect.\n";
253
        }
254
    }
255
256
    /**
257
     * Change password directly.
258
     * @param User|string|integer $user User ID.
259
     * @param string $password Password.
260
     */
261
    public function actionPassword($user, $password)
262
    {
263
        $user = $this->getUser($user);
264
        $user->applyForNewPassword();
265
        $result = $user->resetPassword($password, $user->getPasswordResetToken());
266
        if ($result) {
267
            echo "Password changed.\n";
268
        } else {
269
            echo "Password not changed.\n";
270
        }
271
    }
272
273
    /**
274
     * Confirm password in history.
275
     * This command will list all matching passwords in reverse order.
276
     * @param User|string|integer $user User ID.
277
     * @param string $password Password.
278
     */
279
    public function actionConfirmPasswordHistory($user, $password)
280
    {
281
        $user = $this->getUser($user);
282
        $passwordHistory = $user->passwordHistories;
283
        $passwordInHistory = false;
284
        foreach ($passwordHistory as $pass) {
285
            if ($pass->validatePassword($password)) {
286
                $passwordInHistory = true;
287
                echo "This password was created at " . $pass->getCreatedAt() . ".\n";
288
            }
289
        }
290
        if ($passwordInHistory) {
291
            return true;
292
        }
293
        echo "No password matched.\n";
294
        return false;
295
    }
296
}