IsNotValidationStatusPending()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
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 (Permission::check('ADMIN', 'any', $this->owner)) {
85
            return;
86
        }
87
88
        if ($this->IsAccountExpired()) {
89
            // This allows you to send an activation email if necessary
90
            $this->owner->extend('onExpiredLoginAttempt');
91
92
            $result->addError(_t('ValidationStatusExtension.ACCOUNT_EXPIRED', "Your account has expired"));
93
        }
94
95
        // Check general status
96
        if (self::config()->get('status_prevent_login')) {
97
            if ($this->owner->IsValidationStatusPending()) {
98
                $result->addError(_t('ValidationStatusExtension.ACCOUNT_PENDING', "Your account is currently pending"));
99
            }
100
            if ($this->owner->IsValidationStatusDisabled()) {
101
                $result->addError(_t('ValidationStatusExtension.ACCOUNT_DISABLED', "Your account has been disabled"));
102
            }
103
        }
104
    }
105
106
    public function IsAccountExpired()
107
    {
108
        $account_expiration = self::config()->get("account_expiration");
109
        if (!$account_expiration) {
110
            return false;
111
        }
112
        $lastVisited = null;
113
        if ($this->owner->hasField('LastVisited')) {
114
            $lastVisited = $this->owner->LastVisited;
115
        } else {
116
            // Use last valid attempt as fallback
117
            $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...
118
                ->filter('Status', 'Success')
119
                ->sort('Created', 'DESC')
120
                ->first();
121
            if ($lastValidAttempt) {
122
                $lastVisited = $lastValidAttempt->Created;
123
            }
124
        }
125
        // It needs at least one visit to be able to expire
126
        if ($lastVisited) {
127
            $checkTime = strtotime($account_expiration, strtotime($lastVisited));
128
            // It has expired
129
            if ($checkTime < DBDatetime::now()->getTimestamp()) {
130
                return true;
131
            }
132
        }
133
        return false;
134
    }
135
136
    public function updateCMSFields(FieldList $fields)
137
    {
138
        $fields->makeFieldReadonly('ValidationStatus');
139
    }
140
141
    public function updateCMSActions(FieldList $actions)
142
    {
143
        if ($this->IsValidationStatusPending()) {
144
            $actions->push(new CustomAction('doValidationApprove', _t('ValidationStatusExtension.APPROVE', 'Approve')));
145
            $actions->push(new CustomAction('doValidationDisable', _t('ValidationStatusExtension.DISABLE', 'Disable')));
146
        }
147
        if ($this->IsValidationStatusApproved()) {
148
            $actions->push(new CustomAction('doValidationDisable', _t('ValidationStatusExtension.DISABLE', 'Disable')));
149
        }
150
        if ($this->IsValidationStatusDisabled()) {
151
            $actions->push(new CustomAction('doValidationApprove', _t('ValidationStatusExtension.APPROVE', 'Approve')));
152
        }
153
    }
154
155
    public function doValidationApprove()
156
    {
157
        $this->owner->ValidationStatus = self::VALIDATION_STATUS_APPROVED;
158
        $this->owner->write();
159
160
        $this->owner->extend('onValidationApprove');
161
162
        return _t('ValidationStatusExtension.APPROVED', 'Approved');
163
    }
164
165
    public function doValidationDisable()
166
    {
167
        $this->owner->ValidationStatus = self::VALIDATION_STATUS_DISABLED;
168
        $this->owner->write();
169
170
        $this->owner->extend('onValidationDisable');
171
172
        return _t('ValidationStatusExtension.DISABLED', 'Disabled');
173
    }
174
175
    public function IsValidationStatusPending()
176
    {
177
        return $this->owner->ValidationStatus == self::VALIDATION_STATUS_PENDING;
178
    }
179
180
    public function IsValidationStatusApproved()
181
    {
182
        return $this->owner->ValidationStatus == self::VALIDATION_STATUS_APPROVED;
183
    }
184
185
    public function IsValidationStatusDisabled()
186
    {
187
        return $this->owner->ValidationStatus == self::VALIDATION_STATUS_DISABLED;
188
    }
189
190
    public function IsNotValidationStatusPending()
191
    {
192
        return $this->owner->ValidationStatus != self::VALIDATION_STATUS_PENDING;
193
    }
194
195
    public function IsNotValidationStatusApproved()
196
    {
197
        return $this->owner->ValidationStatus != self::VALIDATION_STATUS_APPROVED;
198
    }
199
200
    public function IsNotValidationStatusDisabled()
201
    {
202
        return $this->owner->ValidationStatus != self::VALIDATION_STATUS_DISABLED;
203
    }
204
}
205