1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace PeterColes\LiveOrLetDie\Middleware; |
4
|
|
|
|
5
|
|
|
use Closure; |
6
|
|
|
use Illuminate\Config\Repository as Config; |
7
|
|
|
use Illuminate\Auth\AuthManager as Auth; |
8
|
|
|
use Illuminate\Session\Store as Session; |
9
|
|
|
|
10
|
|
|
class SessionTimeout |
11
|
|
|
{ |
12
|
|
|
protected $session; |
13
|
|
|
|
14
|
|
|
protected $timeout; |
15
|
|
|
|
16
|
|
|
protected $login; |
17
|
|
|
|
18
|
|
|
protected $logout; |
19
|
|
|
|
20
|
|
|
protected $auth; |
21
|
|
|
|
22
|
|
|
public function __construct(Session $session, Config $config, Auth $auth) |
23
|
|
|
{ |
24
|
|
|
$this->session = $session; |
25
|
|
|
|
26
|
|
|
$this->timeout = $config->get('session.lifetime') * 60; |
27
|
|
|
|
28
|
|
|
$this->login = $config->get('liveorletdie.login', 'login'); |
29
|
|
|
$this->logout = $config->get('liveorletdie.logout', 'logout'); |
30
|
|
|
|
31
|
|
|
$this->auth = $auth; |
32
|
|
|
} |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* Handle an incoming request. |
36
|
|
|
* |
37
|
|
|
* @param \Illuminate\Http\Request $request |
38
|
|
|
* @param \Closure $next |
39
|
|
|
* @return mixed |
40
|
|
|
*/ |
41
|
|
|
public function handle($request, Closure $next) |
42
|
|
|
{ |
43
|
|
|
// don't interfere with normal logout requests |
44
|
|
|
if ($request->is($this->logout)) { |
45
|
|
|
$this->session->forget('last_activity'); |
46
|
|
|
return $next($request); |
47
|
|
|
} |
48
|
|
|
|
49
|
|
|
// check if we should the authenticated session to end |
50
|
|
|
// and if yes, terminate it |
51
|
|
|
if ($this->endSession($request)) { |
52
|
|
|
return $this->terminateAndRespond($request, $next); |
53
|
|
|
} |
54
|
|
|
|
55
|
|
|
// if we just want the time remaining claculate and return that |
56
|
|
|
// this will stop any further processing, including preventing unintended extension of the underlying session |
57
|
|
|
if ($request->is('session/remaining')) { |
58
|
|
|
return response($this->timeout - (time() - $this->session->get('last_activity'))); |
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
// for all other requests, including pings to extend the session, we update our timer and continue normally |
62
|
|
|
$this->updateActivityCounter($request); |
63
|
|
|
|
64
|
|
|
$response = $next($request); |
65
|
|
|
|
66
|
|
|
$this->afterRequest($request); |
67
|
|
|
|
68
|
|
|
return $response; |
69
|
|
|
} |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* After the request has been processed, check if it was for login |
73
|
|
|
* and if so and successful initialise the last_activity timer |
74
|
|
|
* |
75
|
|
|
* @param \Illuminate\Http\Request $request |
76
|
|
|
* @return void |
77
|
|
|
*/ |
78
|
|
|
protected function afterRequest($request) |
79
|
|
|
{ |
80
|
|
View Code Duplication |
if ($request->is($this->login) && !$this->auth->guest()) { |
|
|
|
|
81
|
|
|
$this->session->put('last_activity', time()); |
82
|
|
|
} |
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
/** |
86
|
|
|
* Determine whether the session should be ended due to timeout or frontend request |
87
|
|
|
* We whitelist the login page from this assessment |
88
|
|
|
* |
89
|
|
|
* @param \Illuminate\Http\Request $request |
90
|
|
|
* @return boolean |
91
|
|
|
*/ |
92
|
|
|
protected function endSession($request) |
93
|
|
|
{ |
94
|
|
|
return !$request->is($this->login) && ($this->timedOut() || $request->is('session/end')); |
95
|
|
|
} |
96
|
|
|
|
97
|
|
|
/** |
98
|
|
|
* Determine whether timeout has occurred or been forced by other activity on the site |
99
|
|
|
* If there's no data to do the timeout check, then we assume the session has been otherwise ended |
100
|
|
|
* |
101
|
|
|
* @return boolean |
102
|
|
|
*/ |
103
|
|
|
protected function timedOut() |
104
|
|
|
{ |
105
|
|
|
return !$this->session->has('last_activity') || (time() - $this->session->get('last_activity')) > $this->timeout || $this->auth->guest(); |
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
/** |
109
|
|
|
* Logout and clear our session var - one or both of which may be redundant, but not harmful |
110
|
|
|
* Then, for our package routes compose a suitable response, anf for other routes continue processing as normal |
111
|
|
|
* |
112
|
|
|
* @param \Illuminate\Http\Request $request |
113
|
|
|
* @param Closure $next |
114
|
|
|
* @return mixed |
115
|
|
|
*/ |
116
|
|
|
protected function terminateAndRespond($request, $next) |
117
|
|
|
{ |
118
|
|
|
$this->auth->logout(); |
119
|
|
|
$this->session->forget('last_activity'); |
120
|
|
|
|
121
|
|
|
if ($request->is('session/end')) { |
122
|
|
|
return response('session ended', 200); |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
if ($request->is('session/remaining')) { |
126
|
|
|
return response(0, 200); |
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
if ($request->is('session/ping')) { |
130
|
|
|
return response('trying to keep alive a session that has already expired', 400); |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
return $next($request); |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
/** |
137
|
|
|
* Update the counter |
138
|
|
|
* Normally we wouldn't get here unless we are logged in |
139
|
|
|
* But we whitelisted the login page so need to handle that |
140
|
|
|
* |
141
|
|
|
* @param \Illuminate\Http\Request $request |
142
|
|
|
* @return void |
143
|
|
|
*/ |
144
|
|
|
protected function updateActivityCounter($request) |
145
|
|
|
{ |
146
|
|
View Code Duplication |
if ($request->is($this->login)) { |
|
|
|
|
147
|
|
|
$this->session->forget('last_activity'); |
148
|
|
|
} else { |
149
|
|
|
$this->session->put('last_activity', time()); |
150
|
|
|
} |
151
|
|
|
} |
152
|
|
|
} |
153
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.