Passed
Pull Request — master (#3)
by Tim
02:14
created

ExpiryDate::shWarning()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

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