ExpiryDate::__construct()   B
last analyzed

Complexity

Conditions 11
Paths 63

Size

Total Lines 48
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 24
nc 63
nop 2
dl 0
loc 48
rs 7.3166
c 0
b 0
f 0

How to fix   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
declare(strict_types=1);
4
5
namespace SimpleSAML\Module\expirycheck\Auth\Process;
6
7
use SimpleSAML\Assert\Assert;
8
use SimpleSAML\Auth;
9
use SimpleSAML\Error;
10
use SimpleSAML\Logger;
11
use SimpleSAML\Module;
12
use SimpleSAML\Utils;
13
14
use function array_key_exists;
15
use function date;
16
use function intval;
17
use function is_int;
18
use function is_string;
19
use function strtotime;
20
use function time;
21
22
/**
23
 * Filter which show "about to expire" warning or deny access if netid is expired.
24
 *
25
 * Based on preprodwarning module by rnd.feide.no
26
 *
27
 * <code>
28
 * // show about2xpire warning or deny access if netid is expired
29
 * 10 => [
30
 *     'class' => 'expirycheck:ExpiryDate',
31
 *     'netid_attr' => 'userPrincipalName',
32
 *     'expirydate_attr' => 'accountExpires',
33
 *     'convert_expirydate_to_unixtime' => true,
34
 *     'warndaysbefore' => 60,
35
 *     'date_format' => 'd.m.Y', # php date syntax
36
 * ],
37
 * </code>
38
 *
39
 * @package SimpleSAMLphp
40
 */
41
class ExpiryDate extends Auth\ProcessingFilter
42
{
43
    /** @var int */
44
    private int $warndaysbefore = 0;
45
46
    /**
47
     * @var string
48
     * @psalm-suppress PropertyNotSetInConstructor
49
     */
50
    private string $netidAttr;
51
52
    /**
53
     * @var string
54
     * @psalm-suppress PropertyNotSetInConstructor
55
     * */
56
    private string $expirydateAttr;
57
58
    /** @var string */
59
    private string $dateFormat = 'd.m.Y';
60
61
    /** @var bool */
62
    private bool $convertExpirydateToUnixtime = false;
63
64
65
    /**
66
     * Initialize this filter.
67
     *
68
     * @param array &$config  Configuration information about this filter.
69
     * @param mixed $reserved  For future use.
70
     */
71
    public function __construct(array &$config, $reserved)
72
    {
73
        parent::__construct($config, $reserved);
74
75
        if (array_key_exists('warndaysbefore', $config)) {
76
            if (!is_int($config['warndaysbefore'])) {
77
                throw new Error\Exception('Invalid value for number of days given to expirycheck::ExpiryDate filter.');
78
            }
79
80
            $this->warndaysbefore = $config['warndaysbefore'];
81
        }
82
83
        if (array_key_exists('netid_attr', $config)) {
84
            if (!is_string($config['netid_attr'])) {
85
                throw new Error\Exception(
86
                    'Invalid attribute name given as eduPersonPrincipalName to expirycheck::ExpiryDate filter.',
87
                );
88
            }
89
90
            $this->netidAttr = $config['netid_attr'];
91
        }
92
93
        if (array_key_exists('expirydate_attr', $config)) {
94
            if (!is_string($config['expirydate_attr'])) {
95
                throw new Error\Exception(
96
                    'Invalid attribute name given as schacExpiryDate to expirycheck::ExpiryDate filter.',
97
                );
98
            }
99
100
            $this->expirydateAttr = $config['expirydate_attr'];
101
        }
102
103
        if (array_key_exists('date_format', $config)) {
104
            if (!is_string($config['date_format'])) {
105
                throw new Error\Exception('Invalid date format given to expirycheck::ExpiryDate filter.');
106
            }
107
108
            $this->dateFormat = $config['date_format'];
109
        }
110
111
        if (array_key_exists('convert_expirydate_to_unixtime', $config)) {
112
            if (!is_bool($config['convert_expirydate_to_unixtime'])) {
113
                throw new Error\Exception(
114
                    'Invalid value for convert_expirydate_to_unixtime given to expirycheck::ExpiryDate filter.',
115
                );
116
            }
117
118
            $this->convertExpirydateToUnixtime = $config['convert_expirydate_to_unixtime'];
119
        }
120
    }
121
122
123
    /**
124
     * Show expirational warning if remaining days is equal or under defined $warndaysbefore
125
     *
126
     * @param array &$state
127
     * @param int $expireOnDate
128
     * @param int $warndaysbefore
129
     * @return bool
130
     */
131
    public function shWarning(array &$state, int $expireOnDate, int $warndaysbefore): bool
132
    {
133
        $now = time();
134
        if ($expireOnDate >= $now) {
135
            $days = intval(($expireOnDate - $now) / 86400); //24*60*60=86400
136
            if ($days <= $warndaysbefore) {
137
                $state['daysleft'] = $days;
138
                return true;
139
            }
140
        }
141
        return false;
142
    }
143
144
145
    /**
146
     * Check if given date is older than today
147
     *
148
     * @param int $expireOnDate
149
     * @return bool
150
     */
151
    public function checkDate(int $expireOnDate): bool
152
    {
153
        $now = time();
154
        return $now <= $expireOnDate;
155
    }
156
157
158
    /**
159
     * Apply filter
160
     *
161
     * @param array &$state  The current state.
162
     */
163
    public function process(array &$state): void
164
    {
165
        Assert::keyExists($state, 'Attributes');
166
        Assert::keyExists($state['Attributes'], $this->netidAttr);
167
168
        /*
169
         * UTC format: 20090527080352Z
170
         */
171
        $netId = $state['Attributes'][$this->netidAttr][0];
172
        // expirydateAttr optional
173
        $expireOnDate = "0";
174
        if (array_key_exists($this->expirydateAttr, $state['Attributes'])) {
175
            $expireOnDate = $state['Attributes'][$this->expirydateAttr][0];
176
        }
177
178
        if (intval($expireOnDate) === 0) {
179
            // Never expires
180
            return;
181
        } elseif ($this->convertExpirydateToUnixtime === true) {
182
            $expireOnDate = $this->convertFiletimeToUnixtime($expireOnDate);
183
        } else {
184
            $expireOnDate = strtotime($expireOnDate);
185
        }
186
187
        $httpUtils = new Utils\HTTP();
188
        if ($this->shWarning($state, $expireOnDate, $this->warndaysbefore)) {
189
            if (isset($state['isPassive']) && $state['isPassive'] === true) {
190
                // We have a passive request. Skip the warning.
191
                return;
192
            }
193
            Logger::warning('expirycheck: NetID ' . $netId . ' is about to expire!');
194
195
            // Save state and redirect
196
            $state['expireOnDate'] = date($this->dateFormat, $expireOnDate);
197
            $state['netId'] = $netId;
198
            $id = Auth\State::saveState($state, 'expirywarning:about2expire');
199
            $url = Module::getModuleURL('expirycheck/about2expire');
200
            $httpUtils->redirectTrustedURL($url, ['StateId' => $id]);
201
        }
202
203
        if (!$this->checkDate($expireOnDate)) {
204
            Logger::error('expirycheck: NetID ' . $netId .
205
                ' has expired [' . date($this->dateFormat, $expireOnDate) . ']. Access denied!');
206
207
            /* Save state and redirect. */
208
            $state['expireOnDate'] = date($this->dateFormat, $expireOnDate);
209
            $state['netId'] = $netId;
210
            $id = Auth\State::saveState($state, 'expirywarning:expired');
211
            $url = Module::getModuleURL('expirycheck/expired');
212
            $httpUtils->redirectTrustedURL($url, ['StateId' => $id]);
213
        }
214
    }
215
216
217
    /**
218
     * @param string $fileTime Time as represented by MS Active Directory
219
     * @return int Unix-time
220
     */
221
    private function convertFiletimeToUnixtime(string $fileTime): int
222
    {
223
        $winSecs = intval(intval($fileTime) / 10000000); // divide by 10 000 000 to get seconds
224
        return $winSecs - 11644473600; // 1.1.1600 -> 1.1.1970 difference in seconds
225
    }
226
}
227