Passed
Push — master ( c0c55b...6635bd )
by Thomas
03:40
created

ValidationStatusExtension::IsAccountExpired()   A

Complexity

Conditions 6
Paths 10

Size

Total Lines 28
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 18
c 1
b 0
f 1
dl 0
loc 28
rs 9.0444
cc 6
nc 10
nop 0
1
<?php
2
3
namespace LeKoala\CommonExtensions;
4
5
use SilverStripe\ORM\DataObject;
6
use SilverStripe\Forms\FieldList;
7
use SilverStripe\Security\Member;
8
use SilverStripe\ORM\DataExtension;
9
use LeKoala\CmsActions\CustomAction;
10
use SilverStripe\Security\Permission;
11
use SilverStripe\ORM\ValidationResult;
12
use SilverStripe\Security\LoginAttempt;
13
use SilverStripe\Core\Config\Configurable;
14
use SilverStripe\ORM\FieldType\DBDatetime;
15
16
/**
17
 * Allow to enable/disable login for your objects based on status
18
 *
19
 * @property DataObject|Member $owner
20
 * @property string $ValidationStatus
21
 */
22
class ValidationStatusExtension extends DataExtension
23
{
24
    use Configurable;
25
26
    const VALIDATION_STATUS_PENDING = 'pending';
27
    const VALIDATION_STATUS_APPROVED = 'approved';
28
    const VALIDATION_STATUS_DISABLED = 'disabled';
29
30
    /**
31
     * A time string in the future (eg: +7 days) based on LastVisited column
32
     * This require a LastVisited field on your Member class to be really accurate
33
     * @config
34
     * @var string
35
     */
36
    private static $account_expiration = null;
0 ignored issues
show
introduced by
The private property $account_expiration is not used, and could be removed.
Loading history...
37
38
    /**
39
     * @config
40
     * @var boolean
41
     */
42
    private static $status_prevent_login = true;
0 ignored issues
show
introduced by
The private property $status_prevent_login is not used, and could be removed.
Loading history...
43
    private static $db = [
0 ignored issues
show
introduced by
The private property $db is not used, and could be removed.
Loading history...
44
        "ValidationStatus" => "NiceEnum('pending,approved,disabled')"
45
    ];
46
47
    /**
48
     * @return array
49
     */
50
    public static function listStatus()
51
    {
52
        return [
53
            self::VALIDATION_STATUS_PENDING => _t('ValidationStatusExtension.VALIDATION_STATUS_PENDING', 'pending'),
54
            self::VALIDATION_STATUS_APPROVED => _t('ValidationStatusExtension.VALIDATION_STATUS_APPROVED', 'approved'),
55
            self::VALIDATION_STATUS_DISABLED => _t('ValidationStatusExtension.VALIDATION_STATUS_DISABLED', 'disabled'),
56
        ];
57
    }
58
59
60
    /**
61
     * @param Member $member
62
     * @return boolean
63
     */
64
    public function canView($member = null)
65
    {
66
        // Can always see approved
67
        if ($this->IsValidationStatusApproved()) {
68
            return true;
69
        }
70
        return Permission::check('ADMIN', 'any', $member);
71
    }
72
73
    /**
74
     * This is called by Member::validateCanLogin which is typically called in MemberAuthenticator::authenticate::authenticateMember
75
     * which is used in LoginHandler::doLogin::checkLogin
76
     *
77
     * This means canLogIn is called before 2FA, for instance
78
     *
79
     * @param ValidationResult $result
80
     * @return void
81
     */
82
    public function canLogIn(ValidationResult $result)
83
    {
84
        if ($this->IsAccountExpired()) {
85
            // This allows you to send an activation email if necessary
86
            $this->owner->extend('onExpiredLoginAttempt');
87
88
            $result->addError(_t('ValidationStatusExtension.ACCOUNT_EXPIRED', "Your account has expired"));
89
        }
90
91
        // Check general status
92
        if (self::config()->get('status_prevent_login')) {
93
            if ($this->owner->IsValidationStatusPending()) {
94
                $result->addError(_t('ValidationStatusExtension.ACCOUNT_PENDING', "Your account is currently pending"));
95
            }
96
            if ($this->owner->IsValidationStatusDisabled()) {
97
                $result->addError(_t('ValidationStatusExtension.ACCOUNT_DISABLED', "Your account has been disabled"));
98
            }
99
        }
100
    }
101
102
    public function IsAccountExpired()
103
    {
104
        $account_expiration = self::config()->get("account_expiration");
105
        if (!$account_expiration) {
106
            return false;
107
        }
108
        $lastVisited = null;
109
        if ($this->owner->hasField('LastVisited')) {
110
            $lastVisited = $this->owner->LastVisited;
111
        } else {
112
            // Use last valid attempt as fallback
113
            $lastValidAttempt = LoginAttempt::getByEmail($this->Email)
0 ignored issues
show
Bug Best Practice introduced by
The property Email does not exist on LeKoala\CommonExtensions\ValidationStatusExtension. Did you maybe forget to declare it?
Loading history...
114
                ->filter('Status', 'Success')
115
                ->sort('Created', 'DESC')
116
                ->first();
117
            if ($lastValidAttempt) {
118
                $lastVisited = $lastValidAttempt->Created;
119
            }
120
        }
121
        // It needs at least one visit to be able to expire
122
        if ($lastVisited) {
123
            $checkTime = strtotime($account_expiration, strtotime($lastVisited));
124
            // It has expired
125
            if ($checkTime < DBDatetime::now()->getTimestamp()) {
126
                return true;
127
            }
128
        }
129
        return false;
130
    }
131
132
    public function updateCMSFields(FieldList $fields)
133
    {
134
        $fields->makeFieldReadonly('ValidationStatus');
135
    }
136
137
    public function updateCMSActions(FieldList $actions)
138
    {
139
        if ($this->IsValidationStatusPending()) {
140
            $actions->push(new CustomAction('doValidationApprove', _t('ValidationStatusExtension.APPROVE', 'Approve')));
141
            $actions->push(new CustomAction('doValidationDisable', _t('ValidationStatusExtension.DISABLE', 'Disable')));
142
        }
143
        if ($this->IsValidationStatusApproved()) {
144
            $actions->push(new CustomAction('doValidationDisable', _t('ValidationStatusExtension.DISABLE', 'Disable')));
145
        }
146
        if ($this->IsValidationStatusDisabled()) {
147
            $actions->push(new CustomAction('doValidationApprove', _t('ValidationStatusExtension.APPROVE', 'Approve')));
148
        }
149
    }
150
151
    public function doValidationApprove()
152
    {
153
        $this->owner->ValidationStatus = self::VALIDATION_STATUS_APPROVED;
154
        $this->owner->write();
155
156
        $this->owner->extend('onValidationApprove');
157
158
        return _t('ValidationStatusExtension.APPROVED', 'Approved');
159
    }
160
161
    public function doValidationDisable()
162
    {
163
        $this->owner->ValidationStatus = self::VALIDATION_STATUS_DISABLED;
164
        $this->owner->write();
165
166
        $this->owner->extend('onValidationDisable');
167
168
        return _t('ValidationStatusExtension.DISABLED', 'Disabled');
169
    }
170
171
    public function IsValidationStatusPending()
172
    {
173
        return $this->owner->ValidationStatus == self::VALIDATION_STATUS_PENDING;
174
    }
175
176
    public function IsValidationStatusApproved()
177
    {
178
        return $this->owner->ValidationStatus == self::VALIDATION_STATUS_APPROVED;
179
    }
180
181
    public function IsValidationStatusDisabled()
182
    {
183
        return $this->owner->ValidationStatus == self::VALIDATION_STATUS_DISABLED;
184
    }
185
186
    public function IsNotValidationStatusPending()
187
    {
188
        return $this->owner->ValidationStatus != self::VALIDATION_STATUS_PENDING;
189
    }
190
191
    public function IsNotValidationStatusApproved()
192
    {
193
        return $this->owner->ValidationStatus != self::VALIDATION_STATUS_APPROVED;
194
    }
195
196
    public function IsNotValidationStatusDisabled()
197
    {
198
        return $this->owner->ValidationStatus != self::VALIDATION_STATUS_DISABLED;
199
    }
200
}
201