angular_rails_csrf 4.0.0 → 4.4.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
  SHA256:
3
- metadata.gz: fe9fb037133e9bf60dcd922bd29936ab85b8c4c730cba28ac2602703ab24401d
4
- data.tar.gz: 652584f4e71bf641625437f9e5c596fc4d33737fd6381b2d30d9ab64aad00049
3
+ metadata.gz: f310f1cd6d8278d5b0a669bb7123a802b68cfdcda1ec443de09b85a757c10eb3
4
+ data.tar.gz: 4eaec5f2dcf567dd0b8cf2c7374a0a1b95c965f3226204b729cfff7aa765f6ea
5
5
  SHA512:
6
- metadata.gz: fcba72111050df7ee9184c1d12ad1ce1bc9c4f006d8fa51e6cfeaa11180609893fc44e055a1274ef631f7fefbee1afaf73217083a01725d5508ce34f263099c8
7
- data.tar.gz: 5387400b46c17e2e7edf9b426f45dc83763a43f634d4d8a1baccca4a4d09ae910e17392a7d3391cdfa464b6823908586180aba3d72b52fe7890e766c42bceacd
6
+ metadata.gz: e44c7ab3ebfefe2dbcbe184243d03ba5f0eac7aa7b0b760aaf7cbd878468394cc519e7433bf72ef2dfd8ffcf3e0e099a18290035b86760e5143bf11a3236f42f
7
+ data.tar.gz: 57ce6512718fd75d5fbeb82e54577314b448b3c1b9e4714ad9b8b1021033501bc481e2d70a4f1b3c930727aa020a827535a357319e7a8928ba10e158d6ce8478
data/README.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/angular_rails_csrf.svg)](https://badge.fury.io/rb/angular_rails_csrf)
4
4
  [![Build Status](https://travis-ci.org/jsanders/angular_rails_csrf.svg)](https://travis-ci.org/jsanders/angular_rails_csrf)
5
+ [![Test Coverage](https://codecov.io/gh/jsanders/angular_rails_csrf/graph/badge.svg)](https://codecov.io/gh/jsanders/angular_rails_csrf)
5
6
 
6
7
  The AngularJS [ng.$http](http://docs.angularjs.org/api/ng.$http) service has built-in CSRF protection. By default, it looks for a cookie named `XSRF-TOKEN` and, if found, writes its value into an `X-XSRF-TOKEN` header, which the server compares with the CSRF token saved in the user's session.
7
8
 
@@ -9,7 +10,7 @@ This project adds direct support for this scheme to your Rails application witho
9
10
 
10
11
  Note that there is nothing AngularJS specific here, and this will work with any other front-end that implements the same scheme.
11
12
 
12
- *Version 3 supports only Rails 4+ and Ruby 2.3+. If you are still on Rails 3 (2, 1?!), you have to utilize version 2.1.1!*
13
+ Check [version compatibility](https://github.com/jsanders/angular_rails_csrf/wiki/Version-Compatibility) to learn which Rails/Rubies are currently supported.
13
14
 
14
15
  ## Installation
15
16
 
@@ -51,6 +52,36 @@ end
51
52
 
52
53
  If `angular_rails_csrf_domain` is not set, it defaults to `nil`.
53
54
 
55
+ ### Secure Cookie
56
+
57
+ To set a "secure" flag for the cookie, set the `angular_rails_csrf_secure` option to `true`:
58
+
59
+ ```ruby
60
+ # application.rb
61
+ class Application < Rails::Application
62
+ #...
63
+ config.angular_rails_csrf_secure = true
64
+ end
65
+ ```
66
+
67
+ `angular_rails_csrf_secure` defaults to `false`.
68
+
69
+ ### SameSite
70
+
71
+ The SameSite attribute defaults to `:lax`. You can override this in the config:
72
+
73
+ ```ruby
74
+ # application.rb
75
+ class Application < Rails::Application
76
+ #...
77
+ config.angular_rails_csrf_same_site = :strict
78
+ end
79
+ ```
80
+
81
+ **NOTE**: When using `config.angular_rails_csrf_same_site = :none`, this gem automatically sets the cookie to `Secure` (`config.angular_rails_csrf_secure = true`) to comply with [the specifications](https://tools.ietf.org/html/draft-west-cookie-incrementalism-00).
82
+
83
+ Please note that [Safari is known to have issues](https://bugs.webkit.org/show_bug.cgi?id=198181) with SameSite attribute set to `:none`.
84
+
54
85
  ### Exclusions
55
86
 
56
87
  Sometimes you will want to skip setting the XSRF token for certain controllers (for example, when using SSE or ActionCable, as discussed [here](https://github.com/jsanders/angular_rails_csrf/issues/7)):
@@ -58,7 +89,7 @@ Sometimes you will want to skip setting the XSRF token for certain controllers (
58
89
  ```ruby
59
90
  class ExclusionsController < ApplicationController
60
91
  exclude_xsrf_token_cookie
61
-
92
+
62
93
  # your actions here...
63
94
  end
64
95
  ```
@@ -77,6 +108,6 @@ and then
77
108
  $ rake test
78
109
  ```
79
110
 
80
- ## License
111
+ ## License
81
112
 
82
113
  Licensed under the [MIT License](https://github.com/jsanders/angular_rails_csrf/blob/master/LICENSE).
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
4
  require 'bundler/setup'
3
5
  rescue LoadError
@@ -14,19 +16,14 @@ RDoc::Task.new(:rdoc) do |rdoc|
14
16
  rdoc.rdoc_files.include('lib/**/*.rb')
15
17
  end
16
18
 
17
-
18
-
19
-
20
19
  Bundler::GemHelper.install_tasks
21
20
 
22
21
  require 'rake/testtask'
23
22
 
24
23
  Rake::TestTask.new(:test) do |t|
25
- t.libs << 'lib'
26
- t.libs << 'test'
24
+ t.libs = %w[lib test]
27
25
  t.pattern = 'test/**/*_test.rb'
28
26
  t.verbose = false
29
27
  end
30
28
 
31
-
32
29
  task default: :test
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'angular_rails_csrf/railtie'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module AngularRailsCsrf
2
4
  module Concern
3
5
  extend ActiveSupport::Concern
@@ -7,25 +9,49 @@ module AngularRailsCsrf
7
9
  end
8
10
 
9
11
  def set_xsrf_token_cookie
10
- if protect_against_forgery? && !respond_to?(:__exclude_xsrf_token_cookie?)
11
- config = Rails.application.config
12
- domain = config.respond_to?(:angular_rails_csrf_domain) ? config.angular_rails_csrf_domain : nil
13
- cookie_name = config.respond_to?(:angular_rails_csrf_cookie_name) ? config.angular_rails_csrf_cookie_name : 'XSRF-TOKEN'
14
- cookies[cookie_name] = { value: form_authenticity_token, domain: domain }
15
- end
12
+ return unless defined?(protect_against_forgery?) && protect_against_forgery? && !respond_to?(:__exclude_xsrf_token_cookie?)
13
+
14
+ config = Rails.application.config
15
+
16
+ same_site = same_site_from config
17
+ secure = secure_from config
18
+
19
+ cookie_options = {
20
+ value: form_authenticity_token,
21
+ domain: domain_from(config),
22
+ same_site: same_site,
23
+ secure: same_site.eql?(:none) || secure
24
+ }
25
+
26
+ cookie_name = cookie_name_from config
27
+ cookies[cookie_name] = cookie_options
16
28
  end
17
29
 
18
30
  def verified_request?
19
- if respond_to?(:valid_authenticity_token?, true)
20
- super || valid_authenticity_token?(session, request.headers['X-XSRF-TOKEN'])
21
- else
22
- super || form_authenticity_token == request.headers['X-XSRF-TOKEN']
23
- end
31
+ super || valid_authenticity_token?(session, request.headers['X-XSRF-TOKEN'])
32
+ end
33
+
34
+ private
35
+
36
+ def same_site_from(config)
37
+ config.respond_to?(:angular_rails_csrf_same_site) ? config.angular_rails_csrf_same_site : :lax
38
+ end
39
+
40
+ def secure_from(config)
41
+ config.angular_rails_csrf_secure if config.respond_to?(:angular_rails_csrf_secure)
42
+ end
43
+
44
+ def domain_from(config)
45
+ config.respond_to?(:angular_rails_csrf_domain) ? config.angular_rails_csrf_domain : nil
46
+ end
47
+
48
+ def cookie_name_from(config)
49
+ config.respond_to?(:angular_rails_csrf_cookie_name) ? config.angular_rails_csrf_cookie_name : 'XSRF-TOKEN'
24
50
  end
25
51
 
26
52
  module ClassMethods
27
53
  def exclude_xsrf_token_cookie
28
- self.class_eval do
54
+ class_eval do
29
55
  def __exclude_xsrf_token_cookie?
30
56
  true
31
57
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'angular_rails_csrf/concern'
2
4
 
3
5
  module AngularRailsCsrf
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module AngularRailsCsrf
2
- VERSION = '4.0.0'.freeze
4
+ VERSION = '4.4.0'
3
5
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
 
3
5
  class AngularRailsCsrfExceptionTest < ActionController::TestCase
@@ -8,9 +10,9 @@ class AngularRailsCsrfExceptionTest < ActionController::TestCase
8
10
  @correct_token = @controller.send(:form_authenticity_token)
9
11
  end
10
12
 
11
- test "a get does not set the XSRF-TOKEN cookie" do
13
+ test 'a get does not set the XSRF-TOKEN cookie' do
12
14
  get :index
13
15
  assert_not_equal @correct_token, cookies['XSRF-TOKEN']
14
16
  assert_response :success
15
17
  end
16
- end
18
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ class AngularRailsCsrfSkipTest < ActionController::TestCase
6
+ tests ApiController
7
+
8
+ test 'csrf-cookie is not set and no error if protect_against_forgery? is not defined' do
9
+ refute @controller.respond_to?(:protect_against_forgery?)
10
+ get :index
11
+ assert_nil cookies['XSRF-TOKEN']
12
+ assert_response :success
13
+ end
14
+ end
@@ -1,74 +1,140 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
 
3
5
  class AngularRailsCsrfTest < ActionController::TestCase
4
6
  tests ApplicationController
5
7
 
6
- test "a get sets the XSRF-TOKEN cookie but does not require the X-XSRF-TOKEN header" do
8
+ test 'a get sets the XSRF-TOKEN cookie but does not require the X-XSRF-TOKEN header' do
7
9
  get :index
8
10
  assert_valid_cookie
9
11
  assert_response :success
10
12
  end
11
13
 
12
- test "a post raises an error without the X-XSRF-TOKEN header set" do
14
+ test 'a post raises an error without the X-XSRF-TOKEN header set' do
13
15
  assert_raises ActionController::InvalidAuthenticityToken do
14
16
  post :create
15
17
  end
16
18
  end
17
19
 
18
- test "a post raises an error with the X-XSRF-TOKEN header set to the wrong value" do
19
- set_header_to 'garbage'
20
+ test 'a post raises an error with the X-XSRF-TOKEN header set to the wrong value' do
21
+ header_to 'garbage'
20
22
  assert_raises ActionController::InvalidAuthenticityToken do
21
23
  post :create
22
24
  end
23
25
  end
24
26
 
25
- test "a post is accepted if X-XSRF-TOKEN is set properly" do
26
- set_header_to @controller.send(:form_authenticity_token)
27
+ test 'a post is accepted if X-XSRF-TOKEN is set properly' do
28
+ header_to @controller.send(:form_authenticity_token)
27
29
  post :create
28
30
  assert_valid_cookie
29
31
  assert_response :success
30
32
  end
31
33
 
32
- test "the domain is used if present" do
34
+ test 'csrf-cookie is not set if exclusion is enabled' do
35
+ refute @controller.respond_to?(:__exclude_xsrf_token_cookie?)
36
+ @controller.class_eval { exclude_xsrf_token_cookie }
37
+ get :index
38
+ assert_valid_cookie present: false
39
+ assert @controller.__exclude_xsrf_token_cookie?
40
+ assert_response :success
41
+ end
42
+
43
+ test 'the domain is used if present' do
33
44
  config = Rails.application.config
34
- def config.angular_rails_csrf_domain; :all; end
45
+ def config.angular_rails_csrf_domain
46
+ :all
47
+ end
35
48
 
36
49
  get :index
37
50
  assert @response.headers['Set-Cookie'].include?('.test.host')
38
51
  assert_valid_cookie
39
52
  assert_response :success
53
+ ensure
54
+ config.instance_eval('undef :angular_rails_csrf_domain', __FILE__, __LINE__)
55
+ end
56
+
57
+ test 'the secure flag is set if configured' do
58
+ @request.headers['HTTPS'] = 'on'
59
+
60
+ config = Rails.application.config
61
+ config.define_singleton_method(:angular_rails_csrf_secure) { true }
62
+
63
+ get :index
64
+ assert @response.headers['Set-Cookie'].include?('secure')
65
+ assert_valid_cookie
66
+ assert_response :success
67
+ ensure
68
+ @request.headers['HTTPS'] = nil
69
+ config.instance_eval('undef :angular_rails_csrf_secure', __FILE__, __LINE__)
40
70
  end
41
71
 
42
- test "a custom name is used if present" do
72
+ test 'a custom name is used if present' do
43
73
  use_custom_cookie_name do
44
74
  get :index
45
75
  assert @response.headers['Set-Cookie'].include?('CUSTOM-COOKIE-NAME')
46
- assert_valid_cookie('CUSTOM-COOKIE-NAME')
76
+ assert_valid_cookie name: 'CUSTOM-COOKIE-NAME'
47
77
  assert_response :success
48
78
  end
49
79
  end
50
80
 
81
+ test 'same_site is set to Lax by default' do
82
+ get :index
83
+ assert @response.headers['Set-Cookie'].include?('SameSite=Lax')
84
+ assert_valid_cookie
85
+ assert_response :success
86
+ end
87
+
88
+ test 'same_site can be configured' do
89
+ config = Rails.application.config
90
+ config.define_singleton_method(:angular_rails_csrf_same_site) { :strict }
91
+
92
+ get :index
93
+ assert @response.headers['Set-Cookie'].include?('SameSite=Strict')
94
+ assert_valid_cookie
95
+ assert_response :success
96
+ ensure
97
+ config.instance_eval('undef :angular_rails_csrf_same_site', __FILE__, __LINE__)
98
+ end
99
+
100
+ test 'secure is set automatically when same_site is set to none' do
101
+ @request.headers['HTTPS'] = 'on'
102
+
103
+ config = Rails.application.config
104
+ config.define_singleton_method(:angular_rails_csrf_same_site) { :none }
105
+
106
+ get :index
107
+ assert @response.headers['Set-Cookie'].include?('SameSite=None')
108
+ assert @response.headers['Set-Cookie'].include?('secure')
109
+ assert_valid_cookie
110
+ assert_response :success
111
+ ensure
112
+ config.instance_eval('undef :angular_rails_csrf_same_site', __FILE__, __LINE__)
113
+ end
114
+
51
115
  private
52
116
 
53
117
  # Helpers
54
118
 
55
- def set_header_to(value)
119
+ def header_to(value)
56
120
  @request.headers['X-XSRF-TOKEN'] = value
57
121
  end
58
122
 
59
- def assert_valid_cookie(name = 'XSRF-TOKEN')
60
- if @controller.respond_to?(:valid_authenticity_token?, true)
61
- assert @controller.send(:valid_authenticity_token?, session, cookies[name])
62
- else
63
- assert_equal @controller.send(:form_authenticity_token), cookies['XSRF-TOKEN']
64
- end
123
+ def assert_valid_cookie(name: 'XSRF-TOKEN', present: true)
124
+ cookie_valid = @controller.send(:valid_authenticity_token?, session, cookies[name])
125
+ cookie_valid = !cookie_valid unless present
126
+ assert cookie_valid
65
127
  end
66
128
 
67
129
  def use_custom_cookie_name
68
130
  config = Rails.application.config
69
- def config.angular_rails_csrf_cookie_name; 'CUSTOM-COOKIE-NAME'; end
131
+ def config.angular_rails_csrf_cookie_name
132
+ 'CUSTOM-COOKIE-NAME'
133
+ end
70
134
  yield
71
135
  ensure
72
- config.instance_eval('undef :angular_rails_csrf_cookie_name')
136
+ eval <<-RUBY, binding, __FILE__, __LINE__ + 1
137
+ config.instance_eval('undef :angular_rails_csrf_cookie_name')
138
+ RUBY
73
139
  end
74
140
  end
@@ -0,0 +1,4 @@
1
+ //= link_tree ../images
2
+ //= link_tree ../fonts
3
+ //= link_directory ../javascripts .js
4
+ //= link_directory ../stylesheets .css
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ApiController < ActionController::API
4
+ def index
5
+ head :ok
6
+ end
7
+ end
@@ -1,6 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ApplicationController < ActionController::Base
2
4
  protect_from_forgery with: :exception
3
5
 
4
- def index; head :ok; end
5
- def create; head :ok; end
6
+ def index
7
+ head :ok
8
+ end
9
+
10
+ def create
11
+ head :ok
12
+ end
6
13
  end
@@ -1,5 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ExclusionsController < ApplicationController
2
4
  exclude_xsrf_token_cookie
3
5
 
4
- def index; head :ok; end
5
- end
6
+ def index
7
+ head :ok
8
+ end
9
+ end
@@ -1,4 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is used by Rack-based servers to start the application.
2
4
 
3
- require ::File.expand_path('../config/environment', __FILE__)
5
+ require ::File.expand_path('config/environment', __dir__)
4
6
  run Rails.application
@@ -1,9 +1,11 @@
1
- require File.expand_path('../boot', __FILE__)
1
+ # frozen_string_literal: true
2
2
 
3
- require "action_controller/railtie"
3
+ require File.expand_path('boot', __dir__)
4
+
5
+ require 'action_controller/railtie'
4
6
 
5
7
  Bundler.require(:default, Rails.env)
6
- require "angular_rails_csrf"
8
+ require 'angular_rails_csrf'
7
9
 
8
10
  module Dummy
9
11
  class Application < Rails::Application
@@ -12,4 +14,3 @@ module Dummy
12
14
  config.active_support.test_order = :random
13
15
  end
14
16
  end
15
-