Completed
Push — master ( ec2395...0dfd17 )
by Sherif
02:09
created

UserRepository   C

Complexity

Total Complexity 55

Size/Duplication

Total Lines 390
Duplicated Lines 2.56 %

Coupling/Cohesion

Components 2
Dependencies 3

Importance

Changes 0
Metric Value
wmc 55
lcom 2
cbo 3
dl 10
loc 390
rs 6
c 0
b 0
f 0

19 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A account() 0 13 2
A can() 0 11 2
A hasGroup() 0 5 3
A assignGroups() 10 10 1
B login() 0 16 9
A loginSocial() 0 16 4
A register() 0 13 3
A block() 0 18 5
A unblock() 0 12 2
A sendReset() 0 9 2
A resetPassword() 0 27 5
A changePassword() 0 10 2
A confirmEmail() 0 10 2
A sendConfirmationEmail() 0 12 2
A group() 0 22 4
A saveProfile() 0 9 2
A accessTokenExpiredOrRevoked() 0 13 3
A revokeAccessToken() 0 10 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like UserRepository often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use UserRepository, and based on these observations, apply Extract Interface, too.

1
<?php namespace App\Modules\Users\Repositories;
2
3
use App\Modules\Core\BaseClasses\BaseRepository;
4
use Illuminate\Support\Arr;
5
use App\Modules\Users\AclUser;
6
7
class UserRepository extends BaseRepository
8
{
9
    /**
10
     * Init new object.
11
     *
12
     * @param   AclUser $model
13
     * @return  void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
14
     */
15
    public function __construct(AclUser $model)
16
    {
17
        parent::__construct($model);
18
    }
19
20
    /**
21
     * Return the logged in user account.
22
     *
23
     * @param  array   $relations
24
     * @return boolean
25
     */
26
    public function account($relations = [])
27
    {
28
        $permissions = [];
29
        $user        = $this->find(\Auth::id(), $relations);
30
        foreach ($user->groups()->get() as $group) {
31
            $group->permissions->each(function ($permission) use (&$permissions) {
32
                $permissions[$permission->model][$permission->id] = $permission->name;
33
            });
34
        }
35
        $user->permissions = $permissions;
36
37
        return $user;
38
    }
39
40
    /**
41
     * Check if the logged in user or the given user
42
     * has the given permissions on the given model.
43
     *
44
     * @param  string $nameOfPermission
45
     * @param  string $model
46
     * @param  mixed  $user
47
     * @return boolean
48
     */
49
    public function can($nameOfPermission, $model, $user = false)
50
    {
51
        $user        = $user ?: $this->find(\Auth::id(), ['groups.permissions']);
52
        $permissions = [];
53
54
        $user->groups->pluck('permissions')->each(function ($permission) use (&$permissions, $model) {
55
            $permissions = array_merge($permissions, $permission->where('model', $model)->pluck('name')->toArray());
56
        });
57
58
        return in_array($nameOfPermission, $permissions);
59
    }
60
61
    /**
62
     * Check if the logged in user has the given group.
63
     *
64
     * @param  string[] $groups
65
     * @param  mixed $user
66
     * @return boolean
67
     */
68
    public function hasGroup($groups, $user = false)
69
    {
70
        $user = $user ?: $this->find(\Auth::id());
71
        return $user->groups->whereIn('name', $groups)->count() ? true : false;
72
    }
73
74
    /**
75
     * Assign the given group ids to the given user.
76
     *
77
     * @param  integer $userId
78
     * @param  array   $groupIds
79
     * @return object
80
     */
81 View Code Duplication
    public function assignGroups($userId, $groupIds)
0 ignored issues
show
Duplication introduced by
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.

Loading history...
82
    {
83
        \DB::transaction(function () use ($userId, $groupIds) {
84
            $user = $this->find($userId);
85
            $user->groups()->detach();
86
            $user->groups()->attach($groupIds);
87
        });
88
89
        return $this->find($userId);
90
    }
91
92
93
    /**
94
     * Handle a login request to the application.
95
     *
96
     * @param  array   $credentials
97
     * @param  boolean $adminLogin
98
     * @return object
99
     */
100
    public function login($credentials, $adminLogin = false)
101
    {
102
        if (! $user = $this->first(['email' => $credentials['email']])) {
103
            \ErrorHandler::loginFailed();
104
        } elseif ($adminLogin && ! $user->groups->whereIn('name', ['Admin'])->count()) {
105
            \ErrorHandler::loginFailed();
106
        } elseif (! $adminLogin && $user->groups->whereIn('name', ['Admin'])->count()) {
107
            \ErrorHandler::loginFailed();
108
        } elseif ($user->blocked) {
109
            \ErrorHandler::userIsBlocked();
110
        } elseif (! config('skeleton.disable_confirm_email') && ! $user->confirmed) {
111
            \ErrorHandler::emailNotConfirmed();
112
        }
113
114
        return $user;
115
    }
116
117
    /**
118
     * Handle a social login request of the none admin to the application.
119
     *
120
     * @param  string $authCode
121
     * @param  string $accessToken
122
     * @param  string $type
123
     * @return array
124
     */
125
    public function loginSocial($authCode, $accessToken, $type)
126
    {
127
        $access_token = $authCode ? Arr::get(\Socialite::driver($type)->getAccessTokenResponse($authCode), 'access_token') : $accessToken;
128
        $user         = \Socialite::driver($type)->userFromToken($access_token);
129
130
        if (! $user->email) {
131
            \ErrorHandler::noSocialEmail();
132
        }
133
134
        if (! $this->model->where('email', $user->email)->first()) {
135
            $this->register(['email' => $user->email, 'password' => ''], true);
136
        }
137
138
        $loginProxy = \App::make('App\Modules\Users\Proxy\LoginProxy');
139
        return $loginProxy->login(['email' => $user->email, 'password' => config('skeleton.social_pass')], 0);
140
    }
141
    
142
    /**
143
     * Handle a registration request.
144
     *
145
     * @param  array   $credentials
146
     * @param  boolean $skipConfirmEmail
147
     * @return array
148
     */
149
    public function register($credentials, $skipConfirmEmail = false)
150
    {
151
        $user = $this->save($credentials);
152
153
        if ($skipConfirmEmail) {
154
            $user->confirmed = 1;
155
            $user->save();
0 ignored issues
show
Bug introduced by
The method save cannot be called on $user (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
156
        } elseif (! config('skeleton.disable_confirm_email')) {
157
            $this->sendConfirmationEmail($user->email);
158
        }
159
160
        return $user;
161
    }
162
    
163
    /**
164
     * Block the user.
165
     *
166
     * @param  integer $userId
167
     * @return object
168
     */
169
    public function block($userId)
170
    {
171
        if (! $user = $this->find($userId)) {
172
            \ErrorHandler::notFound('user');
173
        }
174
        if (! $this->hasGroup(['Admin'])) {
175
            \ErrorHandler::noPermissions();
176
        } elseif (\Auth::id() == $userId) {
177
            \ErrorHandler::noPermissions();
178
        } elseif ($user->groups->pluck('name')->search('Admin', true) !== false) {
179
            \ErrorHandler::noPermissions();
180
        }
181
182
        $user->blocked = 1;
183
        $user->save();
184
        
185
        return $user;
186
    }
187
188
    /**
189
     * Unblock the user.
190
     *
191
     * @param  integer $userId
192
     * @return object
193
     */
194
    public function unblock($userId)
195
    {
196
        if (! $this->hasGroup(['Admin'])) {
197
            \ErrorHandler::noPermissions();
198
        }
199
200
        $user          = $this->find($userId);
201
        $user->blocked = 0;
202
        $user->save();
203
204
        return $user;
205
    }
206
207
    /**
208
     * Send a reset link to the given user.
209
     *
210
     * @param  string  $email
211
     * @return void
212
     */
213
    public function sendReset($email)
214
    {
215
        if (! $user = $this->model->where('email', $email)->first()) {
216
            \ErrorHandler::notFound('email');
217
        }
218
219
        $token = \Password::getRepository()->create($user);
220
        \Core::notifications()->notify($user, 'ResetPassword', $token);
221
    }
222
223
    /**
224
     * Reset the given user's password.
225
     *
226
     * @param  array  $credentials
227
     * @return string|null
228
     */
229
    public function resetPassword($credentials)
230
    {
231
        $response = \Password::reset($credentials, function ($user, $password) {
232
            $user->password = $password;
233
            $user->save();
234
        });
235
236
        switch ($response) {
237
            case \Password::PASSWORD_RESET:
238
                return 'success';
239
240
            case \Password::INVALID_TOKEN:
241
                \ErrorHandler::invalidResetToken('token');
242
                //no break
243
244
            case \Password::INVALID_PASSWORD:
245
                \ErrorHandler::invalidResetPassword('email');
246
                //no break
247
248
            case \Password::INVALID_USER:
249
                \ErrorHandler::notFound('user');
250
                //no break
251
252
            default:
253
                \ErrorHandler::generalError();
254
        }
255
    }
256
257
    /**
258
     * Change the logged in user password.
259
     *
260
     * @param  array  $credentials
261
     * @return void
262
     */
263
    public function changePassword($credentials)
264
    {
265
        $user = \Auth::user();
266
        if (! \Hash::check($credentials['old_password'], $user->password)) {
267
            \ErrorHandler::invalidOldPassword();
268
        }
269
270
        $user->password = $credentials['password'];
271
        $user->save();
272
    }
273
274
    /**
275
     * Confirm email using the confirmation code.
276
     *
277
     * @param  string $confirmationCode
278
     * @return void
279
     */
280
    public function confirmEmail($confirmationCode)
281
    {
282
        if (! $user = $this->first(['confirmation_code' => $confirmationCode])) {
283
            \ErrorHandler::invalidConfirmationCode();
284
        }
285
286
        $user->confirmed         = 1;
287
        $user->confirmation_code = null;
288
        $user->save();
289
    }
290
291
    /**
292
     * Send the confirmation mail.
293
     *
294
     * @param  string $email
295
     * @return void
296
     */
297
    public function sendConfirmationEmail($email)
298
    {
299
        $user = $this->first(['email' => $email]);
300
        if ($user->confirmed) {
301
            \ErrorHandler::emailAlreadyConfirmed();
302
        }
303
304
        $user->confirmed         = 0;
305
        $user->confirmation_code = sha1(microtime());
306
        $user->save();
307
        \Core::notifications()->notify($user, 'ConfirmEmail');
308
    }
309
310
    /**
311
     * Paginate all users in the given group based on the given conditions.
312
     *
313
     * @param  string  $groupName
314
     * @param  array   $relations
315
     * @param  integer $perPage
316
     * @param  string  $sortBy
317
     * @param  boolean $desc
318
     * @return \Illuminate\Http\Response
319
     */
320
    public function group($conditions, $groupName, $relations, $perPage, $sortBy, $desc)
321
    {
322
        unset($conditions['page']);
323
        $conditions = $this->constructConditions($conditions, $this->model);
324
        $sort       = $desc ? 'desc' : 'asc';
325
        $model      = $this->model->with($relations);
326
327
        $model->whereHas('groups', function ($q) use ($groupName) {
328
            $q->where('name', $groupName);
329
        });
330
331
        
332
        if (count($conditions['conditionValues'])) {
333
            $model->whereRaw($conditions['conditionString'], $conditions['conditionValues']);
334
        }
335
336
        if ($perPage) {
337
            return $model->orderBy($sortBy, $sort)->paginate($perPage);
338
        }
339
340
        return $model->orderBy($sortBy, $sort)->get();
341
    }
342
343
    /**
344
     * Save the given data to the logged in user.
345
     *
346
     * @param  array $data
347
     * @return void
348
     */
349
    public function saveProfile($data)
350
    {
351
        if (Arr::has($data, 'profile_picture')) {
352
            $data['profile_picture'] = \Media::uploadImageBas64($data['profile_picture'], 'admins/profile_pictures');
353
        }
354
        
355
        $data['id'] = \Auth::id();
356
        return $this->save($data);
357
    }
358
359
    /**
360
     * Ensure access token hasn't expired or revoked.
361
     *
362
     * @param  string $accessToken
363
     * @return boolean
364
     */
365
    public function accessTokenExpiredOrRevoked($accessToken)
366
    {
367
        $accessTokenId = json_decode($accessToken, true)['id'];
368
        $accessToken   = \DB::table('oauth_access_tokens')
369
                ->where('id', $accessTokenId)
370
                ->first();
371
        
372
        if (\Carbon\Carbon::parse($accessToken->expires_at)->isPast() || $accessToken->revoked) {
373
            return true;
374
        }
375
376
        return false;
377
    }
378
379
    /**
380
     * Revoke the given access token and all
381
     * associated refresh tokens.
382
     *
383
     * @param  oject $accessToken
384
     * @return void
385
     */
386
    public function revokeAccessToken($accessToken)
387
    {
388
        \DB::table('oauth_refresh_tokens')
389
            ->where('access_token_id', $accessToken->id)
390
            ->update([
391
                'revoked' => true
392
            ]);
393
394
        $accessToken->revoke();
395
    }
396
}
397