1
<?php
2
/**
3
 * This file is part of the Shieldon package.
4
 *
5
 * (c) Terry L. <contact@terryl.in>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 * 
10
 * php version 7.1.0
11
 * 
12
 * @category  Web-security
13
 * @package   Shieldon
14
 * @author    Terry Lin <contact@terryl.in>
15
 * @copyright 2019 terrylinooo
16
 * @license   https://github.com/terrylinooo/shieldon/blob/2.x/LICENSE MIT
17
 * @link      https://github.com/terrylinooo/shieldon
18
 * @see       https://shieldon.io
19
 */
20

21
declare(strict_types=1);
22

23
namespace Shieldon\Firewall\Kernel;
24

25
use Shieldon\Firewall\Kernel;
26
use function Shieldon\Firewall\get_session_instance;
27
use function Shieldon\Firewall\create_new_session_instance;
28
use function Shieldon\Firewall\get_microtimestamp;
29
use function time;
30

31
/*
32
 * The main functionality for this trait is to limit the online session amount.
33
 */
34
trait SessionTrait
35
{
36
    /**
37
     *   Public methods       | Desctiotion
38
     *  ----------------------|---------------------------------------------
39
     *   limitSession         | Limit the amount of the online users.
40
     *   getSessionCount      | Get the amount of the sessions.
41
     *   removeSessionsByIp   | Remove sessions using the same IP address.
42
     *  ----------------------|---------------------------------------------
43
     */
44

45
    /**
46
     * Are you willing to limit the online session amount?
47
     *
48
     * @var array
49
     */
50
    protected $sessionLimit = [
51

52
        // How many sessions will be available?
53
        // 0 = no limit.
54
        'count' => 0,
55

56
        // How many minutes will a session be availe to visit?
57
        // 0 = no limit.
58
        'period' => 0,
59

60
        // Only allow one session per IP address.
61
        // If this option is set to true, user with multiple sessions will be
62
        // removed from the session table.
63
        'unique_only' => false,
64
    ];
65

66
    /**
67
     * Record the online session status.
68
     * This will be enabled when $sessionLimit[count] > 0
69
     *
70
     * This array is recording a live data, not a setting value.
71
     *
72
     * @var array
73
     */
74
    protected $sessionStatus = [
75

76
        // Online session count.
77
        'count' => 0,
78

79
        // Current session order.
80
        'order' => 0,
81

82
        // Current waiting queue.
83
        'queue' => 0,
84
    ];
85

86

87
    /**
88
     * Current user's session data.
89
     *
90
     * @var array
91
     */
92
    protected $sessionData = [];
93

94
    /**
95
     * Limt online sessions.
96
     *
97
     * @param int $count   The amount of online users. If reached, users will be
98
     *                     in queue.
99
     * @param int $period  The period of time allows users browsing. 
100
     *                     (unit: second)
101
     * @param bool $unique Allow only one session per IP address.
102
     *
103
     * @return void
104
     */
105 3
    public function limitSession(int $count = 1000, int $period = 300, bool $unique = false): void
106
    {
107 3
        $this->sessionLimit = [
108 3
            'count' => $count,
109 3
            'period' => $period,
110 3
            'unique_only' => $unique,
111
        ];
112
    }
113

114
    /**
115
     * Get online people count. If enable limitSession.
116
     *
117
     * @return int
118
     */
119 3
    public function getSessionCount(): int
120
    {
121 3
        return $this->sessionStatus['count'];
122
    }
123

124
    /**
125
     * Deal with online sessions.
126
     *
127
     * @param int $statusCode The response code.
128
     *
129
     * @return int The response code.
130
     */
131 3
    protected function sessionHandler($statusCode): int
132
    {
133 3
        if (Kernel::RESPONSE_ALLOW !== $statusCode) {
134 3
            return $statusCode;
135
        }
136

137
        // If you don't enable `limit traffic`, ignore the following steps.
138 3
        if (empty($this->sessionLimit['count'])) {
139 3
            return Kernel::RESPONSE_ALLOW;
140

141
        } else {
142

143
            // Get the proerties.
144 3
            $limit = (int) ($this->sessionLimit['count'] ?? 0);
145 3
            $period = (int) ($this->sessionLimit['period'] ?? 300);
146 3
            $now = time();
147

148 3
            $this->sessionData = $this->driver->getAll('session');
149

150 3
            $sessionPools = [];
151

152 3
            $i = 1;
153 3
            $sessionOrder = 0;
154

155 3
            $sessionId = get_session_instance()->getId();
156

157 3
            if (!empty($this->sessionData)) {
158 3
                foreach ($this->sessionData as $v) {
159 3
                    $sessionPools[] = $v['id'];
160 3
                    $lasttime = (int) $v['time'];
161

162 3
                    if ($sessionId === $v['id']) {
163 3
                        $sessionOrder = $i;
164
                    }
165
    
166
                    // Remove session if it expires.
167 3
                    if ($now - $lasttime > $period) {
168 3
                        $this->driver->delete($v['id'], 'session');
169
                    }
170 3
                    $i++;
171
                }
172

173 3
                if (0 === $sessionOrder) {
174 3
                    $sessionOrder = $i;
175
                }
176
            } else {
177 3
                $sessionOrder = 0;
178
            }
179

180
            // Count the online sessions.
181 3
            $this->sessionStatus['count'] = count($sessionPools);
182 3
            $this->sessionStatus['order'] = $sessionOrder;
183 3
            $this->sessionStatus['queue'] = $sessionOrder - $limit;
184

185 3
            if (!in_array($sessionId, $sessionPools)) {
186 3
                $this->sessionStatus['count']++;
187
            }
188

189
            /*
190
            if (!in_array($sessionId, $sessionPools)) {
191
                $this->sessionStatus['count']++;
192

193
                $data = [];
194

195
                // New session, record this data.
196
                $data['id'] = $sessionId;
197
                $data['ip'] = $this->ip;
198
                $data['time'] = $now;
199
                $data['microtimestamp'] = get_microtimestamp();
200

201
                $this->driver->save($sessionId, $data, 'session');
202
            }*/
203

204
            // Online session count reached the limit. So return RESPONSE_LIMIT_SESSION response code.
205 3
            if ($sessionOrder >= $limit) {
206 3
                return Kernel::RESPONSE_LIMIT_SESSION;
207
            }
208
        }
209

210 3
        return Kernel::RESPONSE_ALLOW;
211
    }
212

213
    /**
214
     * Remove sessions using the same IP address.
215
     * This method must be run after `sessionHandler`.
216
     * 
217
     * @param string $ip An IP address
218
     *
219
     * @return void
220
     */
221 3
    protected function removeSessionsByIp(string $ip): void
222
    {
223 3
        if ($this->sessionLimit['unique_only']) {
224 3
            foreach ($this->sessionData as $v) {
225 3
                if ($v['ip'] === $ip) {
226 3
                    $this->driver->delete($v['id'], 'session');
227
                }
228
            }
229
        }
230
    }
231

232
    // @codeCoverageIgnoreStart
233

234
    /**
235
     * For testing propose. This method will create new Session.
236
     *
237
     * @param string $sessionId The session Id.
238
     *
239
     * @return void
240
     */
241
    protected function setSessionId(string $sessionId = ''): void
242
    {
243
        if ('' !== $sessionId) {
244
            create_new_session_instance($sessionId);
245
        }
246
    }
247

248
    // @codeCoverageIgnoreEnd
249
}

Read our documentation on viewing source code .

Loading