Completed
Push — authenticator-refactor ( 7dc887...371abb )
by Simon
06:49
created

MemberAuthenticator::authenticateMember()   C

Complexity

Conditions 12
Paths 66

Size

Total Lines 58
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
eloc 32
nc 66
nop 3
dl 0
loc 58
rs 6.5331
c 0
b 0
f 0

How to fix   Long Method    Complexity   

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 SilverStripe\Security\MemberAuthenticator;
4
5
use SilverStripe\Control\Controller;
6
use SilverStripe\Control\Session;
7
use SilverStripe\ORM\ValidationResult;
8
use InvalidArgumentException;
9
use SilverStripe\Security\Authenticator;
10
use SilverStripe\Security\Security;
11
use SilverStripe\Security\Member;
12
use SilverStripe\Security\LoginAttempt;
13
14
/**
15
 * Authenticator for the default "member" method
16
 *
17
 * @author Sam Minnee <[email protected]>
18
 * @author Simon Erkelens <[email protected]>
19
 */
20
class MemberAuthenticator implements Authenticator
21
{
22
23
    public function supportedServices()
24
    {
25
        // Bitwise-OR of all the supported services in this Authenticator, to make a bitmask
26
        return Authenticator::LOGIN | Authenticator::LOGOUT | Authenticator::CHANGE_PASSWORD
27
            | Authenticator::RESET_PASSWORD;
28
    }
29
30
    /**
31
     * @param array $data
32
     * @param null|ValidationResult $result
33
     * @return null|Member
34
     */
35
    public function authenticate($data, &$result = null)
36
    {
37
        // Find authenticated member
38
        $member = $this->authenticateMember($data, $result);
0 ignored issues
show
Bug introduced by
It seems like $result defined by parameter $result on line 35 can be null; however, SilverStripe\Security\Me...r::authenticateMember() does not accept null, maybe add an additional type check?

It seems like you allow that null is being passed for a parameter, however the function which is called does not seem to accept null.

We recommend to add an additional type check (or disallow null for the parameter):

function notNullable(stdClass $x) { }

// Unsafe
function withoutCheck(stdClass $x = null) {
    notNullable($x);
}

// Safe - Alternative 1: Adding Additional Type-Check
function withCheck(stdClass $x = null) {
    if ($x instanceof stdClass) {
        notNullable($x);
    }
}

// Safe - Alternative 2: Changing Parameter
function withNonNullableParam(stdClass $x) {
    notNullable($x);
}
Loading history...
39
40
        // Optionally record every login attempt as a {@link LoginAttempt} object
41
        $this->recordLoginAttempt($data, $member, $result->isValid());
42
43
        if ($member) {
44
            Session::clear('BackURL');
45
        }
46
47
        return $result->isValid() ? $member : null;
48
    }
49
50
    /**
51
     * Attempt to find and authenticate member if possible from the given data
52
     *
53
     * @param array $data Form submitted data
54
     * @param ValidationResult $result
55
     * @param Member|null This third parameter is used in the CMSAuthenticator(s)
56
     * @return Member Found member, regardless of successful login
57
     */
58
    protected function authenticateMember($data, &$result, $member = null)
59
    {
60
        // Default success to false
61
        $email = !empty($data['Email']) ? $data['Email'] : null;
62
        $result = new ValidationResult();
63
64
        // Check default login (see Security::setDefaultAdmin())
65
        $asDefaultAdmin = $email === Security::default_admin_username();
66
        if ($asDefaultAdmin) {
67
            // If logging is as default admin, ensure record is setup correctly
68
            $member = Member::default_admin();
69
            $success = Security::check_default_admin($email, $data['Password']);
70
            $result = $member->canLogIn();
71
            //protect against failed login
72
            if ($success && $result->isValid()) {
73
                return $member;
74
            } else {
75
                $result->addError(_t(
76
                    'SilverStripe\\Security\\Member.ERRORWRONGCRED',
77
                    "The provided details don't seem to be correct. Please try again."
78
                ));
79
            }
80
        }
81
82
        // Attempt to identify user by email
83
        if (!$member && $email) {
84
            // Find user by email
85
            /** @var Member $member */
86
            $member = Member::get()
87
                ->filter([Member::config()->get('unique_identifier_field') => $email])
88
                ->first();
89
        }
90
91
        // Validate against member if possible
92
        if ($member && !$asDefaultAdmin) {
93
            $result = $member->checkPassword($data['Password']);
94
        }
95
96
        // Emit failure to member and form (if available)
97
        if (!$result->isValid()) {
98
            if ($member) {
99
                $member->registerFailedLogin();
100
            }
101
        } else {
102
            if ($member) {
103
                $member->registerSuccessfulLogin();
104
            } else {
105
                // A non-existing member occurred. This will make the result "valid" so let's invalidate
106
                $result->addError(_t(
107
                    'SilverStripe\\Security\\Member.ERRORWRONGCRED',
108
                    "The provided details don't seem to be correct. Please try again."
109
                ));
110
                $member = null;
111
            }
112
        }
113
114
        return $member;
115
    }
116
117
    /**
118
     * Log login attempt
119
     * TODO We could handle this with an extension
120
     *
121
     * @param array $data
122
     * @param Member $member
123
     * @param boolean $success
124
     */
125
    protected function recordLoginAttempt($data, $member, $success)
126
    {
127
        if (!Security::config()->get('login_recording')) {
128
            return;
129
        }
130
131
        // Check email is valid
132
        /** @skipUpgrade */
133
        $email = isset($data['Email']) ? $data['Email'] : null;
134
        if (is_array($email)) {
135
            throw new InvalidArgumentException("Bad email passed to MemberAuthenticator::authenticate(): $email");
136
        }
137
138
        $attempt = LoginAttempt::create();
139
        if ($success && $member) {
140
            // successful login (member is existing with matching password)
141
            $attempt->MemberID = $member->ID;
142
            $attempt->Status = 'Success';
143
144
            // Audit logging hook
145
            $member->extend('authenticated');
146
        } else {
147
            // Failed login - we're trying to see if a user exists with this email (disregarding wrong passwords)
148
            $attempt->Status = 'Failure';
149
            if ($member) {
150
                // Audit logging hook
151
                $attempt->MemberID = $member->ID;
152
                $member->extend('authenticationFailed');
153
            } else {
154
                // Audit logging hook
155
                Member::singleton()->extend('authenticationFailedUnknownUser', $data);
156
            }
157
        }
158
159
        $attempt->Email = $email;
160
        $attempt->IP = Controller::curr()->getRequest()->getIP();
161
        $attempt->write();
162
    }
163
164
    /**
165
     * @param $link
166
     * @return LostPasswordHandler
167
     */
168
    public function getLostPasswordHandler($link)
169
    {
170
        return LostPasswordHandler::create($link, $this);
171
    }
172
173
    /**
174
     * @param string $link
175
     * @return ChangePasswordHandler
176
     */
177
    public function getChangePasswordHandler($link)
178
    {
179
        return ChangePasswordHandler::create($link, $this);
180
    }
181
182
    /**
183
     * @param string $link
184
     * @return LoginHandler
185
     */
186
    public function getLoginHandler($link)
187
    {
188
        return LoginHandler::create($link, $this);
189
    }
190
191
    /**
192
     * @param string $link
193
     * @return LogoutHandler
194
     */
195
    public function getLogoutHandler($link)
196
    {
197
        return LogoutHandler::create($link, $this);
198
    }
199
}
200