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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c72cb658721f8dedcdc8981612407748dcd1c3bb
4
- data.tar.gz: ce5f11294df1af510cf1c8d95b948217e81b98a7
3
+ metadata.gz: 0eb78e7fc8890e920f675060d65d1305ab310caa
4
+ data.tar.gz: dfe7532de11f1b0e4deacd6b5d9476256a6cfb22
5
5
  SHA512:
6
- metadata.gz: 69bd74f1fdce25360acd89e299cbe245c85a2689d42852023a2bbd5da6bdad711feffe21364b0a53e0498263aa347ef34be7e106cd22152382b4762e57f7383a
7
- data.tar.gz: 89c73e22308498ead5d605ad9dd6382b8ef0b463030f7a2a00a600c4debc1908f11d6f12830237b5507bc42b6d7560b2d22d0051a50c6a6055e1546c813ae419
6
+ metadata.gz: c9f94142388dd71adff1c529b0b43c6216f4f4819afca102cedbc523693ddd253b54dad9642c44229ea25ba730b44c08b1769a560eefdd6ae08aa5cd81c8f54c
7
+ data.tar.gz: 5723a7adfe0d66d77e4a9a37749f337536a21a8c5bb24109c51b4bb1089ff6e58766b8c51d8f9f07586a8caf88a5c37106c6ba6210ebfc40c6cd7dda34b8d98e
data/.gitignore CHANGED
@@ -4,7 +4,7 @@
4
4
  .config
5
5
  .ruby-*
6
6
  .yardoc
7
- Gemfile.lock
7
+ Gemfile*.lock
8
8
  InstalledFiles
9
9
  _yardoc
10
10
  coverage
@@ -1,7 +1,16 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 1.9.3
4
- - 2.0.0
5
- - 2.1.0
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
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in frikandel.gemspec
4
+ gemspec
5
+
6
+ gem 'rails', '~> 3.2.0'
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in frikandel.gemspec
4
+ gemspec
5
+
6
+ gem 'rails', '~> 4.0.0'
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in frikandel.gemspec
4
+ gemspec
5
+
6
+ gem 'rails', '~> 4.1.0'
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in frikandel.gemspec
4
+ gemspec
5
+
6
+ gem 'rails', 'github' => 'rails/rails'
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$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
- watch('spec/spec_helper.rb') { "spec" }
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
  [![Gem Version](https://badge.fury.io/rb/frikandel.png)](http://badge.fury.io/rb/frikandel)
3
3
  [![Build Status](https://api.travis-ci.org/taktsoft/frikandel.png)](https://travis-ci.org/taktsoft/frikandel)
4
+ [![Code Climate](https://codeclimate.com/github/taktsoft/frikandel.png)](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 `on_expired_session`-method in your controller:
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.0.0 Added IP address binding. Renamed callback from 'on_expired_session' to 'on_invalid_session'.
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
@@ -27,5 +27,5 @@ Gem::Specification.new do |spec|
27
27
  spec.add_development_dependency "guard-rspec"
28
28
  spec.add_development_dependency "pry"
29
29
 
30
- spec.add_dependency "rails", ['>= 3.2.0', '< 5.0']
30
+ spec.add_dependency "rails", [">= 3.2.0", "< 5.0"]
31
31
  end
@@ -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?(session[:ip_address])
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?(ip_address)
29
- current_ip_address == ip_address
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
@@ -1,7 +1,9 @@
1
+ require 'singleton'
2
+
1
3
  module Frikandel
2
4
  class Configuration
3
- include Singleton
4
- extend SingleForwardable
5
+ include ::Singleton
6
+ extend ::SingleForwardable
5
7
 
6
8
  attr_accessor :ttl, :max_ttl
7
9
 
@@ -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) && (session[:ttl] < Frikandel::Configuration.ttl.ago || session[:max_ttl] < Time.now)
13
+ if session.key?(:ttl) && session.key?(:max_ttl) && (reached_ttl? || reached_max_ttl?)
15
14
  on_invalid_session
16
- elsif !session.key?(:ttl) && !session.key?(:max_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.from_now
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
@@ -1,3 +1,3 @@
1
1
  module Frikandel
2
- VERSION = "2.0.0"
2
+ VERSION = "2.1.0"
3
3
  end
@@ -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
- describe BindSessionToIpAddressController do
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
- it "raises an exception if session address and current ip address don't match" do
20
- session[:ip_address] = "1.2.3.4"
21
- controller.should_receive(:on_invalid_session)
17
+ protected
22
18
 
23
- get :home
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[:user_id].should be_blank
33
+ expect(session[:ip_address]).to eql("0.0.0.0")
33
34
  end
34
35
 
35
- it "allows the request to be rendered as normal" do
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
- response.body.should eql("bind test")
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] = "Something"
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] = "Something"
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
- it "holds the session for at least .1 seconds" do
14
- get :home
15
- session[:user_id] = 1337
16
- sleep 0.1
17
- get :home
18
-
19
- session[:user_id].should be_present
20
- session[:user_id].should eq 1337
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
- session[:user_id].should be_blank
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
- request.session[:max_ttl] = 1.minute.ago
37
- get :home
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
- session[:user_id].should be_blank
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
- Frikandel::Configuration.ttl = old_value
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
- session[:user_id].should be_blank
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 "allows the request to be rendered as normal" do
63
- get :home
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
- response.body.should eql("ttl test")
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: db/development.sqlite3
8
+ database: ":memory:"
9
9
  pool: 5
10
- timeout: 5000
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: db/test.sqlite3
17
+ database: ":memory:"
18
18
  pool: 5
19
- timeout: 5000
19
+ timeout: 500
20
20
 
21
21
  production:
22
22
  adapter: sqlite3
23
- database: db/production.sqlite3
23
+ database: ":memory:"
24
24
  pool: 5
25
- timeout: 5000
25
+ timeout: 500
@@ -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 "/customized_controller_home" => "customized_on_invalid_session#home", as: :customized_controller_home
6
- get "/bind_session_to_ip_address_home" => "bind_session_to_ip_address#home"
7
- get "/combined_controller_home" => "combined#home"
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.0.0
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-04-24 00:00:00.000000000 Z
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
File without changes