• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lonnieezell / myth-auth / 3798654288

pending completion
3798654288

Pull #584

github

GitHub
Merge db6ea1b4f into da3522738
Pull Request #584: fixes bug with Group Permissions and User cache

829 of 2294 relevant lines covered (36.14%)

8.52 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

25.31
/src/Controllers/AuthController.php
1
<?php
2

3
namespace Myth\Auth\Controllers;
4

5
use CodeIgniter\Controller;
6
use CodeIgniter\HTTP\CLIRequest;
7
use CodeIgniter\HTTP\IncomingRequest;
8
use CodeIgniter\HTTP\RedirectResponse;
9
use CodeIgniter\Session\Session;
10
use Myth\Auth\Config\Auth as AuthConfig;
11
use Myth\Auth\Entities\User;
12
use Myth\Auth\Models\UserModel;
13

14
class AuthController extends Controller
15
{
16
    /**
17
     * Analysis assist; remove after CodeIgniter 4.3 release.
18
     *
19
     * @var CLIRequest|IncomingRequest
20
     */
21
    protected $request;
22

23
    protected $auth;
24

25
    /**
26
     * @var AuthConfig
27
     */
28
    protected $config;
29

30
    /**
31
     * @var Session
32
     */
33
    protected $session;
34

35
    /**
36
     * Constructor
37
     */
38
    public function __construct()
39
    {
40
        // Most services in this controller require
41
        // the session to be started - so fire it up!
42
        $this->session = service('session');
9✔
43

44
        $this->config = config('Auth');
9✔
45
        $this->auth   = service('authentication');
9✔
46
    }
47

48
    // --------------------------------------------------------------------
49
    // Login/out
50
    // --------------------------------------------------------------------
51
    /**
52
     * Displays the login form, or redirects
53
     * the user to their destination/home if
54
     * they are already logged in.
55
     *
56
     * @return RedirectResponse|string
57
     */
58
    public function login()
59
    {
60
        // No need to show a login form if the user
61
        // is already logged in.
62
        if ($this->auth->check()) {
1✔
63
            $redirectURL = session('redirect_url') ?? site_url($this->config->landingRoute);
×
64
            unset($_SESSION['redirect_url']);
×
65

66
            return redirect()
×
67
                ->to($redirectURL);
×
68
        }
69

70
        // Set a return URL if none is specified.
71
        $_SESSION['redirect_url'] = session('redirect_url') ?? previous_url();
1✔
72

73
        // Display the login view.
74
        return $this->_render($this->config->views['login'], ['config' => $this->config]);
1✔
75
    }
76

77
    /**
78
     * Attempts to verify the user's credentials
79
     * through a POST request.
80
     *
81
     * @return RedirectResponse
82
     */
83
    public function attemptLogin()
84
    {
85
        $rules = [
3✔
86
            'login'    => 'required',
3✔
87
            'password' => 'required',
3✔
88
        ];
3✔
89
        if ($this->config->validFields === ['email']) {
3✔
90
            $rules['login'] .= '|valid_email';
×
91
        }
92

93
        if (! $this->validate($rules)) {
3✔
94
            return redirect()
1✔
95
                ->back()
1✔
96
                ->withInput()
1✔
97
                ->with('errors', $this->validator->getErrors());
1✔
98
        }
99

100
        $login    = $this->request->getPost('login');
2✔
101
        $password = $this->request->getPost('password');
2✔
102
        $remember = (bool) $this->request->getPost('remember');
2✔
103

104
        // Determine credential type
105
        $type = filter_var($login, FILTER_VALIDATE_EMAIL) ? 'email' : 'username';
2✔
106

107
        // Try to log them in...
108
        if (! $this->auth->attempt([$type => $login, 'password' => $password], $remember)) {
2✔
109
            return redirect()
×
110
                ->back()
×
111
                ->withInput()
×
112
                ->with('error', $this->auth->error() ?? lang('Auth.badAttempt'));
×
113
        }
114

115
        // Is the user being forced to reset their password?
116
        if ($this->auth->user()->force_pass_reset === true) {
2✔
117
            $url = route_to('reset-password') . '?token=' . $this->auth->user()->reset_hash;
×
118

119
            return redirect()
×
120
                ->to($url)
×
121
                ->withCookies();
×
122
        }
123

124
        $redirectURL = session('redirect_url') ?? site_url($this->config->landingRoute);
2✔
125
        unset($_SESSION['redirect_url']);
2✔
126

127
        return redirect()
2✔
128
            ->to($redirectURL)
2✔
129
            ->withCookies()
2✔
130
            ->with('message', lang('Auth.loginSuccess'));
2✔
131
    }
132

133
    /**
134
     * Log the user out.
135
     *
136
     * @return RedirectResponse
137
     */
138
    public function logout()
139
    {
140
        if ($this->auth->check()) {
×
141
            $this->auth->logout();
×
142
        }
143

144
        return redirect()->to(site_url('/'));
×
145
    }
146

147
    // --------------------------------------------------------------------
148
    // Register
149
    // --------------------------------------------------------------------
150
    /**
151
     * Displays the user registration page.
152
     *
153
     * @return RedirectResponse|string
154
     */
155
    public function register()
156
    {
157
        // check if already logged in.
158
        if ($this->auth->check()) {
1✔
159
            return redirect()->back();
×
160
        }
161

162
        // Check if registration is allowed
163
        if (! $this->config->allowRegistration) {
1✔
164
            return redirect()
×
165
                ->back()
×
166
                ->withInput()
×
167
                ->with('error', lang('Auth.registerDisabled'));
×
168
        }
169

170
        return $this->_render($this->config->views['register'], ['config' => $this->config]);
1✔
171
    }
172

173
    /**
174
     * Attempt to register a new user.
175
     *
176
     * @return RedirectResponse
177
     */
178
    public function attemptRegister()
179
    {
180
        // Check if registration is allowed
181
        if (! $this->config->allowRegistration) {
4✔
182
            return redirect()
1✔
183
                ->back()
1✔
184
                ->withInput()
1✔
185
                ->with('error', lang('Auth.registerDisabled'));
1✔
186
        }
187

188
        $users = model(UserModel::class);
3✔
189

190
        // Validate basics first since some password rules rely on these fields
191
        $rules = config('Validation')->registrationRules ?? [
3✔
192
            'username' => 'required|alpha_numeric_space|min_length[3]|max_length[30]|is_unique[users.username]',
3✔
193
            'email'    => 'required|valid_email|is_unique[users.email]',
3✔
194
        ];
195

196
        if (! $this->validate($rules)) {
3✔
197
            return redirect()
1✔
198
                ->back()
1✔
199
                ->withInput()
1✔
200
                ->with('errors', $this->validator->getErrors());
1✔
201
        }
202

203
        // Validate passwords since they can only be validated properly here
204
        $rules = [
2✔
205
            'password'     => 'required|strong_password',
2✔
206
            'pass_confirm' => 'required|matches[password]',
2✔
207
        ];
2✔
208

209
        if (! $this->validate($rules)) {
2✔
210
            return redirect()
×
211
                ->back()
×
212
                ->withInput()
×
213
                ->with('errors', $this->validator->getErrors());
×
214
        }
215

216
        // Save the user
217
        $allowedPostFields = array_merge(['password'], $this->config->validFields, $this->config->personalFields);
2✔
218
        $user              = new User($this->request->getPost($allowedPostFields));
2✔
219

220
        $this->config->requireActivation === null ? $user->activate() : $user->generateActivateHash();
2✔
221

222
        // Ensure default group gets assigned if set
223
        if (! empty($this->config->defaultUserGroup)) {
2✔
224
            $users = $users->withGroup($this->config->defaultUserGroup);
1✔
225
        }
226

227
        if (! $users->save($user)) {
2✔
228
            return redirect()
×
229
                ->back()
×
230
                ->withInput()
×
231
                ->with('errors', $users->errors());
×
232
        }
233

234
        if ($this->config->requireActivation !== null) {
2✔
235
            $activator = service('activator');
×
236
            $sent      = $activator->send($user);
×
237

238
            if (! $sent) {
×
239
                return redirect()
×
240
                    ->back()
×
241
                    ->withInput()
×
242
                    ->with('error', $activator->error() ?? lang('Auth.unknownError'));
×
243
            }
244

245
            // Success!
246
            return redirect()
×
247
                ->route('login')
×
248
                ->with('message', lang('Auth.activationSuccess'));
×
249
        }
250

251
        // Success!
252
        return redirect()
2✔
253
            ->route('login')
2✔
254
            ->with('message', lang('Auth.registerSuccess'));
2✔
255
    }
256

257
    // --------------------------------------------------------------------
258
    // Forgot Password
259
    // --------------------------------------------------------------------
260
    /**
261
     * Displays the forgot password form.
262
     *
263
     * @return RedirectResponse|string
264
     */
265
    public function forgotPassword()
266
    {
267
        if ($this->config->activeResetter === null) {
×
268
            return redirect()
×
269
                ->route('login')
×
270
                ->with('error', lang('Auth.forgotDisabled'));
×
271
        }
272

273
        return $this->_render($this->config->views['forgot'], ['config' => $this->config]);
×
274
    }
275

276
    /**
277
     * Attempts to find a user account with that password
278
     * and send password reset instructions to them.
279
     *
280
     * @return RedirectResponse
281
     */
282
    public function attemptForgot()
283
    {
284
        if ($this->config->activeResetter === null) {
×
285
            return redirect()
×
286
                ->route('login')
×
287
                ->with('error', lang('Auth.forgotDisabled'));
×
288
        }
289

290
        $rules = [
×
291
            'email' => [
×
292
                'label' => lang('Auth.emailAddress'),
×
293
                'rules' => 'required|valid_email',
×
294
            ],
×
295
        ];
×
296

297
        if (! $this->validate($rules)) {
×
298
            return redirect()
×
299
                ->back()
×
300
                ->withInput()
×
301
                ->with('errors', $this->validator->getErrors());
×
302
        }
303

304
        $users = model(UserModel::class);
×
305

306
        $user = $users->where('email', $this->request->getPost('email'))->first();
×
307

308
        if (null === $user) {
×
309
            return redirect()
×
310
                ->back()
×
311
                ->with('error', lang('Auth.forgotNoUser'));
×
312
        }
313

314
        // Save the reset hash /
315
        $user->generateResetHash();
×
316
        $users->save($user);
×
317

318
        $resetter = service('resetter');
×
319
        $sent     = $resetter->send($user);
×
320

321
        if (! $sent) {
×
322
            return redirect()
×
323
                ->back()
×
324
                ->withInput()
×
325
                ->with('error', $resetter->error() ?? lang('Auth.unknownError'));
×
326
        }
327

328
        return redirect()
×
329
            ->route('reset-password')
×
330
            ->with('message', lang('Auth.forgotEmailSent'));
×
331
    }
332

333
    /**
334
     * Displays the Reset Password form.
335
     *
336
     * @return RedirectResponse|string
337
     */
338
    public function resetPassword()
339
    {
340
        if ($this->config->activeResetter === null) {
×
341
            return redirect()
×
342
                ->route('login')
×
343
                ->with('error', lang('Auth.forgotDisabled'));
×
344
        }
345

346
        $token = $this->request->getGet('token');
×
347

348
        return $this->_render($this->config->views['reset'], [
×
349
            'config' => $this->config,
×
350
            'token'  => $token,
×
351
        ]);
×
352
    }
353

354
    /**
355
     * Verifies the code with the email and saves the new password,
356
     * if they all pass validation.
357
     *
358
     * @return RedirectResponse
359
     */
360
    public function attemptReset()
361
    {
362
        if ($this->config->activeResetter === null) {
×
363
            return redirect()
×
364
                ->route('login')
×
365
                ->with('error', lang('Auth.forgotDisabled'));
×
366
        }
367

368
        $users = model(UserModel::class);
×
369

370
        // First things first - log the reset attempt.
371
        $users->logResetAttempt(
×
372
            $this->request->getPost('email'),
×
373
            $this->request->getPost('token'),
×
374
            $this->request->getIPAddress(),
×
375
            (string) $this->request->getUserAgent()
×
376
        );
×
377

378
        $rules = [
×
379
            'token'        => 'required',
×
380
            'email'        => 'required|valid_email',
×
381
            'password'     => 'required|strong_password',
×
382
            'pass_confirm' => 'required|matches[password]',
×
383
        ];
×
384

385
        if (! $this->validate($rules)) {
×
386
            return redirect()
×
387
                ->back()
×
388
                ->withInput()
×
389
                ->with('errors', $this->validator->getErrors());
×
390
        }
391

392
        $user = $users->where('email', $this->request->getPost('email'))
×
393
            ->where('reset_hash', $this->request->getPost('token'))
×
394
            ->first();
×
395

396
        if (null === $user) {
×
397
            return redirect()
×
398
                ->back()
×
399
                ->with('error', lang('Auth.forgotNoUser'));
×
400
        }
401

402
        // Reset token still valid?
403
        if (! empty($user->reset_expires) && time() > $user->reset_expires->getTimestamp()) {
×
404
            return redirect()
×
405
                ->back()
×
406
                ->withInput()
×
407
                ->with('error', lang('Auth.resetTokenExpired'));
×
408
        }
409

410
        // Success! Save the new password, and cleanup the reset hash.
411
        $user->password         = $this->request->getPost('password');
×
412
        $user->reset_hash       = null;
×
413
        $user->reset_at         = date('Y-m-d H:i:s');
×
414
        $user->reset_expires    = null;
×
415
        $user->force_pass_reset = false;
×
416
        $users->save($user);
×
417

418
        return redirect()
×
419
            ->route('login')
×
420
            ->with('message', lang('Auth.resetSuccess'));
×
421
    }
422

423
    /**
424
     * Activate account.
425
     *
426
     * @return mixed
427
     */
428
    public function activateAccount()
429
    {
430
        $users = model(UserModel::class);
×
431

432
        // First things first - log the activation attempt.
433
        $users->logActivationAttempt(
×
434
            $this->request->getGet('token'),
×
435
            $this->request->getIPAddress(),
×
436
            (string) $this->request->getUserAgent()
×
437
        );
×
438

439
        $throttler = service('throttler');
×
440

441
        if ($throttler->check(md5($this->request->getIPAddress()), 2, MINUTE) === false) {
×
442
            return service('response')
×
443
                ->setStatusCode(429)
×
444
                ->setBody(lang('Auth.tooManyRequests', [$throttler->getTokentime()]));
×
445
        }
446

447
        $user = $users->where('activate_hash', $this->request->getGet('token'))
×
448
            ->where('active', 0)
×
449
            ->first();
×
450

451
        if (null === $user) {
×
452
            return redirect()
×
453
                ->route('login')
×
454
                ->with('error', lang('Auth.activationNoUser'));
×
455
        }
456

457
        $user->activate();
×
458

459
        $users->save($user);
×
460

461
        return redirect()
×
462
            ->route('login')
×
463
            ->with('message', lang('Auth.registerSuccess'));
×
464
    }
465

466
    /**
467
     * Resend activation account.
468
     *
469
     * @return mixed
470
     */
471
    public function resendActivateAccount()
472
    {
473
        if ($this->config->requireActivation === null) {
×
474
            return redirect()
×
475
                ->route('login');
×
476
        }
477

478
        $throttler = service('throttler');
×
479

480
        if ($throttler->check(md5($this->request->getIPAddress()), 2, MINUTE) === false) {
×
481
            return service('response')
×
482
                ->setStatusCode(429)
×
483
                ->setBody(lang('Auth.tooManyRequests', [$throttler->getTokentime()]));
×
484
        }
485

486
        $login = urldecode($this->request->getGet('login'));
×
487
        $type  = filter_var($login, FILTER_VALIDATE_EMAIL) ? 'email' : 'username';
×
488

489
        $users = model(UserModel::class);
×
490

491
        $user = $users->where($type, $login)
×
492
            ->where('active', 0)
×
493
            ->first();
×
494

495
        if (null === $user) {
×
496
            return redirect()
×
497
                ->route('login')
×
498
                ->with('error', lang('Auth.activationNoUser'));
×
499
        }
500

501
        $activator = service('activator');
×
502
        $sent      = $activator->send($user);
×
503

504
        if (! $sent) {
×
505
            return redirect()
×
506
                ->back()
×
507
                ->withInput()
×
508
                ->with('error', $activator->error() ?? lang('Auth.unknownError'));
×
509
        }
510

511
        // Success!
512
        return redirect()
×
513
            ->route('login')
×
514
            ->with('message', lang('Auth.activationSuccess'));
×
515
    }
516

517
    /**
518
     * Render the view.
519
     *
520
     * @return string
521
     */
522
    protected function _render(string $view, array $data = [])
523
    {
524
        return view($view, $data);
2✔
525
    }
526
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc