Passed
Push — master ( 28a360...d194a0 )
by Mihail
11:20
created

User::actionRecovery()   B

Complexity

Conditions 8
Paths 9

Size

Total Lines 55
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 55
rs 7.4033
cc 8
eloc 30
nc 9
nop 2

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Apps\Controller\Front;
4
5
use Apps\ActiveRecord\Invite;
6
use Apps\ActiveRecord\UserRecovery;
7
use Apps\Model\Front\User\FormRecovery;
8
use Apps\Model\Front\User\FormRegister;
9
use Apps\Model\Front\User\FormSocialAuth;
10
use Extend\Core\Arch\FrontAppController;
11
use Ffcms\Core\App;
12
use Apps\Model\Front\User\FormLogin;
13
use Ffcms\Core\Arch\View;
14
use Ffcms\Core\Exception\ForbiddenException;
15
use Ffcms\Core\Exception\NativeException;
16
use Ffcms\Core\Exception\NotFoundException;
17
use Ffcms\Core\Exception\SyntaxException;
18
use Ffcms\Core\Helper\Type\Obj;
19
use Ffcms\Core\Helper\Type\Str;
20
use Apps\ActiveRecord\UserLog;
21
22
/**
23
 * Class User - standard user controller: login/signup/logout/etc
24
 * @package Apps\Controller\Front
25
 */
26
class User extends FrontAppController
27
{
28
    const EVENT_USER_LOGIN_SUCCESS = 'user.login.success';
29
    const EVENT_USER_LOGIN_FAIL = 'user.login.fail';
30
    const EVENT_USER_REGISTER_SUCCESS = 'user.signup.success';
31
    const EVENT_USER_REGISTER_FAIL = 'user.signup.fail';
32
33
    /**
34
     * View login form and process submit action
35
     * @throws ForbiddenException
36
     * @throws NativeException
37
     * @throws SyntaxException
38
     */
39
    public function actionLogin()
40
    {
41
        if (App::$User->isAuth()) { // always auth? get the f*ck out
42
            throw new ForbiddenException();
43
        }
44
45
        $configs = $this->getConfigs();
46
        // load login model
47
        $loginForm = new FormLogin($configs['captchaOnLogin'] === 1);
48
49
        // check if data is send and valid
50
        if ($loginForm->send() && $loginForm->validate()) {
51
            if ($loginForm->tryAuth()) {
52
                // initialize success event
53
                App::$Event->run(static::EVENT_USER_LOGIN_SUCCESS, [
54
                    'model' => $loginForm
55
                ]);
56
                App::$Response->redirect('/'); // void header change & exit()
57
            }
58
            App::$Session->getFlashBag()->add('error', __('User is never exist or password is incorrect!'));
59
            // initialize fail event
60
            App::$Event->run(static::EVENT_USER_LOGIN_FAIL, [
61
               'model' => $loginForm
62
            ]);
63
        }
64
65
        // render view
66
        return App::$View->render('login', [
67
            'model' => $loginForm->filter(),
68
            'useCaptcha' => $configs['captchaOnLogin'] === 1
69
        ]);
70
    }
71
72
    /**
73
     * Authorization in social networks over hybridauth layer. How its work:
74
     *  1. User visit actionSocialauth and initialize openid instance
75
     *  2. 3rd party software generate redirect to @api -> User::actionEndpoint() (as endpoint) where create hash's, tokens and other shit
76
     *  3. After successful auth on service user redirect back to actionSocialauth and we can work with $userIdentity if no exceptions catched.
77
     * Don't aks me "why did you do this sh@t"? I want to make container in User class, but this shit work only on direct call on endpoint.
78
     * @param string $provider
79
     * @return string
80
     * @throws \Ffcms\Core\Exception\NativeException
81
     * @throws ForbiddenException
82
     * @throws SyntaxException
83
     */
84
    public function actionSocialauth($provider)
85
    {
86
        // get hybridauth instance
87
        /** @var \Hybrid_Auth $instance */
88
        $instance = App::$User->getOpenidInstance();
89
        if ($instance === null) {
90
            throw new ForbiddenException(__('OpenID auth is disabled'));
91
        }
92
93
        // try to get user identity data from remove service
94
        $userIdentity = null;
95
        try {
96
            $adapter = $instance->authenticate($provider);
97
            $userIdentity = $adapter->getUserProfile();
98
        } catch (\Exception $e) {
99
            throw new SyntaxException(__('Authorization failed: %e%', ['e' => $e->getMessage()]));
100
        }
101
102
        // check if openid data provided
103
        if ($userIdentity === null || Str::likeEmpty($userIdentity->identifier)) {
104
            throw new ForbiddenException(__('User data not provided!'));
105
        }
106
107
        // initialize model and pass user identity
108
        $model = new FormSocialAuth($provider, $userIdentity);
0 ignored issues
show
Documentation introduced by
$provider is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
109
        // check if user is always registered
110
        if ($model->identityExists()) {
111
            $model->makeAuth();
112
            App::$Response->redirect('/');
113
            return null;
114
        }
115
        // its a new identify, check if finish register form is submited
116
        if ($model->send() && $model->validate()) {
117
            if ($model->tryRegister()) {
118
                // registration is completed, lets open new session
119
                $loginModel = new FormLogin();
120
                $loginModel->openSession($model->_userObject);
0 ignored issues
show
Bug introduced by
It seems like $model->_userObject can be null; however, openSession() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
121
                App::$Response->redirect('/'); // session is opened, refresh page
122
            } else { // something gonna wrong, lets notify user
123
                App::$Session->getFlashBag()->add('error', __('Login or email is always used on website'));
124
            }
125
        }
126
127
        // render output view
128
        return App::$View->render('social_signup', [
129
            'model' => $model
130
        ]);
131
    }
132
133
    /**
134
     * View register form and process submit action
135
     * @throws ForbiddenException
136
     * @throws \Ffcms\Core\Exception\NativeException
137
     * @throws \Ffcms\Core\Exception\SyntaxException
138
     */
139
    public function actionSignup()
140
    {
141
        if (App::$User->isAuth()) { // always auth? prevent any actions
142
            throw new ForbiddenException();
143
        }
144
145
        // load configs
146
        $configs = $this->getConfigs();
147
148
        // init register model
149
        $registerForm = new FormRegister($configs['captchaOnRegister'] === 1);
150
151
        // registration based on invite. Check conditions.
152
        if ($configs['registrationType'] === 0) {
153
            // get token and email
154
            $inviteToken = App::$Request->query->get('token');
155
            $inviteEmail = App::$Request->query->get('email');
156
            // data sounds like a invalid?
157
            if (Str::length($inviteToken) < 32 || !Str::isEmail($inviteEmail)) {
158
                throw new ForbiddenException(__('Registration allowed only if you have invite!'));
159
            }
160
            // remove oldest data
161
            Invite::clean();
162
            // try to find token
163
            $find = Invite::where('token', '=', $inviteToken)
164
                ->where('email', '=', $inviteEmail)->count();
165
166
            // token not foud? invalid invite key
167
            if ($find !== 1) {
168
                throw new ForbiddenException(__('Your invite token is invalid! Contact with administrator'));
169
            }
170
            // notify the invite token is accepted
171
            if (!$registerForm->send()) {
172
                App::$Session->getFlashBag()->add('success', __('Invite was accepted! Continue registration'));
173
            }
174
175
            // set email from token data
176
            $registerForm->email = $inviteEmail;
177
        }
178
179
        // if register data is send and valid
180
        if ($registerForm->send() && $registerForm->validate()) {
181
            $activation = $configs['registrationType'] === 1;
182
            if ($registerForm->tryRegister($activation)) {
183
                // initialize succes signup event
184
                App::$Event->run(static::EVENT_USER_REGISTER_SUCCESS, [
185
                   'model' => $registerForm
186
                ]);
187
                // if no activation is required - just open session and redirect user to main page
188
                if (!$activation) {
189
                    $loginModel = new FormLogin();
190
                    $loginModel->openSession($registerForm->_userObject);
0 ignored issues
show
Bug introduced by
It seems like $registerForm->_userObject can be null; however, openSession() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
191
                    App::$Response->redirect('/'); // session is opened, refresh page
192
                }
193
                // send notification of successful registering
194
                App::$Session->getFlashBag()->add('success', __('Your account is registered. You must confirm account via email'));
195
            } else {
196
                // init fail signup event
197
                App::$Event->run(static::EVENT_USER_REGISTER_FAIL, [
198
                   'model' => $registerForm
199
                ]);
200
                App::$Session->getFlashBag()->add('error', __('Login or email is always used on website'));
201
            }
202
        }
203
204
        // render view
205
        return App::$View->render('signup', [
206
            'model' => $registerForm->filter(),
207
            'config' => $configs,
208
            'useCaptcha' => $configs['captchaOnRegister'] === 1
209
        ]);
210
    }
211
212
    /**
213
     * Recovery form and recovery submit action
214
     * @param int|null $id
215
     * @param string|null $token
216
     * @return string
217
     * @throws \Ffcms\Core\Exception\NativeException
218
     * @throws ForbiddenException
219
     * @throws NotFoundException
220
     * @throws \Ffcms\Core\Exception\SyntaxException
221
     */
222
    public function actionRecovery($id = null, $token = null)
223
    {
224
        if (App::$User->isAuth()) { // always auth? prevent any actions
225
            throw new ForbiddenException();
226
        }
227
228
        // is recovery submit?
229
        if (Obj::isLikeInt($id) && Str::length($token) >= 64) {
230
            $rObject = UserRecovery::where('id', '=', $id)
231
                ->where('token', '=', $token)
232
                ->where('archive', '=', false);
233
            // check if recovery row exist
234
            if ($rObject->count() !== 1) {
235
                throw new NotFoundException('This recovery data is not found');
236
            }
237
238
            $rData = $rObject->first();
239
            // check if user with this "user_id" in recovery row exist
240
            $rUser = App::$User->identity($rData->user_id);
241
            if ($rUser === null) {
242
                throw new NotFoundException('User is not found');
243
            }
244
245
            // all is ok, lets set new pwd
246
            $rUser->password = $rData->password;
0 ignored issues
show
Bug introduced by
Accessing password on the interface Ffcms\Core\Interfaces\iUser suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
247
            $rUser->save();
248
249
            $rData->archive = true;
250
            $rData->save();
251
252
            // add notification
253
            App::$Session->getFlashBag()->add('success', __('Your account are successful recovered. We recommend you change password'));
254
255
            // lets open user session with recovered data
256
            $loginModel = new FormLogin();
257
            $loginModel->openSession($rUser);
258
            App::$Response->redirect('/'); // session is opened, refresh page
259
        }
260
261
        // lets work with recovery form data
262
        $model = new FormRecovery();
263
        if ($model->send()) {
264
            if ($model->validate()) {
265
                $model->make();
266
                App::$Session->getFlashBag()->add('success', __('We send to you email with instruction to recovery your account'));
267
            } else {
268
                App::$Session->getFlashBag()->add('error', __('Form validation is failed'));
269
            }
270
        }
271
272
        // render visual form content
273
        return App::$View->render('recovery', [
274
            'model' => $model->filter()
275
        ]);
276
    }
277
278
    /**
279
     * Make logout if user is signIn
280
     * @throws ForbiddenException
281
     */
282
    public function actionLogout()
283
    {
284
        if (!App::$User->isAuth()) { // not auth? what you wanna?
285
            throw new ForbiddenException();
286
        }
287
288
        // unset session data
289
        App::$Session->invalidate();
290
291
        // redirect to main
292
        App::$Response->redirect('/');
293
    }
294
295
    /**
296
     * Approve user profile via $email and $token params
297
     * @param string $email
298
     * @param string $token
299
     * @throws ForbiddenException
300
     */
301
    public function actionApprove($email, $token)
302
    {
303
        // sounds like a not valid token
304
        if (App::$User->isAuth() || Str::length($token) < 32 || !Str::isEmail($email)) {
305
            throw new ForbiddenException();
306
        }
307
        // lets find token&email
308
        $find = App::$User->where('approve_token', '=', $token)
309
            ->where('email', '=', $email);
310
311
        // not found? exit
312
        if ($find->count() !== 1) {
313
            throw new ForbiddenException();
314
        }
315
316
        // get row and update approve information
317
        $user = $find->first();
318
        $user->approve_token = '0';
319
        $user->save();
320
321
        // open session and redirect to main
322
        $loginModel = new FormLogin();
323
        $loginModel->openSession($user);
324
        App::$Response->redirect('/'); // session is opened, refresh page
325
    }
326
}