Passed
Pull Request — master (#127)
by Garion
02:33
created

SecurityAdminAccountResetExtension::reset()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 71
Code Lines 42

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 42
dl 0
loc 71
rs 8.6257
c 0
b 0
f 0
cc 6
nc 5
nop 1

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 SilverStripe\MFA\Extension;
4
5
use Exception;
6
use Psr\Log\LoggerInterface;
7
use SilverStripe\Control\Email\Email;
8
use SilverStripe\Control\HTTPRequest;
9
use SilverStripe\Control\HTTPResponse;
10
use SilverStripe\Core\Extension;
11
use SilverStripe\Admin\SecurityAdmin;
12
use SilverStripe\Core\Injector\Injector;
13
use SilverStripe\ORM\ValidationException;
14
use SilverStripe\Security\Member;
15
use SilverStripe\Security\PasswordEncryptor_NotFoundException;
16
use SilverStripe\Security\Permission;
17
18
/**
19
 * This extension is applied to SecurityAdmin to provide an additional endpoint
20
 * for sending account reset requests.
21
 *
22
 * @package SilverStripe\MFA\Extension
23
 * @property SecurityAdmin $owner
24
 */
25
class SecurityAdminAccountResetExtension extends Extension
26
{
27
    private static $allowed_actions = [
0 ignored issues
show
introduced by
The private property $allowed_actions is not used, and could be removed.
Loading history...
28
        'reset',
29
    ];
30
31
    public function reset(HTTPRequest $request): HTTPResponse
32
    {
33
        if (!$request->isPOST() || !$request->param('ID')) {
34
            return $this->owner
35
                ->getResponse()
36
                ->setStatusCode(400)
37
                ->addHeader('Content-Type', 'application/json')
38
                ->setBody(json_encode(
39
                    [
40
                        'error' => _t(__CLASS__ . '.BAD_REQUEST', 'Invalid request')
41
                    ]
42
                ));
43
        }
44
45
        if (!Permission::check(MemberMFAExtension::MFA_ADMINISTER_REGISTERED_METHODS)) {
46
            return $this->owner
47
                ->getResponse()
48
                ->setStatusCode(403)
49
                ->addHeader('Content-Type', 'application/json')
50
                ->setBody(json_encode(
51
                    [
52
                        'error' => _t(
53
                            __CLASS__ . '.INSUFFICIENT_PERMISSIONS',
54
                            'Insufficient permissions to reset user'
55
                        )
56
                    ]
57
                ));
58
        }
59
60
        /** @var Member $memberToReset */
61
        $memberToReset = Member::get()->byID($request->param('ID'));
0 ignored issues
show
Bug introduced by
$request->param('ID') of type string is incompatible with the type integer expected by parameter $id of SilverStripe\ORM\DataList::byID(). ( Ignorable by Annotation )

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

61
        $memberToReset = Member::get()->byID(/** @scrutinizer ignore-type */ $request->param('ID'));
Loading history...
62
63
        if ($memberToReset === null) {
64
            return $this->owner
65
                ->getResponse()
66
                ->setStatusCode(403)
67
                ->addHeader('Content-Type', 'application/json')
68
                ->setBody(json_encode(
69
                    [
70
                        'error' => _t(
71
                            __CLASS__ . '.INVALID_MEMBER',
72
                            'Requested member for reset not found'
73
                        )
74
                    ]
75
                ));
76
        }
77
78
        $sent = $this->sendResetEmail($memberToReset);
79
80
        if (!$sent) {
81
            return $this->owner
82
                ->getResponse()
83
                ->setStatusCode(500)
84
                ->addHeader('Content-Type', 'application/json')
85
                ->setBody(json_encode(
86
                    [
87
                        'error' => _t(
88
                            __CLASS__ . '.EMAIL_NOT_SENT',
89
                            'Email sending failed'
90
                        )
91
                    ]
92
                ));
93
        }
94
95
        return $this->owner
96
            ->getResponse()
97
            ->setStatusCode(200)
98
            ->addHeader('Content-Type', 'application/json')
99
            ->setBody(json_encode(
100
                [
101
                    'success' => true,
102
                ]
103
            ));
104
    }
105
106
    /**
107
     * @param Member&MemberResetExtension $member
108
     * @return bool
109
     * @throws ValidationException
110
     * @throws PasswordEncryptor_NotFoundException
111
     */
112
    private function sendResetEmail($member)
113
    {
114
        // Generate / store / obtain reset token
115
        $token = $member->generateAccountResetTokenAndStoreHash();
116
117
        // Create email and fire
118
        try {
119
            $email = Email::create()
120
                ->setHTMLTemplate('SilverStripe\\MFA\\Email\\AccountReset')
121
                ->setData($member)
122
                ->setSubject(_t(
123
                    __CLASS__ . '.ACCOUNT_RESET_EMAIL_SUBJECT',
124
                    'Reset your account'
125
                ))
126
                ->addData('AccountResetLink', $this->getAccountResetLink($member, $token))
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->getAccountResetLink($member, $token) targeting SilverStripe\MFA\Extensi...::getAccountResetLink() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
127
                ->addData('Member', $member)
128
                ->setFrom(Email::config()->admin_email)
129
                ->setTo($member->Email);
130
131
            return $email->send();
132
        } catch (Exception $e) {
133
            Injector::inst()->get(LoggerInterface::class)->info('WARNING: Account Reset Email failed to send');
134
            return false;
135
        }
136
    }
137
138
    private function getAccountResetLink(Member $member, string $token)
0 ignored issues
show
Unused Code introduced by
The parameter $token is not used and could be removed. ( Ignorable by Annotation )

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

138
    private function getAccountResetLink(Member $member, /** @scrutinizer ignore-unused */ string $token)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $member is not used and could be removed. ( Ignorable by Annotation )

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

138
    private function getAccountResetLink(/** @scrutinizer ignore-unused */ Member $member, string $token)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
139
    {
140
        return null;
141
    }
142
}
143