rack-policy 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md ADDED
@@ -0,0 +1,14 @@
1
+ 0.2.0 (June 24, 2012)
2
+
3
+ * Fix bug when checking allowed cookie.
4
+ * Refactor cookie clearing to work off request and response cycle.
5
+ * Add cookie deletion.
6
+ * Add response headers handling and fix bug with cache revalidation.
7
+ * Add code comments.
8
+ * Improve documentation with sinatra and rackup examples.
9
+ * Add code example for rails 3 app.
10
+
11
+ 0.1.0 (June 23, 2012)
12
+
13
+ * Add cookie limiter middleware.
14
+ * Add spec tests.
data/Gemfile CHANGED
@@ -1,4 +1,3 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- # Specify your gem's dependencies in rack-policy.gemspec
4
3
  gemspec
data/README.md CHANGED
@@ -30,8 +30,12 @@ By default when the Rack application is loaded no cookies will be set(provided n
30
30
  Rack::Policy::CookieLimiter consent_token: 'allow_me'
31
31
  ```
32
32
 
33
+ The very same `consent_token` is used to toggle the limiter behaviour.
34
+
33
35
  ## Examples
34
36
 
37
+ Adding `Rack::Policy::CookieLimiter` do Rack applications
38
+
35
39
  ### Rails 3.x
36
40
 
37
41
  ```ruby
@@ -47,18 +51,18 @@ And then in your custome controller create actions responsible for setting and u
47
51
 
48
52
  ```ruby
49
53
  class CookiePolicyController < ApplicationController
54
+
50
55
  def allow
51
- cookies[:consent_token] = {
56
+ response.set_cookie 'rack.policy', {
52
57
  value: 'true',
53
58
  expires: 1.year.from_now.utc
54
59
  }
60
+ render nothing: true
55
61
  end
56
62
 
57
63
  def deny
58
- cookies[:consent_token] = {
59
- value: '',
60
- expires: Time.at(0)
61
- }
64
+ response.delete_cookie 'rack.policy'
65
+ render nothing: true
62
66
  end
63
67
  end
64
68
  ```
@@ -69,7 +73,7 @@ end
69
73
  # config/environment
70
74
 
71
75
  Rails::Initializer.run do |config|
72
- config.middleware.use Rack::Policy::Cookie :consent_token => 'rack.policy'
76
+ config.middleware.use Rack::Policy::CookieLimiter consent_token: 'rack.policy'
73
77
  end
74
78
  ```
75
79
 
@@ -77,19 +81,35 @@ Set and unset cookie consent in similar way to Rails 3.x example.
77
81
 
78
82
  ### Sinatra
79
83
 
84
+ For classic style sinatra application do
85
+
80
86
  ```ruby
87
+ #!/usr/bin/env ruby -rubygems
81
88
  require 'sinatra'
82
89
  require 'rack/policy'
83
90
 
84
91
  use Rack::Policy::CookieLimiter consent_token: 'rack.policy'
85
92
 
86
- get('/hello') { "Hello world" }
93
+ get('/') { "Allow cookies to be set? <a href='/allow'>Allow</a>" }
87
94
 
88
- get('/allow') { }
95
+ get('/allow') { response.set_cookie 'rack.policy' }
96
+
97
+ get('/deny') { response.delete_cookie 'rack.policy' }
89
98
  ```
90
99
 
100
+ ### Padrino app
101
+
91
102
  ### Rackup app
92
103
 
104
+ ```ruby
105
+ #!/usr/bin/env rackup
106
+ require 'rack/policy'
107
+
108
+ use Rack::Policy::CookieLimiter consent_token: 'rack.policy'
109
+
110
+ run lambda { |env| [200, {'Content-Type' => 'text/plain'}, "Hello, world!\n"] }
111
+ ```
112
+
93
113
  ## Contributing
94
114
 
95
115
  1. Fork it
@@ -0,0 +1,8 @@
1
+ source :rubygems
2
+
3
+ gem 'actionpack', "~> 3.2"
4
+ gem 'railties', "~> 3.2"
5
+ gem 'rack-policy', :path => File.expand_path('../../../', __FILE__)
6
+ gem 'tzinfo'
7
+ gem 'rack'
8
+ gem 'thin'
@@ -0,0 +1,51 @@
1
+ # RAILS_ENV=production bundle exec rackup -p 3000 -s thin
2
+
3
+ require 'rails'
4
+ require 'rails/all'
5
+ require 'rack/policy'
6
+
7
+ class MyApp < Rails::Application
8
+ routes.append do
9
+ match '/hello' => 'policy#hello'
10
+ match '/allow' => 'policy#allow'
11
+ match '/deny' => 'policy#deny'
12
+ end
13
+
14
+ require 'rack/policy'
15
+
16
+ config.middleware.use Rack::Policy::CookieLimiter
17
+ end
18
+
19
+ class PolicyController < ActionController::Base
20
+
21
+ def hello
22
+ response.set_cookie :custom_cookie, {
23
+ :value => 'illegal cookie',
24
+ :expires => 2.hours.from_now.utc
25
+ }
26
+ render :text => "Cookies #{cookies.inspect}"
27
+ end
28
+
29
+ def allow
30
+ response.set_cookie :cookie_limiter, {
31
+ :value => 'true',
32
+ :expires => 2.hours.from_now.utc
33
+ }
34
+ render :text => "Cookies #{cookies.inspect}"
35
+ end
36
+
37
+ def deny
38
+ response.delete_cookie :cookie_limiter
39
+ render :text => "Cookies #{cookies.inspect}"
40
+ end
41
+ end
42
+
43
+ MyApp.initialize!
44
+
45
+ # Print middleware stack
46
+ Rails.configuration.middleware.each do |middleware|
47
+ puts "use #{middleware.inspect}"
48
+ end
49
+ puts "run #{Rails.application.class.name}.routes"
50
+
51
+ run MyApp
@@ -2,17 +2,20 @@
2
2
 
3
3
  module Rack
4
4
  module Policy
5
+ # This is the class for limiting cookie storage on client machine.
5
6
  class CookieLimiter
6
7
  include ::Rack::Utils
7
8
 
9
+ HTTP_COOKIE = "HTTP_COOKIE".freeze
8
10
  SET_COOKIE = "Set-Cookie".freeze
9
11
  CACHE_CONTROL = "Cache-Control".freeze
10
12
  CONSENT_TOKEN = "cookie_limiter".freeze
11
13
 
12
- attr_reader :app
13
- attr_reader :options
14
- attr_accessor :headers
14
+ attr_reader :app, :options
15
+ attr_accessor :status, :headers, :body
15
16
 
17
+ # @option options [String] :consent_token
18
+ #
16
19
  def initialize(app, options={})
17
20
  @app, @options = app, options
18
21
  end
@@ -26,23 +29,41 @@ module Rack
26
29
  end
27
30
 
28
31
  def call(env)
29
- status, headers, body = app.call(env)
30
- self.headers = headers
31
- clear_cookies! unless allowed?
32
- [status, headers, body]
32
+ self.status, self.headers, self.body = @app.call(env)
33
+ request = Rack::Request.new(env)
34
+ response = Rack::Response.new body, status, headers
35
+ clear_cookies!(request, response) unless allowed?(request)
36
+ finish(env)
33
37
  end
34
38
 
35
- def allowed?
36
- if parse_cookies.has_key?(consent_token)
39
+ # Returns `false` if the cookie policy disallows cookie storage
40
+ # for a given request, or `true` otherwise.
41
+ #
42
+ def allowed?(request)
43
+ if ( request.cookies.has_key?(consent_token.to_s) ||
44
+ parse_cookies.has_key?(consent_token.to_s) )
37
45
  true
38
46
  else
39
47
  false
40
48
  end
41
49
  end
42
50
 
51
+ # Finish http response with proper headers
52
+ def finish(env)
53
+ if [204, 304].include?(status.to_i)
54
+ headers.delete "Content-Type"
55
+ [status.to_i, headers, []]
56
+ elsif env['REQUEST_METHOD'] == 'HEAD'
57
+ [status.to_i, headers, []]
58
+ else
59
+ [status.to_i, headers, body]
60
+ end
61
+ end
62
+
43
63
  protected
44
64
 
45
65
  # Returns the response cookies converted to Hash
66
+ #
46
67
  def parse_cookies
47
68
  cookies = {}
48
69
  if header = headers[SET_COOKIE]
@@ -57,9 +78,16 @@ module Rack
57
78
  cookies
58
79
  end
59
80
 
60
- def clear_cookies!
81
+ def clear_cookies!(request, response)
82
+ cookies = parse_cookies
61
83
  headers.delete(SET_COOKIE)
84
+ request.env.delete(HTTP_COOKIE)
62
85
  revalidate_cache!
86
+
87
+ cookies.merge(request.cookies).each do |key, value|
88
+ response.delete_cookie key.to_sym
89
+ end
90
+
63
91
  headers
64
92
  end
65
93
 
@@ -71,6 +99,10 @@ module Rack
71
99
  ::Rack::Utils.set_cookie_header!(headers, key, value)
72
100
  end
73
101
 
102
+ def delete_cookie(key, value)
103
+ ::Rack::Utils.delete_cookie_header!(headers, key, value)
104
+ end
105
+
74
106
  end # CookieLimiter
75
107
  end # Policy
76
108
  end # Rack
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Rack
4
4
  module Policy
5
- VERSION = "0.1.0"
5
+ VERSION = "0.2.0"
6
6
  end
7
7
  end
@@ -7,21 +7,36 @@ describe Rack::Policy::CookieLimiter do
7
7
  last_response.body.should == 'ok'
8
8
  end
9
9
 
10
- it "does not meter where the middleware is inserted"
10
+ it "does not meter where the middleware is inserted" do
11
+ mock_app {
12
+ use Rack::Policy::CookieLimiter
13
+ use Rack::Session::Cookie, :key => 'app.session', :path => '/'
14
+ run DummyApp
15
+ }
16
+ get '/'
17
+ last_response.should be_ok
18
+ last_response.headers['Set-Cookie'].should be_nil
19
+ end
11
20
 
12
21
  context 'no consent' do
13
22
  it 'removes cookie session header' do
14
- mock_app with_headers('Set-Cookie' => "google=bot")
15
- get '/'
23
+ mock_app {
24
+ use Rack::Policy::CookieLimiter
25
+ run DummyApp
26
+ }
27
+ request '/'
16
28
  last_response.should be_ok
17
29
  last_response.headers['Set-Cookie'].should be_nil
18
30
  end
19
31
 
20
32
  it 'revalidates caches' do
21
- mock_app with_headers('Set-Cookie' => "google=bot")
22
- get '/'
33
+ mock_app {
34
+ use Rack::Policy::CookieLimiter
35
+ run DummyApp
36
+ }
37
+ request '/'
23
38
  last_response.should be_ok
24
- last_response.headers['Cache-control'].should_not be_nil
39
+ last_response.headers['Cache-Control'].should =~ /must-revalidate/
25
40
  end
26
41
  end
27
42
 
@@ -46,4 +61,15 @@ describe Rack::Policy::CookieLimiter do
46
61
  end
47
62
  end
48
63
 
64
+ context 'finish response' do
65
+ it 'returns correct response for head request' do
66
+ mock_app {
67
+ use Rack::Policy::CookieLimiter
68
+ run DummyApp
69
+ }
70
+ head '/'
71
+ last_response.should be_ok
72
+ end
73
+ end
74
+
49
75
  end # Rack::Policy::CookieLimiter
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-policy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-06-23 00:00:00.000000000 Z
12
+ date: 2012-06-24 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rack
16
- requirement: &2160301740 !ruby/object:Gem::Requirement
16
+ requirement: &2152703340 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '1.1'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *2160301740
24
+ version_requirements: *2152703340
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rack-test
27
- requirement: &2160301320 !ruby/object:Gem::Requirement
27
+ requirement: &2152702840 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *2160301320
35
+ version_requirements: *2152702840
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rspec
38
- requirement: &2160300860 !ruby/object:Gem::Requirement
38
+ requirement: &2152702300 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *2160300860
46
+ version_requirements: *2152702300
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: rake
49
- requirement: &2160300240 !ruby/object:Gem::Requirement
49
+ requirement: &2152701660 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,7 +54,7 @@ dependencies:
54
54
  version: '0'
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *2160300240
57
+ version_requirements: *2152701660
58
58
  description: This is Rack middleware that makes your app compliant with the 'EU ePrivacy
59
59
  Directive'
60
60
  email:
@@ -67,10 +67,13 @@ files:
67
67
  - .rspec
68
68
  - .rvmrc
69
69
  - .travis.yml
70
+ - CHANGELOG.md
70
71
  - Gemfile
71
72
  - LICENSE
72
73
  - README.md
73
74
  - Rakefile
75
+ - examples/rails_3/Gemfile
76
+ - examples/rails_3/rails_3.ru
74
77
  - lib/rack-policy.rb
75
78
  - lib/rack/policy.rb
76
79
  - lib/rack/policy/cookie_limiter.rb