LoginHandler::lockoutMember()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 5
nc 1
nop 2
dl 0
loc 7
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Firesphere\HaveIBeenPwned\Controllers;
4
5
use Firesphere\HaveIBeenPwned\Extensions\MemberExtension;
6
use Firesphere\HaveIBeenPwned\Models\HaveIBeenPwnedPage;
7
use Firesphere\HaveIBeenPwned\Services\HaveIBeenPwnedService;
8
use GuzzleHttp\Exception\GuzzleException;
9
use SilverStripe\Control\HTTPRequest;
10
use SilverStripe\Control\HTTPResponse;
11
use SilverStripe\Core\Injector\Injector;
12
use SilverStripe\ORM\ValidationException;
13
use SilverStripe\ORM\ValidationResult;
14
use SilverStripe\Security\Authenticator;
15
use SilverStripe\Security\DefaultAdminService;
16
use SilverStripe\Security\Member;
17
use SilverStripe\Security\MemberAuthenticator\LoginHandler as BaseLoginHandler;
18
use SilverStripe\Security\MemberAuthenticator\LostPasswordForm;
19
use SilverStripe\Security\MemberAuthenticator\MemberAuthenticator;
20
use SilverStripe\Security\MemberAuthenticator\MemberLoginForm;
21
use SilverStripe\Security\Security;
22
23
/**
24
 * Class LoginHandler
25
 * @package Firesphere\HaveIBeenPwned\Controllers
26
 */
27
class LoginHandler extends BaseLoginHandler
28
{
29
30
    /**
31
     * @var HaveIBeenPwnedService
32
     */
33
    protected $service;
34
35
36
    /**
37
     * LoginHandler constructor.
38
     *
39
     * @param string $link
40
     * @param MemberAuthenticator $authenticator
41
     */
42
    public function __construct($link, MemberAuthenticator $authenticator)
43
    {
44
        /** @var HaveIBeenPwnedService service */
45
        $this->service = Injector::inst()->get(HaveIBeenPwnedService::class);
46
47
        parent::__construct($link, $authenticator);
48
    }
49
50
    /**
51
     * @param $data
52
     * @param MemberLoginForm $form
53
     * @param HTTPRequest $request
54
     * @return HTTPResponse
55
     * @throws GuzzleException
56
     * @throws ValidationException
57
     */
58
    public function doLogin($data, MemberLoginForm $form, HTTPRequest $request)
59
    {
60
        $isDefaultAdmin = DefaultAdminService::isDefaultAdminCredentials($data['Email'], $data['Password']);
61
        /**
62
         * @var Member|MemberExtension $member
63
         * @var ValidationResult $result
64
         */
65
        list($member, $pwnedPasswordCount) = $this->validateMember($data, $request, $result);
66
67
        // Also, exclude default admin from forcing a reset
68
        if (!$isDefaultAdmin &&
69
            $pwnedPasswordCount &&
70
            !HaveIBeenPwnedService::config()->get('allow_pwnd')
71
        ) {
72
            if ($member !== null) {
73
                $this->lockoutMember($member, $pwnedPasswordCount);
74
            }
75
            // A breached member or unknown member get the reset form
76
            // It's doing both, because otherwise we'd leak data about members being registered
77
            return $this->redirectToResetPassword();
78
        }
79
80
        // The result is invalid or valid, we don't care, go to the parent
81
        return parent::doLogin($data, $form, $request);
82
    }
83
84
    /**
85
     * @param Member|MemberExtension $member
86
     * @param Int $breachCount
87
     * @throws ValidationException
88
     */
89
    protected function lockoutMember($member, $breachCount)
90
    {
91
        $member->PasswordIsPwnd = $breachCount;
92
        $member->AutoLoginHash = null;
0 ignored issues
show
Bug introduced by
The property AutoLoginHash does not seem to exist on Firesphere\HaveIBeenPwne...ensions\MemberExtension.
Loading history...
93
        $member->PasswordExpiry = '1970-01-01 00:00:00'; // To the beginning of Unixtime it is
0 ignored issues
show
Bug introduced by
The property PasswordExpiry does not seem to exist on Firesphere\HaveIBeenPwne...ensions\MemberExtension.
Loading history...
94
        $member->Password = null; // And reset the password to prevent any visitor to log in again
0 ignored issues
show
Bug introduced by
The property Password does not exist on Firesphere\HaveIBeenPwne...ensions\MemberExtension. Did you mean PasswordIsPwnd?
Loading history...
95
        $member->write();
0 ignored issues
show
Bug introduced by
The method write() does not exist on Firesphere\HaveIBeenPwne...ensions\MemberExtension. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

95
        $member->/** @scrutinizer ignore-call */ 
96
                 write();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
96
    }
97
98
    /**
99
     * Invoked if password is expired and must be changed
100
     *
101
     * @skipUpgrade
102
     * @return HTTPResponse
103
     */
104
    protected function redirectToResetPassword()
105
    {
106
        $lostPasswordForm = LostPasswordForm::create($this, Authenticator::class, 'lostPasswordForm');
107
108
        /** @var HaveIBeenPwnedPage|null $pwndPage */
109
        $pwndPage = HaveIBeenPwnedPage::get()->first();
110
        $lostPasswordForm->sessionMessage(
111
            _t(
112
                self::class . '.PASSWORDEXPIREDORBREACHED',
113
                'Because of security concerns with the password you entered, you need to reset your password. 
114
                Do not worry, your account has not been compromised, this is just a precaution'
115
            ),
116
            'warning'
117
        );
118
119
        if ($pwndPage !== null) {
120
            $lostPasswordForm->sessionMessage(
121
                _t(
122
                    self::class . '.PASSWORDEXPIRYREASON',
123
                    '<a href="{link}">You can read more here</a>',
124
                    ['link' => $pwndPage->Link()]
125
                ),
126
                'good'
127
            );
128
        }
129
        $resetPasswordLink = Security::singleton()->Link('lostpassword');
130
131
        return $this->redirect($resetPasswordLink);
132
    }
133
134
    /**
135
     * @return HaveIBeenPwnedService
136
     */
137
    public function getService()
138
    {
139
        return $this->service;
140
    }
141
142
    /**
143
     * @param HaveIBeenPwnedService $service
144
     * @return LoginHandler
145
     */
146
    public function setService($service)
147
    {
148
        $this->service = $service;
149
150
        return $this;
151
    }
152
153
    /**
154
     * @param array $data
155
     * @param HTTPRequest $request
156
     * @param ValidationResult|null $result
157
     * @return array
158
     * @throws GuzzleException
159
     */
160
    protected function validateMember($data, HTTPRequest $request, &$result)
161
    {
162
        /** @var Member|MemberExtension $member */
163
        $member = $this->checkLogin($data, $request, $result);
164
        $password = $data['Password'];
165
        // How often can we find this password?
166
        $pwnedPasswordCount = $this->service->checkPwnedPassword($password);
167
168
        if ($member && $result->isValid()) {
169
            $member->PasswordIsPwnd = $pwnedPasswordCount;
170
        }
171
172
        return [$member, $pwnedPasswordCount];
173
    }
174
}
175