frikandel 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/.travis.yml +13 -4
- data/Gemfile.rails-3.2.x +6 -0
- data/Gemfile.rails-4.0.x +6 -0
- data/Gemfile.rails-4.1.x +6 -0
- data/Gemfile.rails-head +6 -0
- data/Guardfile +3 -4
- data/README.md +12 -2
- data/frikandel.gemspec +1 -1
- data/lib/frikandel/bind_session_to_ip_address.rb +9 -4
- data/lib/frikandel/configuration.rb +4 -2
- data/lib/frikandel/limit_session_lifetime.rb +18 -4
- data/lib/frikandel/version.rb +1 -1
- data/spec/controllers/bind_session_to_ip_address_controller_spec.rb +129 -16
- data/spec/controllers/combined_controller_spec.rb +63 -10
- data/spec/controllers/limit_session_lifetime_controller_spec.rb +341 -37
- data/spec/dummy/config/database.yml +6 -6
- data/spec/spec_helper.rb +17 -0
- data/spec/support/application_controller.rb +8 -5
- metadata +6 -4
- data/spec/dummy/db/test.sqlite3 +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0eb78e7fc8890e920f675060d65d1305ab310caa
|
4
|
+
data.tar.gz: dfe7532de11f1b0e4deacd6b5d9476256a6cfb22
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c9f94142388dd71adff1c529b0b43c6216f4f4819afca102cedbc523693ddd253b54dad9642c44229ea25ba730b44c08b1769a560eefdd6ae08aa5cd81c8f54c
|
7
|
+
data.tar.gz: 5723a7adfe0d66d77e4a9a37749f337536a21a8c5bb24109c51b4bb1089ff6e58766b8c51d8f9f07586a8caf88a5c37106c6ba6210ebfc40c6cd7dda34b8d98e
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -1,7 +1,16 @@
|
|
1
1
|
language: ruby
|
2
2
|
rvm:
|
3
|
-
- 1.9.3
|
4
|
-
- 2.0.0
|
5
|
-
- 2.1.
|
3
|
+
- "1.9.3"
|
4
|
+
- "2.0.0"
|
5
|
+
- "2.1.1"
|
6
|
+
- ruby-head
|
6
7
|
- jruby-19mode
|
7
|
-
|
8
|
+
gemfile:
|
9
|
+
- Gemfile.rails-3.2.x
|
10
|
+
- Gemfile.rails-4.0.x
|
11
|
+
- Gemfile.rails-4.1.x
|
12
|
+
- Gemfile.rails-head
|
13
|
+
matrix:
|
14
|
+
allow_failures:
|
15
|
+
- rvm: ruby-head
|
16
|
+
- gemfile: Gemfile.rails-head
|
data/Gemfile.rails-3.2.x
ADDED
data/Gemfile.rails-4.0.x
ADDED
data/Gemfile.rails-4.1.x
ADDED
data/Gemfile.rails-head
ADDED
data/Guardfile
CHANGED
@@ -3,8 +3,7 @@
|
|
3
3
|
|
4
4
|
guard :rspec do
|
5
5
|
watch(%r{^spec/.+_spec\.rb$})
|
6
|
-
watch(%r{^lib/(.+)\.rb$})
|
7
|
-
watch(
|
8
|
-
|
6
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| ["spec/lib/#{m[1]}_spec.rb", "spec/controllers"] }
|
7
|
+
watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
|
8
|
+
watch('spec/spec_helper.rb') { "spec" }
|
9
9
|
end
|
10
|
-
|
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# Frikandel
|
2
2
|
[](http://badge.fury.io/rb/frikandel)
|
3
3
|
[](https://travis-ci.org/taktsoft/frikandel)
|
4
|
+
[](https://codeclimate.com/github/taktsoft/frikandel)
|
4
5
|
|
5
6
|
This gem aims to improve the security of your rails application. It allows you to add a TTL (Time To Live) to the session cookie and allows you to bind the session to an IP address.
|
6
7
|
|
@@ -71,7 +72,7 @@ The default values are `24.hours` for `max_ttl` and `2.hours` for `ttl`. If you
|
|
71
72
|
|
72
73
|
### Customize on_invalid_session behavior
|
73
74
|
|
74
|
-
You can also overwrite what should happen when a cookie times out on the controller-level. The default behaviour is to do a `reset_session` and `redirect_to root_path`. For example, if you want to overwrite the default behavior when a user is on the `PublicController`, you want to overwrite the `
|
75
|
+
You can also overwrite what should happen when a cookie times out on the controller-level. The default behaviour is to do a `reset_session` and `redirect_to root_path`. For example, if you want to overwrite the default behavior when a user is on the `PublicController`, you want to overwrite the `on_invalid_session`-method in your controller:
|
75
76
|
|
76
77
|
```ruby
|
77
78
|
class PublicController < ApplicationController
|
@@ -91,7 +92,16 @@ end
|
|
91
92
|
|
92
93
|
## Changes
|
93
94
|
|
94
|
-
2.
|
95
|
+
2.1.0 -- Reset session only once if using the combination of TTL and IP address binding.
|
96
|
+
2.0.0 -- Added IP address binding. Renamed callback from 'on_expired_session' to 'on_invalid_session'.
|
97
|
+
|
98
|
+
## Test
|
99
|
+
|
100
|
+
To run the test suite with different rails version by selecting the corresponding gemfile. You can use this one liners:
|
101
|
+
|
102
|
+
$ BUNDLE_GEMFILE=Gemfile.rails-3.2.x bundle update && bundle exec rake spec
|
103
|
+
$ BUNDLE_GEMFILE=Gemfile.rails-4.0.x bundle update && bundle exec rake spec
|
104
|
+
$ BUNDLE_GEMFILE=Gemfile.rails-4.1.x bundle update && bundle exec rake spec
|
95
105
|
|
96
106
|
## Contributing
|
97
107
|
1. Fork it
|
data/frikandel.gemspec
CHANGED
@@ -4,16 +4,17 @@ module Frikandel
|
|
4
4
|
|
5
5
|
included do
|
6
6
|
append_before_filter :validate_session_ip_address
|
7
|
-
append_after_filter :persist_session_ip_address
|
8
7
|
end
|
9
8
|
|
10
9
|
private
|
11
10
|
|
12
11
|
def validate_session_ip_address
|
13
|
-
if session.key?(:ip_address) && !ip_address_match_with_current?
|
12
|
+
if session.key?(:ip_address) && !ip_address_match_with_current?
|
14
13
|
on_invalid_session
|
15
14
|
elsif !session.key?(:ip_address)
|
16
15
|
reset_session
|
16
|
+
else # session ip address is valid
|
17
|
+
persist_session_ip_address
|
17
18
|
end
|
18
19
|
end
|
19
20
|
|
@@ -25,9 +26,13 @@ module Frikandel
|
|
25
26
|
request.remote_ip
|
26
27
|
end
|
27
28
|
|
28
|
-
def ip_address_match_with_current?
|
29
|
-
|
29
|
+
def ip_address_match_with_current?
|
30
|
+
session[:ip_address] == current_ip_address
|
30
31
|
end
|
31
32
|
|
33
|
+
def reset_session
|
34
|
+
super
|
35
|
+
persist_session_ip_address
|
36
|
+
end
|
32
37
|
end
|
33
38
|
end
|
@@ -5,22 +5,36 @@ module Frikandel
|
|
5
5
|
|
6
6
|
included do
|
7
7
|
append_before_filter :validate_session_timestamp
|
8
|
-
append_after_filter :persist_session_timestamp
|
9
8
|
end
|
10
9
|
|
11
10
|
private
|
12
11
|
|
13
12
|
def validate_session_timestamp
|
14
|
-
if session.key?(:ttl) && session.key?(:max_ttl) && (
|
13
|
+
if session.key?(:ttl) && session.key?(:max_ttl) && (reached_ttl? || reached_max_ttl?)
|
15
14
|
on_invalid_session
|
16
|
-
elsif !session.key?(:ttl)
|
15
|
+
elsif !session.key?(:ttl) || !session.key?(:max_ttl)
|
17
16
|
reset_session
|
17
|
+
else # session timestamp is valid
|
18
|
+
persist_session_timestamp
|
18
19
|
end
|
19
20
|
end
|
20
21
|
|
22
|
+
def reached_ttl?
|
23
|
+
session[:ttl] < Frikandel::Configuration.ttl.ago
|
24
|
+
end
|
25
|
+
|
26
|
+
def reached_max_ttl?
|
27
|
+
session[:max_ttl] < Time.now
|
28
|
+
end
|
29
|
+
|
21
30
|
def persist_session_timestamp
|
22
31
|
session[:ttl] = Time.now
|
23
|
-
session[:max_ttl] ||= Frikandel::Configuration.max_ttl.
|
32
|
+
session[:max_ttl] ||= Frikandel::Configuration.max_ttl.since
|
33
|
+
end
|
34
|
+
|
35
|
+
def reset_session
|
36
|
+
super
|
37
|
+
persist_session_timestamp
|
24
38
|
end
|
25
39
|
end
|
26
40
|
end
|
data/lib/frikandel/version.rb
CHANGED
@@ -1,41 +1,154 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
require "support/application_controller"
|
3
3
|
|
4
|
+
|
4
5
|
class BindSessionToIpAddressController < ApplicationController
|
5
6
|
include Frikandel::BindSessionToIpAddress
|
6
7
|
|
8
|
+
before_filter :flash_alert_and_redirect_home, only: [:redirect_home]
|
9
|
+
|
7
10
|
def home
|
8
11
|
render text: "bind test"
|
9
12
|
end
|
10
|
-
end
|
11
13
|
|
12
|
-
|
13
|
-
it "writes current ip address to session" do
|
14
|
-
expect(session[:ip_address]).to be_nil
|
15
|
-
get :home
|
16
|
-
expect(session[:ip_address]).to eql("0.0.0.0")
|
14
|
+
def redirect_home
|
17
15
|
end
|
18
16
|
|
19
|
-
|
20
|
-
session[:ip_address] = "1.2.3.4"
|
21
|
-
controller.should_receive(:on_invalid_session)
|
17
|
+
protected
|
22
18
|
|
23
|
-
|
19
|
+
def flash_alert_and_redirect_home
|
20
|
+
flash[:alert] = "alert test"
|
21
|
+
redirect_to bind_session_to_ip_address_home_url
|
24
22
|
end
|
23
|
+
end
|
24
|
+
|
25
25
|
|
26
|
+
describe BindSessionToIpAddressController do
|
27
|
+
context "requests" do
|
28
|
+
it "writes current ip address to session" do
|
29
|
+
expect(session[:ip_address]).to be_nil
|
26
30
|
|
27
|
-
context "ip address isn't present in session" do
|
28
|
-
it "resets the session" do
|
29
|
-
session[:user_id] = 4337
|
30
31
|
get :home
|
31
32
|
|
32
|
-
session[:
|
33
|
+
expect(session[:ip_address]).to eql("0.0.0.0")
|
33
34
|
end
|
34
35
|
|
35
|
-
it "
|
36
|
+
it "writes current ip address to session even on redirect in another before filter" do
|
37
|
+
expect(session[:ip_address]).to be_nil
|
38
|
+
|
39
|
+
simulate_redirect!(:redirect_home, :home)
|
40
|
+
|
41
|
+
expect(session[:ip_address]).to eql("0.0.0.0")
|
42
|
+
|
43
|
+
flash.should_not be_empty
|
44
|
+
flash[:alert].should eql("alert test")
|
45
|
+
end
|
46
|
+
|
47
|
+
it "raises an exception if session address and current ip address don't match" do
|
48
|
+
session[:ip_address] = "1.2.3.4"
|
49
|
+
controller.should_receive(:on_invalid_session)
|
50
|
+
|
36
51
|
get :home
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
context "ip address isn't present in session" do
|
56
|
+
it "resets the session and persists the ip address" do
|
57
|
+
session[:user_id] = 4337
|
58
|
+
session.delete(:ip_address)
|
59
|
+
session[:ttl] = "SomeTTL"
|
60
|
+
session[:max_ttl] = "SomeMaxTTL"
|
61
|
+
|
62
|
+
controller.should_receive(:reset_session).and_call_original
|
63
|
+
controller.should_receive(:persist_session_ip_address).and_call_original
|
64
|
+
get :home
|
65
|
+
|
66
|
+
session[:user_id].should be_blank
|
67
|
+
session[:ip_address].should be_present
|
68
|
+
session[:ip_address].should eql("0.0.0.0")
|
69
|
+
session[:ttl].should be_blank
|
70
|
+
session[:max_ttl].should be_blank
|
71
|
+
end
|
72
|
+
|
73
|
+
it "allows the request to be rendered as normal" do
|
74
|
+
get :home
|
75
|
+
|
76
|
+
response.body.should eql("bind test")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
context ".validate_session_ip_address" do
|
83
|
+
it "calls on_invalid_session if ip address doesn't match with current" do
|
84
|
+
session[:ip_address] = "1.3.3.7"
|
85
|
+
|
86
|
+
controller.should_receive(:ip_address_match_with_current?).and_return(false)
|
87
|
+
controller.should_receive(:on_invalid_session)
|
88
|
+
|
89
|
+
controller.send(:validate_session_ip_address)
|
90
|
+
end
|
91
|
+
|
92
|
+
it "calls reset_session if ip address isn't persisted in session" do
|
93
|
+
session.delete(:ip_address)
|
94
|
+
|
95
|
+
controller.should_not_receive(:ip_address_match_with_current?)
|
96
|
+
controller.should_receive(:reset_session)
|
97
|
+
|
98
|
+
controller.send(:validate_session_ip_address)
|
99
|
+
end
|
100
|
+
|
101
|
+
it "calls persist_session_ip_address if validation passes" do
|
102
|
+
session[:ip_address] = "1.3.3.7"
|
103
|
+
|
104
|
+
controller.should_receive(:ip_address_match_with_current?).and_return(true)
|
105
|
+
controller.should_receive(:persist_session_ip_address)
|
106
|
+
|
107
|
+
controller.send(:validate_session_ip_address)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
context ".persist_session_ip_address" do
|
113
|
+
it "sets the current ip address in session on key ip_address" do
|
114
|
+
expect {
|
115
|
+
controller.should_receive(:current_ip_address).and_return("1.3.3.7")
|
116
|
+
controller.send(:persist_session_ip_address)
|
117
|
+
}.to change {
|
118
|
+
session[:ip_address]
|
119
|
+
}.from(nil).to("1.3.3.7")
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
context ".current_ip_address" do
|
125
|
+
it "returns the remote_ip from request" do
|
126
|
+
request.should_receive(:remote_ip).and_return(:request_remote_ip)
|
127
|
+
|
128
|
+
controller.send(:current_ip_address).should eql(:request_remote_ip)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
|
133
|
+
context ".ip_address_match_with_current?" do
|
134
|
+
it "compares ip address from session with the current ip address" do
|
135
|
+
controller.stub(:current_ip_address).and_return("1.3.3.7")
|
136
|
+
|
137
|
+
session[:ip_address] = "1.3.3.7"
|
138
|
+
|
139
|
+
controller.send(:ip_address_match_with_current?).should be_true
|
140
|
+
|
141
|
+
session[:ip_address] = "7.3.3.1"
|
142
|
+
|
143
|
+
controller.send(:ip_address_match_with_current?).should be_false
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
37
147
|
|
38
|
-
|
148
|
+
context ".reset_session" do
|
149
|
+
it "calls persist_session_ip_address" do
|
150
|
+
controller.should_receive(:persist_session_ip_address).and_call_original
|
151
|
+
controller.send(:reset_session)
|
39
152
|
end
|
40
153
|
end
|
41
154
|
end
|
@@ -1,24 +1,40 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
require "support/application_controller"
|
3
3
|
|
4
|
+
|
4
5
|
class CombinedController < ApplicationController
|
5
6
|
include Frikandel::LimitSessionLifetime
|
6
7
|
include Frikandel::BindSessionToIpAddress
|
7
8
|
|
9
|
+
before_filter :flash_alert_and_redirect_home, only: [:redirect_home]
|
10
|
+
|
8
11
|
def home
|
9
12
|
render text: "combined test"
|
10
13
|
end
|
14
|
+
|
15
|
+
def redirect_home
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
20
|
+
def flash_alert_and_redirect_home
|
21
|
+
flash[:alert] = "alert test"
|
22
|
+
redirect_to combined_home_url
|
23
|
+
end
|
11
24
|
end
|
12
25
|
|
26
|
+
|
13
27
|
describe CombinedController do
|
14
28
|
context "ttl nor ip isn't present in session" do
|
15
|
-
it "resets the session" do
|
29
|
+
it "resets the session and persists ip address, ttl & max_ttl" do
|
16
30
|
session[:user_id] = 4337
|
31
|
+
|
17
32
|
get :home
|
18
33
|
|
19
34
|
session[:user_id].should be_blank
|
20
|
-
session[:ttl].should be_present
|
21
35
|
session[:ip_address].should be_present
|
36
|
+
session[:ttl].should be_present
|
37
|
+
session[:max_ttl].should be_present
|
22
38
|
end
|
23
39
|
|
24
40
|
it "allows the request to be rendered as normal" do
|
@@ -26,31 +42,68 @@ describe CombinedController do
|
|
26
42
|
|
27
43
|
response.body.should eql("combined test")
|
28
44
|
end
|
45
|
+
|
46
|
+
it "persists ttl, max_ttl and ip even on redirect in another before filter" do
|
47
|
+
session[:ip_address].should be_nil
|
48
|
+
session[:ttl].should be_nil
|
49
|
+
session[:max_ttl].should be_nil
|
50
|
+
|
51
|
+
simulate_redirect!(:redirect_home, :home)
|
52
|
+
|
53
|
+
session[:ip_address].should be_present
|
54
|
+
session[:ttl].should be_present
|
55
|
+
session[:max_ttl].should be_present
|
56
|
+
|
57
|
+
flash.should_not be_empty
|
58
|
+
flash[:alert].should eql("alert test")
|
59
|
+
end
|
29
60
|
end
|
30
61
|
|
62
|
+
|
31
63
|
context "ttl or ip isn't present in session" do
|
32
|
-
it "resets the session if ip address is missing" do
|
64
|
+
it "resets the session and persists ip address, ttl & max_ttl if ip address is missing" do
|
33
65
|
session[:user_id] = 4337
|
34
|
-
session[:ttl] =
|
66
|
+
session[:ttl] = last_ttl = Time.now
|
67
|
+
session[:max_ttl] = last_max_ttl = Frikandel::Configuration.max_ttl.from_now
|
68
|
+
|
35
69
|
get :home
|
36
70
|
|
37
71
|
session[:user_id].should be_blank
|
38
|
-
|
39
|
-
session[:ttl].should be_present
|
40
|
-
session[:ttl].should_not eql("Something")
|
41
72
|
session[:ip_address].should be_present
|
73
|
+
session[:ttl].should be_present
|
74
|
+
session[:ttl].should_not eql(last_ttl)
|
75
|
+
session[:max_ttl].should be_present
|
76
|
+
session[:max_ttl].should_not eql(last_max_ttl)
|
42
77
|
end
|
43
78
|
|
44
|
-
it "resets the session if ttl is missing" do
|
79
|
+
it "resets the session and persists ip address, ttl & max_ttl if ttl is missing" do
|
45
80
|
session[:user_id] = 4337
|
46
|
-
session[:ip_address] = "
|
81
|
+
session[:ip_address] = "0.0.0.0"
|
82
|
+
session[:max_ttl] = last_max_ttl = Frikandel::Configuration.max_ttl.from_now
|
83
|
+
|
47
84
|
get :home
|
48
85
|
|
49
86
|
session[:user_id].should be_blank
|
50
|
-
|
87
|
+
session[:ip_address].should be_present
|
88
|
+
session[:ip_address].should eql("0.0.0.0")
|
51
89
|
session[:ttl].should be_present
|
90
|
+
session[:max_ttl].should be_present
|
91
|
+
session[:max_ttl].should_not eql(last_max_ttl)
|
92
|
+
end
|
93
|
+
|
94
|
+
it "resets the session and persists ip address, ttl & max_ttl if max_ttl is missing" do
|
95
|
+
session[:user_id] = 4337
|
96
|
+
session[:ip_address] = "0.0.0.0"
|
97
|
+
session[:ttl] = last_ttl = Time.now
|
98
|
+
|
99
|
+
get :home
|
100
|
+
|
101
|
+
session[:user_id].should be_blank
|
52
102
|
session[:ip_address].should be_present
|
53
103
|
session[:ip_address].should eql("0.0.0.0")
|
104
|
+
session[:ttl].should be_present
|
105
|
+
session[:ttl].should_not eql(last_ttl)
|
106
|
+
session[:max_ttl].should be_present
|
54
107
|
end
|
55
108
|
end
|
56
109
|
end
|
@@ -1,68 +1,372 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
require "support/application_controller"
|
3
3
|
|
4
|
+
|
4
5
|
class LimitSessionLifetimeController < ApplicationController
|
5
6
|
include Frikandel::LimitSessionLifetime
|
6
7
|
|
8
|
+
before_filter :flash_alert_and_redirect_home, only: [:redirect_home]
|
9
|
+
|
7
10
|
def home
|
8
11
|
render text: "ttl test"
|
9
12
|
end
|
13
|
+
|
14
|
+
def redirect_home
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def flash_alert_and_redirect_home
|
20
|
+
flash[:alert] = "alert test"
|
21
|
+
|
22
|
+
redirect_to limit_session_lifetime_home_url
|
23
|
+
end
|
10
24
|
end
|
11
25
|
|
26
|
+
|
12
27
|
describe LimitSessionLifetimeController do
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
28
|
+
context "requests" do
|
29
|
+
it "writes ttl and max_ttl to session" do
|
30
|
+
expect(session[:ttl]).to be_nil
|
31
|
+
expect(session[:max_ttl]).to be_nil
|
32
|
+
|
33
|
+
get :home
|
34
|
+
|
35
|
+
expect(session[:ttl]).to be_a(Time)
|
36
|
+
expect(session[:max_ttl]).to be_a(Time)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "writes ttl and max_ttl to session even on redirect in another before filter" do
|
40
|
+
expect(session[:ttl]).to be_nil
|
41
|
+
expect(session[:max_ttl]).to be_nil
|
42
|
+
|
43
|
+
simulate_redirect!(:redirect_home, :home)
|
44
|
+
|
45
|
+
expect(session[:ttl]).to be_a(Time)
|
46
|
+
expect(session[:max_ttl]).to be_a(Time)
|
47
|
+
|
48
|
+
flash.should_not be_empty
|
49
|
+
flash[:alert].should eql("alert test")
|
50
|
+
end
|
51
|
+
|
52
|
+
it "holds the session for at least .1 seconds" do
|
53
|
+
get :home
|
54
|
+
|
55
|
+
session[:user_id] = 1337
|
56
|
+
sleep 0.1
|
57
|
+
|
58
|
+
get :home
|
59
|
+
|
60
|
+
session[:user_id].should be_present
|
61
|
+
session[:user_id].should eq 1337
|
62
|
+
end
|
63
|
+
|
64
|
+
it "destroys the session after SESSION_TTL" do
|
65
|
+
get :home
|
66
|
+
|
67
|
+
session[:user_id] = 2337
|
68
|
+
session[:ttl] = (Frikandel::Configuration.ttl + 1.minute).seconds.ago
|
69
|
+
|
70
|
+
get :home
|
71
|
+
|
72
|
+
session[:user_id].should be_blank
|
73
|
+
end
|
74
|
+
|
75
|
+
it "destroys the session after SESSION_MAX_TTL" do
|
76
|
+
get :home
|
77
|
+
|
78
|
+
session[:user_id] = 3337
|
79
|
+
request.session[:max_ttl] = 1.minute.ago
|
80
|
+
|
81
|
+
get :home
|
82
|
+
|
83
|
+
session[:user_id].should be_blank
|
84
|
+
end
|
85
|
+
|
86
|
+
it "is configurable" do
|
87
|
+
Frikandel::Configuration.ttl = 1.minute
|
88
|
+
get :home
|
89
|
+
|
90
|
+
session[:ttl] = 30.minutes.ago
|
91
|
+
session[:user_id] = 5337
|
92
|
+
|
93
|
+
get :home
|
94
|
+
|
95
|
+
session[:user_id].should be_blank
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
context "ttl isn't present in session" do
|
100
|
+
it "resets the session and persists ttl & max_ttl" do
|
101
|
+
session[:user_id] = 4337
|
102
|
+
session[:ip_address] = "SomeIP"
|
103
|
+
session.delete(:ttl)
|
104
|
+
session[:max_ttl] = "SomeMaxTTL"
|
105
|
+
|
106
|
+
controller.should_receive(:reset_session).and_call_original
|
107
|
+
controller.should_receive(:persist_session_timestamp).and_call_original
|
108
|
+
get :home
|
109
|
+
|
110
|
+
session[:user_id].should be_blank
|
111
|
+
session[:ip_address].should be_blank
|
112
|
+
session[:ttl].should be_present
|
113
|
+
session[:ttl].should be_a(Time)
|
114
|
+
session[:max_ttl].should be_present
|
115
|
+
session[:max_ttl].should_not eql("SomeMaxTTL")
|
116
|
+
session[:max_ttl].should be_a(Time)
|
117
|
+
end
|
118
|
+
|
119
|
+
it "allows the request to be rendered as normal" do
|
120
|
+
session.delete(:ttl)
|
121
|
+
session[:max_ttl] = "SomeMaxTTL"
|
122
|
+
|
123
|
+
get :home
|
124
|
+
|
125
|
+
response.body.should eql("ttl test")
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
|
130
|
+
context "max_ttl isn't present in session" do
|
131
|
+
it "resets the session and persists ttl & max_ttl" do
|
132
|
+
session[:user_id] = 4337
|
133
|
+
session[:ip_address] = "SomeIP"
|
134
|
+
session[:ttl] = "SomeTTL"
|
135
|
+
session.delete(:max_ttl)
|
136
|
+
|
137
|
+
controller.should_receive(:reset_session).and_call_original
|
138
|
+
controller.should_receive(:persist_session_timestamp).and_call_original
|
139
|
+
get :home
|
140
|
+
|
141
|
+
session[:user_id].should be_blank
|
142
|
+
session[:ip_address].should be_blank
|
143
|
+
session[:ttl].should be_present
|
144
|
+
session[:ttl].should_not eql("SomeTTL")
|
145
|
+
session[:ttl].should be_a(Time)
|
146
|
+
session[:max_ttl].should be_present
|
147
|
+
session[:max_ttl].should be_a(Time)
|
148
|
+
end
|
149
|
+
|
150
|
+
it "allows the request to be rendered as normal" do
|
151
|
+
session[:ttl] = "SomeTTL"
|
152
|
+
session.delete(:max_ttl)
|
153
|
+
|
154
|
+
get :home
|
155
|
+
|
156
|
+
response.body.should eql("ttl test")
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
|
161
|
+
context "ttl and max_ttl isn't present in session" do
|
162
|
+
it "resets the session and persists ttl & max_ttl" do
|
163
|
+
session[:user_id] = 4337
|
164
|
+
session[:ip_address] = "SomeIP"
|
165
|
+
session.delete(:ttl)
|
166
|
+
session.delete(:max_ttl)
|
167
|
+
|
168
|
+
controller.should_receive(:reset_session).and_call_original
|
169
|
+
controller.should_receive(:persist_session_timestamp).and_call_original
|
170
|
+
get :home
|
171
|
+
|
172
|
+
session[:user_id].should be_blank
|
173
|
+
session[:ip_address].should be_blank
|
174
|
+
session[:ttl].should be_present
|
175
|
+
session[:ttl].should be_a(Time)
|
176
|
+
session[:max_ttl].should be_present
|
177
|
+
session[:max_ttl].should be_a(Time)
|
178
|
+
end
|
179
|
+
|
180
|
+
it "allows the request to be rendered as normal" do
|
181
|
+
session.delete(:ttl)
|
182
|
+
session.delete(:max_ttl)
|
183
|
+
|
184
|
+
get :home
|
185
|
+
|
186
|
+
response.body.should eql("ttl test")
|
187
|
+
end
|
188
|
+
end
|
21
189
|
end
|
22
190
|
|
23
|
-
it "destroys the session after SESSION_TTL" do
|
24
|
-
get :home
|
25
|
-
session[:user_id] = 2337
|
26
|
-
request.session[:ttl] = (Frikandel::Configuration.ttl + 1.minute).seconds.ago
|
27
|
-
get :home
|
28
191
|
|
29
|
-
|
192
|
+
context ".validate_session_timestamp" do
|
193
|
+
it "calls on_invalid_session if ttl is reached" do
|
194
|
+
session[:ttl] = "SomeTTL"
|
195
|
+
session[:max_ttl] = "SomeMaxTTL"
|
196
|
+
|
197
|
+
controller.should_receive(:reached_ttl?).and_return(true)
|
198
|
+
controller.stub(:reached_max_ttl?).and_return(false)
|
199
|
+
|
200
|
+
controller.should_receive(:on_invalid_session)
|
201
|
+
|
202
|
+
controller.send(:validate_session_timestamp)
|
203
|
+
end
|
204
|
+
|
205
|
+
it "calls on_invalid_session if max_ttl is reached" do
|
206
|
+
session[:ttl] = "SomeTTL"
|
207
|
+
session[:max_ttl] = "SomeMaxTTL"
|
208
|
+
|
209
|
+
controller.stub(:reached_ttl?).and_return(false)
|
210
|
+
controller.should_receive(:reached_max_ttl?).and_return(true)
|
211
|
+
|
212
|
+
controller.should_receive(:on_invalid_session)
|
213
|
+
|
214
|
+
controller.send(:validate_session_timestamp)
|
215
|
+
end
|
216
|
+
|
217
|
+
it "calls on_invalid_session if ttl and max_ttl are reached" do
|
218
|
+
session[:ttl] = "SomeTTL"
|
219
|
+
session[:max_ttl] = "SomeMaxTTL"
|
220
|
+
|
221
|
+
controller.stub(:reached_ttl?).and_return(true)
|
222
|
+
controller.stub(:reached_max_ttl?).and_return(true)
|
223
|
+
|
224
|
+
controller.should_receive(:on_invalid_session)
|
225
|
+
|
226
|
+
controller.send(:validate_session_timestamp)
|
227
|
+
end
|
228
|
+
|
229
|
+
it "calls reset_session if ttl isn't persisted in session" do
|
230
|
+
session.delete(:ttl)
|
231
|
+
session[:max_ttl] = "SomeMaxTTL"
|
232
|
+
|
233
|
+
controller.should_receive(:reset_session)
|
234
|
+
|
235
|
+
controller.send(:validate_session_timestamp)
|
236
|
+
end
|
237
|
+
|
238
|
+
it "calls reset_session if max_ttl isn't persisted in session" do
|
239
|
+
session[:ttl] = "SomeTTL"
|
240
|
+
session.delete(:max_ttl)
|
241
|
+
|
242
|
+
controller.should_receive(:persist_session_timestamp)
|
243
|
+
|
244
|
+
controller.send(:validate_session_timestamp)
|
245
|
+
end
|
246
|
+
|
247
|
+
it "calls reset_session if ttl and max_ttl aren't persisted in session" do
|
248
|
+
session.delete(:ttl)
|
249
|
+
session.delete(:max_ttl)
|
250
|
+
|
251
|
+
controller.should_receive(:persist_session_timestamp)
|
252
|
+
|
253
|
+
controller.send(:validate_session_timestamp)
|
254
|
+
end
|
255
|
+
|
256
|
+
it "calls persist_session_timestamp if validation passes" do
|
257
|
+
session[:ttl] = "SomeTTL"
|
258
|
+
session[:max_ttl] = "SomeMaxTTL"
|
259
|
+
|
260
|
+
controller.stub(:reached_ttl?).and_return(false)
|
261
|
+
controller.stub(:reached_max_ttl?).and_return(false)
|
262
|
+
|
263
|
+
controller.should_receive(:persist_session_timestamp)
|
264
|
+
|
265
|
+
controller.send(:validate_session_timestamp)
|
266
|
+
end
|
30
267
|
end
|
31
268
|
|
32
|
-
it "destroys the session after SESSION_MAX_TTL" do
|
33
|
-
get :home
|
34
|
-
session[:user_id] = 3337
|
35
269
|
|
36
|
-
|
37
|
-
|
270
|
+
context ".reached_ttl?" do
|
271
|
+
it "returns true if persisted ttl is less than configured ttl seconds ago" do
|
272
|
+
current_time = Time.now
|
273
|
+
Time.stub(:now).and_return(current_time)
|
274
|
+
|
275
|
+
session[:ttl] = current_time.ago(Frikandel::Configuration.ttl + 1)
|
276
|
+
|
277
|
+
controller.send(:reached_ttl?).should be_true
|
278
|
+
end
|
279
|
+
|
280
|
+
it "returns false if persisted ttl is equal to configured ttl seconds ago" do
|
281
|
+
current_time = Time.now
|
282
|
+
Time.stub(:now).and_return(current_time)
|
283
|
+
|
284
|
+
session[:ttl] = current_time.ago(Frikandel::Configuration.ttl)
|
285
|
+
|
286
|
+
controller.send(:reached_ttl?).should be_false
|
287
|
+
end
|
38
288
|
|
39
|
-
|
289
|
+
it "returns false if persisted ttl is greater than configured ttl seconds ago" do
|
290
|
+
current_time = Time.now
|
291
|
+
Time.stub(:now).and_return(current_time)
|
292
|
+
|
293
|
+
session[:ttl] = current_time.ago(Frikandel::Configuration.ttl - 1)
|
294
|
+
|
295
|
+
controller.send(:reached_ttl?).should be_false
|
296
|
+
end
|
40
297
|
end
|
41
|
-
it "is configurable" do
|
42
|
-
old_value = Frikandel::Configuration.ttl
|
43
|
-
Frikandel::Configuration.ttl = 1.minute
|
44
|
-
get :home
|
45
|
-
session[:ttl] = 30.minutes.ago
|
46
|
-
session[:user_id] = 5337
|
47
298
|
|
48
|
-
get :home
|
49
|
-
session[:user_id].should be_blank
|
50
299
|
|
51
|
-
|
300
|
+
context ".reached_max_ttl?" do
|
301
|
+
it "returns true if persisted max_ttl is less than current time" do
|
302
|
+
current_time = Time.now
|
303
|
+
Time.stub(:now).and_return(current_time)
|
304
|
+
|
305
|
+
session[:max_ttl] = current_time.ago(1)
|
306
|
+
|
307
|
+
controller.send(:reached_max_ttl?).should be_true
|
308
|
+
end
|
309
|
+
|
310
|
+
it "returns false if persisted max_ttl is equal to current time" do
|
311
|
+
current_time = Time.now
|
312
|
+
Time.stub(:now).and_return(current_time)
|
313
|
+
|
314
|
+
session[:max_ttl] = current_time
|
315
|
+
|
316
|
+
controller.send(:reached_max_ttl?).should be_false
|
317
|
+
end
|
318
|
+
|
319
|
+
it "returns false if persisted max_ttl is greater than current time" do
|
320
|
+
current_time = Time.now
|
321
|
+
Time.stub(:now).and_return(current_time)
|
322
|
+
|
323
|
+
session[:max_ttl] = current_time.since(1)
|
324
|
+
|
325
|
+
controller.send(:reached_max_ttl?).should be_false
|
326
|
+
end
|
52
327
|
end
|
53
328
|
|
54
|
-
context "ttl isn't present in session" do
|
55
|
-
it "resets the session" do
|
56
|
-
session[:user_id] = 4337
|
57
|
-
get :home
|
58
329
|
|
59
|
-
|
330
|
+
context ".persist_session_timestamp" do
|
331
|
+
it "sets ttl to current time" do
|
332
|
+
current_time = Time.now
|
333
|
+
Time.stub(:now).and_return(current_time)
|
334
|
+
|
335
|
+
expect {
|
336
|
+
controller.send(:persist_session_timestamp)
|
337
|
+
}.to change {
|
338
|
+
session[:ttl]
|
339
|
+
}.from(nil).to(current_time)
|
60
340
|
end
|
61
341
|
|
62
|
-
it "
|
63
|
-
|
342
|
+
it "sets max_ttl to configured max_ttl seconds in future if it's blank" do
|
343
|
+
current_time = Time.now
|
344
|
+
max_ttl_time = current_time.since(Frikandel::Configuration.max_ttl)
|
345
|
+
Time.stub(:now).and_return(current_time)
|
346
|
+
|
347
|
+
expect {
|
348
|
+
controller.send(:persist_session_timestamp)
|
349
|
+
}.to change {
|
350
|
+
session[:max_ttl]
|
351
|
+
}.from(nil).to(max_ttl_time)
|
352
|
+
end
|
353
|
+
|
354
|
+
it "doesn't set max_ttl if it's present" do
|
355
|
+
session[:max_ttl] = "SomeMaxTTL"
|
356
|
+
|
357
|
+
expect {
|
358
|
+
controller.send(:persist_session_timestamp) # second call, shouldn't change max_ttl
|
359
|
+
}.to_not change {
|
360
|
+
session[:max_ttl]
|
361
|
+
}.from("SomeMaxTTL")
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
64
365
|
|
65
|
-
|
366
|
+
context ".reset_session" do
|
367
|
+
it "calls persist_session_timestamp" do
|
368
|
+
controller.should_receive(:persist_session_timestamp).and_call_original
|
369
|
+
controller.send(:reset_session)
|
66
370
|
end
|
67
371
|
end
|
68
372
|
end
|
@@ -5,21 +5,21 @@
|
|
5
5
|
# gem 'sqlite3'
|
6
6
|
development:
|
7
7
|
adapter: sqlite3
|
8
|
-
database:
|
8
|
+
database: ":memory:"
|
9
9
|
pool: 5
|
10
|
-
timeout:
|
10
|
+
timeout: 500
|
11
11
|
|
12
12
|
# Warning: The database defined as "test" will be erased and
|
13
13
|
# re-generated from your development database when you run "rake".
|
14
14
|
# Do not set this db to the same as development or production.
|
15
15
|
test:
|
16
16
|
adapter: sqlite3
|
17
|
-
database:
|
17
|
+
database: ":memory:"
|
18
18
|
pool: 5
|
19
|
-
timeout:
|
19
|
+
timeout: 500
|
20
20
|
|
21
21
|
production:
|
22
22
|
adapter: sqlite3
|
23
|
-
database:
|
23
|
+
database: ":memory:"
|
24
24
|
pool: 5
|
25
|
-
timeout:
|
25
|
+
timeout: 500
|
data/spec/spec_helper.rb
CHANGED
@@ -19,3 +19,20 @@ RSpec.configure do |config|
|
|
19
19
|
Frikandel::Configuration.defaults!
|
20
20
|
end
|
21
21
|
end
|
22
|
+
|
23
|
+
|
24
|
+
# some helper methods
|
25
|
+
|
26
|
+
def simulate_redirect!(from_action, to_action)
|
27
|
+
get from_action.intern
|
28
|
+
from_flash = request.flash # HACK for RAILS_VERSION=3.2.0
|
29
|
+
|
30
|
+
controller.instance_variable_set(:@_frikandel_did_reset_session, nil) # reset state for redirect request
|
31
|
+
|
32
|
+
get to_action.intern
|
33
|
+
request.flash.update(from_flash.to_hash) # HACK for RAILS_VERSION=3.2.0
|
34
|
+
end
|
35
|
+
|
36
|
+
def flash
|
37
|
+
request.flash
|
38
|
+
end
|
@@ -1,12 +1,15 @@
|
|
1
|
-
|
2
1
|
Rails.application.routes.draw do
|
3
2
|
root to: "application#home"
|
4
|
-
get "/limit_session_lifetime_home" => "limit_session_lifetime#home"
|
5
|
-
get "/
|
6
|
-
get "/bind_session_to_ip_address_home" => "bind_session_to_ip_address#home"
|
7
|
-
get "/
|
3
|
+
get "/limit_session_lifetime_home" => "limit_session_lifetime#home", :as => :limit_session_lifetime_home
|
4
|
+
get "/limit_session_lifetime_redirect_home" => "limit_session_lifetime#redirect_home"
|
5
|
+
get "/bind_session_to_ip_address_home" => "bind_session_to_ip_address#home", :as => :bind_session_to_ip_address_home
|
6
|
+
get "/bind_session_to_ip_address_redirect_home" => "bind_session_to_ip_address#redirect_home"
|
7
|
+
get "/combined_controller_home" => "combined#home", :as => :combined_home
|
8
|
+
get "/combined_controller_redirect_home" => "combined#redirect_home"
|
9
|
+
get "/customized_controller_home" => "customized_on_invalid_session#home", :as => :customized_controller_home
|
8
10
|
end
|
9
11
|
|
12
|
+
|
10
13
|
class ApplicationController < ActionController::Base
|
11
14
|
protect_from_forgery with: :exception
|
12
15
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: frikandel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Taktsoft
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-05-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -125,6 +125,10 @@ files:
|
|
125
125
|
- ".rspec"
|
126
126
|
- ".travis.yml"
|
127
127
|
- Gemfile
|
128
|
+
- Gemfile.rails-3.2.x
|
129
|
+
- Gemfile.rails-4.0.x
|
130
|
+
- Gemfile.rails-4.1.x
|
131
|
+
- Gemfile.rails-head
|
128
132
|
- Guardfile
|
129
133
|
- LICENSE.txt
|
130
134
|
- README.md
|
@@ -172,7 +176,6 @@ files:
|
|
172
176
|
- spec/dummy/config/initializers/wrap_parameters.rb
|
173
177
|
- spec/dummy/config/locales/en.yml
|
174
178
|
- spec/dummy/config/routes.rb
|
175
|
-
- spec/dummy/db/test.sqlite3
|
176
179
|
- spec/dummy/lib/assets/.keep
|
177
180
|
- spec/dummy/log/test.log
|
178
181
|
- spec/dummy/public/404.html
|
@@ -215,7 +218,6 @@ test_files:
|
|
215
218
|
- spec/controllers/customized_on_invalid_session_controller_spec.rb
|
216
219
|
- spec/support/application_controller.rb
|
217
220
|
- spec/dummy/README.rdoc
|
218
|
-
- spec/dummy/db/test.sqlite3
|
219
221
|
- spec/dummy/public/500.html
|
220
222
|
- spec/dummy/public/favicon.ico
|
221
223
|
- spec/dummy/public/422.html
|
data/spec/dummy/db/test.sqlite3
DELETED
File without changes
|