rack-policy 0.1.0 → 0.2.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.
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