cookies_manager 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/.rvmrc CHANGED
@@ -1 +1 @@
1
- rvm use 1.8.7@cookies_manager --create
1
+ rvm use 1.9.2@cookies_manager --create
@@ -1,3 +1,8 @@
1
+ 0.3.0 (May 04, 2012)
2
+
3
+ * Adapted to Ruby 1.9.3 and Rails 3.2.3
4
+ * Now using signed cookies to prevent tampering
5
+
1
6
  0.2.x (Nov 08, 2011)
2
7
 
3
8
  * Simplified usage: in reads operations, unpacks the data by default
data/Gemfile CHANGED
@@ -2,3 +2,4 @@ source "http://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in cookies_manager.gemspec
4
4
  gemspec
5
+
@@ -4,16 +4,20 @@ CookiesManager is a simple tool that provides a convenient way to manage any kin
4
4
  - The data you store in the cookies is automatically marshalled, zipped, and base64-encoded.
5
5
  - The data you ask to retrieve from the cookies is read from a cache, which spares the need for reverse transformation as long as the cache is in sync with the cookies. When the cache is out of sync, CookiesManager automatically resynchronizes the cache by reading the data from the cookies and processing reverse transformation (base64-decoding, unzipping, unmarshalling).
6
6
  - The cache is defined somehow at the controller instance level. Thus, each HTTP request has its own cache for its whole lifetime.
7
+ - Cookies are signed to prevent tampering.
7
8
 
8
9
  == Installation
9
10
 
10
- In <b>Rails 2</b>, add this to your environment.rb file.
11
+ In <b>Rails 3</b>, add this to your environment.rb file.
11
12
 
12
- config.gem "cookies_manager"
13
-
14
- Alternatively, you can install it as a plugin:
15
-
16
- script/plugin install git@github.com:RStrike/CookiesManager.git
13
+ gem 'cookies_manager'
14
+
15
+ Then set a secret token in your config/secret_token.rb file.
16
+ # Your secret key for verifying the integrity of signed cookies.
17
+ # If you change this key, all old signed cookies will become invalid!
18
+ # Make sure the secret is at least 30 characters and all random,
19
+ # no regular words or you'll be exposed to dictionary attacks.
20
+ <Your Application Name>::Application.config.secret_token = 'your secret token'
17
21
 
18
22
  == Getting started
19
23
 
@@ -55,16 +59,20 @@ than the session length, such as user display preferences.
55
59
 
56
60
  == Important notes
57
61
 
62
+ - Do not store sensitive data in your cookies. Although all cookies are signed, those are not encrypted.
58
63
  - If your application is multi-threaded, using CookiesManager is threadsafe as long as your threads refer the same CookiesManager instance.
59
64
  - Unless you know what you are really doing, do not store large data in your cookies, since these are included in HTTP request headers.
60
65
  - Do not store model objects in cookies (nor in session). For more explanation, check {Ryan Bates's screencast about dangers of model in session}[http://railscasts.com/episodes/13-dangers-of-model-in-session].
66
+ - If you really need to deal with an object, I recommend to store it in your database and set a simple related token in your the cookies.
61
67
 
62
68
  == Running the tests
63
69
 
64
70
  RSpec 2 is used for testing. To get the specs running, check {spec/README}[https://github.com/RStrike/CookiesManager/blob/master/spec/README.rdoc].
65
71
 
66
- == Coming up next
67
- - Adapt the gem to support Ruby 1.9.x and Rails 3.
72
+ == Compatibility
73
+ Tested on:
74
+ - Ruby 1.9.3
75
+ - Rails 3.2.3
68
76
 
69
77
  == Changes
70
78
 
@@ -18,8 +18,8 @@ Gem::Specification.new do |s|
18
18
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
19
  s.require_paths = ["lib"]
20
20
 
21
- s.add_dependency 'actionpack', '~> 2.3.14'
22
- s.add_dependency 'aquarium', '~> 0.4.4'
23
- s.add_development_dependency 'rspec', '~> 2.7.0'
21
+ s.add_dependency 'actionpack', '~> 3.2.3'
22
+ s.add_dependency 'activesupport', '~> 3.2.3'
23
+ s.add_development_dependency 'rspec', '~> 2.9.0'
24
24
  s.add_development_dependency 'rr', '~> 1.0.4'
25
25
  end
@@ -1,9 +1,6 @@
1
- require 'aquarium'
2
-
3
1
  module CookiesManager
4
2
  # The base class of CookiesManager
5
3
  class Base
6
- include Aquarium::DSL # AOP Filters are defined at the end of the class
7
4
 
8
5
  attr_accessor :cookies # The cookies hash to be based on
9
6
 
@@ -36,6 +33,7 @@ module CookiesManager
36
33
  # @return [Object] the data associated with the key
37
34
  #
38
35
  def read(key, opts = {})
36
+ opts.symbolize_keys!
39
37
  result = nil
40
38
  getMutex(key).synchronize do
41
39
  result = read_from_cache_or_cookies(key, opts)
@@ -68,12 +66,13 @@ module CookiesManager
68
66
  # @return [Integer] the number of bytes written in the cookies
69
67
  #
70
68
  def write(key, data, opts = {})
69
+ opts.symbolize_keys!
71
70
  unpacked_data = data
72
71
  data = pack(data) unless opts[:skip_pack]
73
72
  result = nil
74
73
  getMutex(key).synchronize do
75
74
  cache[key] ||= {}
76
- result = cookies[key] = {:value => data}.merge(opts) # store the packed data in the cookies hash
75
+ result = cookies.signed[key] = {:value => data}.merge(opts) # store the packed data in the cookies hash
77
76
  cache[key][:unpacked_data] = unpacked_data # store the unpacked data in the cache for fast read in the read method
78
77
  cache[key][:packed_data] = data # store the packed data in the cache for fast change diff in the read method
79
78
  end
@@ -93,6 +92,7 @@ module CookiesManager
93
92
  # @return (see #read)
94
93
  #
95
94
  def delete(key, opts = {})
95
+ opts.symbolize_keys!
96
96
  result = nil
97
97
  getMutex(key).synchronize do
98
98
  result = read_from_cache_or_cookies(key, opts)
@@ -121,7 +121,7 @@ module CookiesManager
121
121
  # reads from the cache if in sync. Otherwise, reads from the cookies and resynchronizes the cache for the given key
122
122
  def read_from_cache_or_cookies(key, opts)
123
123
  result = nil
124
- data_from_cookies = cookies[key]
124
+ data_from_cookies = cookies.signed[key]
125
125
  cache[key] ||= {}
126
126
  if cache[key][:packed_data] == data_from_cookies # checks whether cache is in sync with cookies
127
127
  result = cache[key][:unpacked_data] # reads from cache
@@ -145,28 +145,9 @@ module CookiesManager
145
145
  end
146
146
 
147
147
  def unpack(data)
148
- Marshal.load(ActiveSupport::Gzip.decompress(Base64.decode64(data)))
148
+ Marshal.load(ActiveSupport::Gzip.decompress(Base64.decode64(data))) unless data.nil?
149
149
  end
150
150
 
151
- #=================#
152
- #== AOP Filters ==#
153
- #=================#
154
-
155
- # Since Aquarium sets the arities of observered methods to -1, we need to save the methods arities in a hash declared as a class variable
156
- self.instance_methods(false).each { |method| (@method_arities ||= {})[method.to_sym] = instance_method(method).arity }
157
-
158
- around :methods => [:read, :write, :delete] do |join_point, object, *args|
159
- key = (args[0] = args[0].to_s) # we should stick with string keys since the cookies hash does not support indifferent access (i.e. :foo and "foo" are different keys), although this has changed in rails 3.1
160
- opts = args[last_arg_index(join_point.method_name)] # retrieve the options arg (last argument)
161
- opts.symbolize_keys! if opts.is_a?(Hash)
162
- join_point.proceed(*args)
163
- end
164
-
165
- # Returns the index of the last arg in the method signature
166
- def self.last_arg_index(method_name)
167
- instance_eval { @method_arities[method_name] }.abs - 1
168
- end
169
-
170
151
  end
171
152
 
172
153
  end
@@ -4,26 +4,40 @@ module CookiesManager
4
4
  # It is automatically extended by all controllers.
5
5
  module ControllerAdditions
6
6
 
7
- # Sets up a before filter that creates a new CookiesManager into an instance variable,
8
- # which is made available to all views through the +cookies_manager+ helper method.
9
- #
10
- # You can call this method on your controller class as follows:
11
- #
12
- # class YourController < ApplicationController
13
- # load_cookies_manager
14
- #
15
- def load_cookies_manager
16
- self.before_filter do |controller|
17
- # defines a CookiesManager instance variable, based on the cookies hash
18
- controller.instance_variable_set(:@_cookies_manager, CookiesManager::Base.new(controller.instance_eval { cookies } ))
19
- # wraps the instance variable in a the +cookies_manager+ method
20
- define_method :cookies_manager, proc { controller.instance_variable_get(:@_cookies_manager) }
21
- # makes the +cookies_manager+ method available to all views as a helper method
22
- helper_method :cookies_manager
7
+ module ClassMethods
8
+ # Sets up a before filter that creates a new CookiesManager into an instance variable,
9
+ # which is made available to all views through the +cookies_manager+ helper method.
10
+ #
11
+ # You can call this method on your controller class as follows:
12
+ #
13
+ # class YourController < ApplicationController
14
+ # load_cookies_manager
15
+ #
16
+ def load_cookies_manager
17
+ before_filter :build_cookies_manager
23
18
  end
24
19
  end
20
+
21
+ # Builds a cookies manager as a controller instance variable, made available to all views
22
+ def build_cookies_manager
23
+ # defines a CookiesManager instance variable, based on the cookies hash
24
+ @_cookies_manager = CookiesManager::Base.new(cookies)
25
+ # wraps the instance variable in the +cookies_manager+ instance method
26
+ define_singleton_method :cookies_manager, proc { @_cookies_manager }
27
+ # makes the +cookies_manager+ method available to all views as a helper method
28
+ self.class.helper_method :cookies_manager
29
+ end
30
+
31
+ def self.included(base)
32
+ base.extend ClassMethods
33
+ end
34
+
25
35
  end
26
36
  end
27
37
 
28
- # Automatically add all ControllerAdditions methods to controller classes as class methods
29
- ActionController::Base.extend CookiesManager::ControllerAdditions
38
+ # Automatically add all ControllerAdditions methods to controllers
39
+ if defined? ActionController
40
+ ActionController::Base.class_eval do
41
+ include CookiesManager::ControllerAdditions
42
+ end
43
+ end
@@ -1,3 +1,3 @@
1
1
  module CookiesManager
2
- VERSION = "0.2.2"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -8,14 +8,7 @@
8
8
  bundle
9
9
  rake
10
10
 
11
- Some of the specs, covering race conditions in multithreaded contexts, pause in purpose some threads for a few seconds through AOP to test critical sections. Those "slow" tests can be easily skipped by passing +skip_slow+ as an argument to the +rake+ command:
12
-
13
- rake skip_slow
14
-
15
- However, I recommend running all the specs whenever you change the code.
16
-
17
11
  == Ruby versions
18
12
 
19
- Currently, the specs require Ruby 1.8.7.
13
+ Currently, the specs require Ruby 1.9.3
20
14
 
21
- Ruby 1.9.x support is coming up next.
@@ -12,7 +12,6 @@ module MacrosForCookiesManager
12
12
  end
13
13
 
14
14
  include MacrosForCookiesManager
15
- include Aquarium::Aspects
16
15
 
17
16
  describe CookiesManager::Base do
18
17
  before(:all) do
@@ -26,41 +25,37 @@ describe CookiesManager::Base do
26
25
  describe "#write" do
27
26
  describe "#write + pack data" do
28
27
  context "when write complex data" do
29
- before { @bytesize = subject.write('my_key', @complex_data) }
30
- specify { @bytesize.should == pack(@complex_data).bytesize }
31
- specify { unpack(cookies['my_key']).should eql @complex_data }
28
+ before { subject.write('my_key', @complex_data) }
29
+ specify { unpack(cookies.signed['my_key']).should eql @complex_data }
32
30
  end
33
31
  context "when write nil value" do
34
- before { @bytesize = subject.write('my_key', nil) }
35
- specify { @bytesize.should == pack(nil).bytesize }
36
- specify { unpack(cookies['my_key']).should be_nil}
32
+ before { subject.write('my_key', nil) }
33
+ specify { unpack(cookies.signed['my_key']).should be_nil}
37
34
  end
38
35
  end
39
36
  describe "write without packing data" do
40
37
  context "when write simple data" do
41
- before { @bytesize = subject.write('my_key', @simple_data, :skip_pack => true) }
42
- specify { @bytesize.should == @simple_data.bytesize }
43
- specify { cookies['my_key'].should eql @simple_data }
38
+ before { subject.write('my_key', @simple_data, :skip_pack => true) }
39
+ specify { cookies.signed['my_key'].should eql @simple_data }
44
40
  end
45
41
  context "when write nil value" do
46
- before { @bytesize = subject.write('my_key', nil, :skip_pack => true) }
47
- specify { @bytesize.should == 0 }
48
- specify { cookies['my_key'].should be_nil}
42
+ before { subject.write('my_key', nil, :skip_pack => true) }
43
+ specify { cookies.signed['my_key'].should be_nil}
49
44
  end
50
45
  end
51
46
  describe "#write + set expiration date" do
52
47
  before { subject.write(@key = 'my_key', @complex_data, :expires => (@expiration_date = 2.hours.from_now)) }
53
- specify { Time.parse(cookies.controller.response["Set-Cookie"].select { |cookie_str| cookie_str =~ /\A#{@key}=/ }.last[/expires=(.*?)(;|\Z)/, 1]).to_i.should == @expiration_date.to_i } #parse the expiration date from the cookies string in the response header using some simple non-greedy regex and convert it to epochs to test the time equality. This conversion technique works only for timestamps between 1901-12-13 and 2038-01-19, but is acceptable for our tests.
48
+ specify { cookies.instance_eval {@set_cookies['my_key'][:expires]}.should == @expiration_date }
54
49
  end
55
50
  describe "#write with nil key" do
56
51
  before { subject.write(nil, @complex_data) }
57
- specify { unpack(cookies[nil]).should eql @complex_data }
58
- specify { unpack(cookies['']).should eql @complex_data }
52
+ specify { unpack(cookies.signed[nil]).should eql @complex_data }
53
+ specify { unpack(cookies.signed['']).should eql @complex_data }
59
54
  end
60
55
  describe "#write with empty string key" do
61
56
  before { subject.write('', @complex_data) }
62
- specify { unpack(cookies[nil]).should eql @complex_data }
63
- specify { unpack(cookies['']).should eql @complex_data }
57
+ specify { unpack(cookies.signed[nil]).should eql @complex_data }
58
+ specify { unpack(cookies.signed['']).should eql @complex_data }
64
59
  end
65
60
  end
66
61
 
@@ -85,20 +80,20 @@ describe CookiesManager::Base do
85
80
  end
86
81
  describe "#read some data previously stored directly into the cookies hash" do
87
82
  context "when reading non-nil data" do
88
- before { cookies['my_key'] = {:value => @complex_data} }
83
+ before { cookies.signed['my_key'] = {:value => @complex_data} }
89
84
  specify { subject.read('my_key', :skip_unpack => true).should eql @complex_data }
90
85
  end
91
86
  context "when reading a nil value" do
92
- before { cookies['my_key'] = {:value => nil } }
87
+ before { cookies.signed['my_key'] = {:value => nil } }
93
88
  specify { subject.read('my_key').should be_nil }
94
89
  end
95
90
  context "when reading some data stored with a nil key" do
96
- before { cookies[nil] = {:value => @complex_data} }
91
+ before { cookies.signed[nil] = {:value => @complex_data} }
97
92
  specify { subject.read(nil, :skip_unpack => true).should eql @complex_data }
98
93
  specify { subject.read('', :skip_unpack => true).should eql @complex_data }
99
94
  end
100
95
  context "when reading some data stored with an empty string key" do
101
- before { cookies[''] = {:value => @complex_data} }
96
+ before { cookies.signed[''] = {:value => @complex_data} }
102
97
  specify { subject.read(nil, :skip_unpack => true).should eql @complex_data }
103
98
  specify { subject.read('', :skip_unpack => true).should eql @complex_data }
104
99
  end
@@ -107,7 +102,7 @@ describe CookiesManager::Base do
107
102
  context "when read some simple data" do
108
103
  before do
109
104
  subject.write('my_key', @simple_data)
110
- cookies['my_key'] = {:value => (@new_simple_data = "some new data")}
105
+ cookies.signed['my_key'] = {:value => (@new_simple_data = "some new data")}
111
106
  end
112
107
  specify { subject.read('my_key', :skip_unpack => true).should eql @new_simple_data }
113
108
  end
@@ -115,7 +110,7 @@ describe CookiesManager::Base do
115
110
  before do
116
111
  subject.write('my_key', @complex_data)
117
112
  @new_complex_data = @complex_data.merge(:some_new_item => 'it modifies the data')
118
- cookies['my_key'] = {:value => pack(@new_complex_data)}
113
+ cookies.signed['my_key'] = {:value => pack(@new_complex_data)}
119
114
  end
120
115
  specify { subject.read('my_key').should eql @new_complex_data }
121
116
  specify { unpack(subject.read('my_key', :skip_unpack => true)).should eql @new_complex_data } #if :skip_unpack option is set, we need to unpack the data manually
@@ -125,7 +120,7 @@ describe CookiesManager::Base do
125
120
  shared_examples_for "reading with indifferent access key" do
126
121
  specify { subject.read(:my_key).should eql @complex_data }
127
122
  specify { subject.read('my_key').should eql @complex_data }
128
- specify { unpack(cookies['my_key']).should eql @complex_data }
123
+ specify { unpack(cookies.signed['my_key']).should eql @complex_data }
129
124
  end
130
125
  context "when data has been written with a key of type symbol" do
131
126
  before { subject.write(:my_key, @complex_data) }
@@ -150,7 +145,7 @@ describe CookiesManager::Base do
150
145
  it_should_behave_like "when deleting existing data"
151
146
  end
152
147
  context "when data has been previously stored directly into the cookies hash" do
153
- before { cookies['my_key'] = {:value => @complex_data} }
148
+ before { cookies.signed['my_key'] = {:value => @complex_data} }
154
149
  it_should_behave_like "when deleting existing data"
155
150
  end
156
151
  end
@@ -186,7 +181,7 @@ describe CookiesManager::Base do
186
181
 
187
182
  describe "#symbol/string indifferent keys in options hash" do
188
183
  describe "#read" do
189
- before { cookies['my_key'] = pack(@complex_data) }
184
+ before { cookies.signed['my_key'] = pack(@complex_data) }
190
185
  specify { subject.read('my_key', :unpack => true).should eql @complex_data }
191
186
  specify { subject.read('my_key', 'unpack' => true).should eql @complex_data }
192
187
  end
@@ -195,11 +190,11 @@ describe CookiesManager::Base do
195
190
  subject.write('key1', @simple_data, :skip_pack => true)
196
191
  subject.write('key2', @simple_data, 'skip_pack' => true)
197
192
  end
198
- specify { cookies['key1'].should eql @simple_data }
199
- specify { cookies['key2'].should eql @simple_data }
193
+ specify { cookies.signed['key1'].should eql @simple_data }
194
+ specify { cookies.signed['key2'].should eql @simple_data }
200
195
  end
201
196
  describe "#delete" do
202
- before { cookies['my_key'] = pack(@complex_data) }
197
+ before { cookies.signed['my_key'] = pack(@complex_data) }
203
198
  specify { subject.delete('my_key', :unpack => true).should eql @complex_data }
204
199
  specify { subject.delete('my_key', 'unpack' => true).should eql @complex_data }
205
200
  end
@@ -217,16 +212,16 @@ describe CookiesManager::Base do
217
212
  describe "#cache in sync with the cookies" do
218
213
  describe "#accessing some data that has been read at least once by the CookiesManager" do
219
214
  before do
220
- cookies['my_key'] = pack(@complex_data)
221
- subject.read('my_key').should eql @complex_data # this should store the data in the cache, thus sparing the need for future unmarshalling
222
- dont_allow(Marshal).load # this makes sure unmarshalling is never done (i.e. the cache is used)
215
+ cookies.signed['my_key'] = pack(@complex_data)
216
+ subject.read('my_key').should eql @complex_data # this should store the data in the cache, thus sparing the need for future unpacking
217
+ dont_allow(subject).unpack # this makes sure unpacking is never done (i.e. the cache is used)
223
218
  end
224
219
  it_should_behave_like 'when accessing the data'
225
220
  end
226
221
  describe "#accessing some data that has been written through the CookiesManager" do
227
222
  before do
228
- subject.write('my_key', @complex_data) # this should store the data in the cache, thus sparing the need for future unmarshalling
229
- dont_allow(Marshal).load # this makes sure unmarshalling is never done (i.e. the cache is used)
223
+ subject.write('my_key', @complex_data) # this should store the data in the cache, thus sparing the need for future unpacking
224
+ dont_allow(subject).unpack # this makes sure unpacking is never done (i.e. the cache is used)
230
225
  end
231
226
  it_should_behave_like 'when accessing the data'
232
227
  end
@@ -234,129 +229,30 @@ describe CookiesManager::Base do
234
229
  describe "#cache out of sync" do
235
230
  before do
236
231
  subject.write('my_key', @original_data = ['my', 'original', 'array'])
237
- cookies['my_key'] = pack(@complex_data) # this causes the cache to be out of sync, thus causing future reads to unmarshall the data from the cookies
238
- mock.proxy(Marshal).load.with_any_args # this makes sure unmarshalling is invoked
232
+ cookies.signed['my_key'] = pack(@complex_data) # this causes the cache to be out of sync, thus causing future reads to unmarshall the data from the cookies
233
+ mock.proxy(subject).unpack.with_any_args # this makes sure unpacking is invoked
239
234
  end
240
235
  it_should_behave_like 'when accessing the data'
241
236
  describe "#automatic cache resynchronization on read" do
242
237
  before do
243
- subject.read('my_key').should eql @complex_data # this causes unmarshalling, and cache resynchronization
244
- dont_allow(Marshal).load # this makes sure we don't unmarshall anymore at this point (i.e. the cache is now in sync and can be used)
238
+ subject.read('my_key').should eql @complex_data # this causes unpacking, and cache resynchronization
239
+ dont_allow(subject).unpack # this makes sure we don't unmarshall anymore at this point (i.e. the cache is now in sync and can be used)
245
240
  end
246
241
  specify { subject.read('my_key').should eql @complex_data}
247
242
  end
248
243
  end
249
244
  end
250
245
 
251
- describe "#multi-threading", :slow do
252
- def print_inside_critical_section(method_name)
253
- p "Inside the critical section, when calling cookies#{method_name}, the #{thread_name} thread pauses for #{sleep(2)} seconds, to make the other thread wait at the entrance of the critical section..."
254
- end
255
-
256
- def print_wait_before_action(action)
257
- p "Before calling ##{action}, wait for #{sleep(1)} seconds to let the #{thread_name} thread lock the critical section..."
258
- end
259
-
260
- def run_thread
261
- Thread.new do
262
- Thread.current["name"] = thread_name
263
- yield
264
- end
265
- end
266
-
267
- def build_aspect(method_name)
268
- Aspect.new :around, :calls_to => method_name, :on_objects => subject.cookies do |join_point, object, *args|
269
- print_inside_critical_section(join_point.method_name) if Thread.current["name"] == thread_name
270
- join_point.proceed
271
- end
272
- end
273
-
274
- before { subject.write('my_key', @original_data = 'original data') }
275
- after { @aspect.unadvise }
276
-
277
- describe "#a thread is reading with a key" do
278
- let(:thread_name) { :reader }
279
- before do
280
- @aspect = build_aspect('[]')
281
- @reader = run_thread { @result = subject.read('my_key') }
282
- end
283
- shared_examples_for 'when another thread wants to access the data with the same key while reading' do
284
- it "should wait until the reader finishes reading" do
285
- @reader.join
286
- @result.should eql @original_data
287
- end
288
- end
289
- context "when another thread wants to write some new data with the same key" do
290
- before do
291
- print_wait_before_action(:write)
292
- subject.write('my_key', @complex_data)
293
- end
294
- it_should_behave_like 'when another thread wants to access the data with the same key while reading'
295
- end
296
- context "when another thread wants to delete with the same key" do
297
- before do
298
- print_wait_before_action(:delete)
299
- subject.delete('my_key')
300
- end
301
- it_should_behave_like 'when another thread wants to access the data with the same key while reading'
302
- end
303
- end
304
- describe "#a thread is writing with a key" do
305
- let(:thread_name) { :writer }
246
+ describe "#cookies tampering" do
247
+ describe "#when tampering a cookie value" do
306
248
  before do
307
- @aspect = build_aspect('[]=')
308
- @writer = run_thread { subject.write('my_key', @complex_data) }
249
+ subject.write('my_key', @complex_data)
250
+ cookies['my_key'] = 'some new value' # note that we intentionally don't call the method 'signed' on the cookies hash in order to tamper the cookies
309
251
  end
310
- shared_examples_for 'when another thread wants to access the data with the same key while writing' do
311
- it 'should wait until the writer finishes writing' do
312
- @writer.join
313
- @result.should eql @complex_data
314
- end
315
- end
316
- context "when another thread wants to read with same key" do
317
- before do
318
- print_wait_before_action(:read)
319
- @result = subject.read('my_key')
320
- end
321
- it_should_behave_like 'when another thread wants to access the data with the same key while writing'
322
- end
323
- context "when another thread wants to delete with the same key" do
324
- before do
325
- print_wait_before_action(:delete)
326
- @result = subject.delete('my_key')
327
- end
328
- it_should_behave_like 'when another thread wants to access the data with the same key while writing'
329
- end
252
+ specify { subject.read('my_key').should be_nil}
330
253
  end
331
- describe "#a thread is deleting with a key" do
332
- let(:thread_name) { :deletor }
333
- before do
334
- @aspect = build_aspect(:delete)
335
- @deletor = run_thread { @delete_result = subject.delete('my_key') }
336
- end
337
- shared_examples_for 'when another thread wants to access the data with the same key while deleting' do
338
- it 'should wait until the deletor finishes deleting' do
339
- @deletor.join
340
- @delete_result.should eql @original_data
341
- end
342
- end
343
- context "when another thread wants to read with the same key" do
344
- before do
345
- print_wait_before_action(:read)
346
- @read_result = subject.read('my_key')
347
- end
348
- it_should_behave_like 'when another thread wants to access the data with the same key while deleting'
349
- specify { @read_result.should be_nil }
350
- end
351
- context "when another thread wants to write with the same key" do
352
- before do
353
- print_wait_before_action(:write)
354
- subject.write('my_key', @complex_data)
355
- end
356
- it_should_behave_like 'when another thread wants to access the data with the same key while deleting'
357
- end
358
- end
359
- end
254
+ end
255
+
360
256
 
361
257
  end
362
258
 
@@ -5,7 +5,7 @@ describe CookiesManager::ControllerAdditions do
5
5
  context "when calling the class method :load_cookies_manager on the controller class" do
6
6
  before do
7
7
  mock(TestController).helper_method(:cookies_manager) # makes sure the :cookies_manager method is declared as a helper (to make it available to the views, for example)
8
- mock(TestController).before_filter { |block| block.call(controller) } # makes sure a before_filter is set AND runs the filter block with our controller as an argument
8
+ mock(TestController).before_filter(:build_cookies_manager) { controller.build_cookies_manager } # makes sure a before_filter is set AND calls the related method on our controller instance
9
9
  TestController.load_cookies_manager # the class method we want to test
10
10
  end
11
11
  it "should create a :cookies_manager instance method" do
@@ -1,6 +1,6 @@
1
- require 'rubygems'
2
1
  require 'bundler/setup'
3
2
  require 'action_controller'
3
+ require 'active_support/all'
4
4
  Bundler.require(:default)
5
5
 
6
6
  RSpec.configure do |config|
@@ -11,19 +11,11 @@ RSpec.configure do |config|
11
11
  config.mock_with :rr
12
12
  end
13
13
 
14
- # A test controller with a cookies hash (this works in rails 2.3.14 but needs to be adapted for versions of rails >= 3.x.x)
15
14
  class TestController < ActionController::Base
16
15
 
17
- def request
18
- @request ||= ActionController::Request.new('test')
19
- end
20
-
21
- def response
22
- @response ||= ActionController::Response.new
23
- end
24
-
25
- def initialize
26
- self.cookies = ActionController::CookieJar.new(self)
16
+ def initialize(*args)
17
+ super(*args)
18
+ self.cookies = ActionDispatch::Cookies::CookieJar.new('b57121a9239fe9e55d46c534c7af7218')
27
19
  end
28
20
 
29
21
  protected
metadata CHANGED
@@ -1,13 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cookies_manager
3
3
  version: !ruby/object:Gem::Version
4
- hash: 19
5
4
  prerelease:
6
- segments:
7
- - 0
8
- - 2
9
- - 2
10
- version: 0.2.2
5
+ version: 0.3.0
11
6
  platform: ruby
12
7
  authors:
13
8
  - Christophe Levand
@@ -15,7 +10,7 @@ autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
12
 
18
- date: 2011-11-08 00:00:00 +01:00
13
+ date: 2012-05-06 00:00:00 +02:00
19
14
  default_executable:
20
15
  dependencies:
21
16
  - !ruby/object:Gem::Dependency
@@ -26,28 +21,18 @@ dependencies:
26
21
  requirements:
27
22
  - - ~>
28
23
  - !ruby/object:Gem::Version
29
- hash: 31
30
- segments:
31
- - 2
32
- - 3
33
- - 14
34
- version: 2.3.14
24
+ version: 3.2.3
35
25
  type: :runtime
36
26
  version_requirements: *id001
37
27
  - !ruby/object:Gem::Dependency
38
- name: aquarium
28
+ name: activesupport
39
29
  prerelease: false
40
30
  requirement: &id002 !ruby/object:Gem::Requirement
41
31
  none: false
42
32
  requirements:
43
33
  - - ~>
44
34
  - !ruby/object:Gem::Version
45
- hash: 7
46
- segments:
47
- - 0
48
- - 4
49
- - 4
50
- version: 0.4.4
35
+ version: 3.2.3
51
36
  type: :runtime
52
37
  version_requirements: *id002
53
38
  - !ruby/object:Gem::Dependency
@@ -58,12 +43,7 @@ dependencies:
58
43
  requirements:
59
44
  - - ~>
60
45
  - !ruby/object:Gem::Version
61
- hash: 19
62
- segments:
63
- - 2
64
- - 7
65
- - 0
66
- version: 2.7.0
46
+ version: 2.9.0
67
47
  type: :development
68
48
  version_requirements: *id003
69
49
  - !ruby/object:Gem::Dependency
@@ -74,11 +54,6 @@ dependencies:
74
54
  requirements:
75
55
  - - ~>
76
56
  - !ruby/object:Gem::Version
77
- hash: 31
78
- segments:
79
- - 1
80
- - 0
81
- - 4
82
57
  version: 1.0.4
83
58
  type: :development
84
59
  version_requirements: *id004
@@ -124,18 +99,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
124
99
  requirements:
125
100
  - - ">="
126
101
  - !ruby/object:Gem::Version
127
- hash: 3
128
- segments:
129
- - 0
130
102
  version: "0"
131
103
  required_rubygems_version: !ruby/object:Gem::Requirement
132
104
  none: false
133
105
  requirements:
134
106
  - - ">="
135
107
  - !ruby/object:Gem::Version
136
- hash: 3
137
- segments:
138
- - 0
139
108
  version: "0"
140
109
  requirements: []
141
110