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 +4 -4
- data/README.md +34 -3
- data/Rakefile +3 -6
- data/lib/angular_rails_csrf.rb +2 -0
- data/lib/angular_rails_csrf/concern.rb +38 -12
- data/lib/angular_rails_csrf/railtie.rb +2 -0
- data/lib/angular_rails_csrf/version.rb +3 -1
- data/test/angular_rails_csrf_exception_test.rb +4 -2
- data/test/angular_rails_csrf_skip_test.rb +14 -0
- data/test/angular_rails_csrf_test.rb +85 -19
- data/test/dummy/app/assets/config/manifest.js +4 -0
- data/test/dummy/app/controllers/api_controller.rb +7 -0
- data/test/dummy/app/controllers/application_controller.rb +9 -2
- data/test/dummy/app/controllers/exclusions_controller.rb +6 -2
- data/test/dummy/config.ru +3 -1
- data/test/dummy/config/application.rb +5 -4
- data/test/dummy/config/boot.rb +3 -1
- data/test/dummy/config/environment.rb +3 -1
- data/test/dummy/config/routes.rb +4 -0
- data/test/dummy/log/test.log +1381 -0
- data/test/test_helper.rb +16 -3
- metadata +70 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f310f1cd6d8278d5b0a669bb7123a802b68cfdcda1ec443de09b85a757c10eb3
|
4
|
+
data.tar.gz: 4eaec5f2dcf567dd0b8cf2c7374a0a1b95c965f3226204b729cfff7aa765f6ea
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e44c7ab3ebfefe2dbcbe184243d03ba5f0eac7aa7b0b760aaf7cbd878468394cc519e7433bf72ef2dfd8ffcf3e0e099a18290035b86760e5143bf11a3236f42f
|
7
|
+
data.tar.gz: 57ce6512718fd75d5fbeb82e54577314b448b3c1b9e4714ad9b8b1021033501bc481e2d70a4f1b3c930727aa020a827535a357319e7a8928ba10e158d6ce8478
|
data/README.md
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
[](https://badge.fury.io/rb/angular_rails_csrf)
|
4
4
|
[](https://travis-ci.org/jsanders/angular_rails_csrf)
|
5
|
+
[](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
|
-
|
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
|
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
|
data/lib/angular_rails_csrf.rb
CHANGED
@@ -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
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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
|
-
|
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 '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
|
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
|
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
|
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
|
19
|
-
|
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
|
26
|
-
|
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
|
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
|
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
|
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
|
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
|
119
|
+
def header_to(value)
|
56
120
|
@request.headers['X-XSRF-TOKEN'] = value
|
57
121
|
end
|
58
122
|
|
59
|
-
def assert_valid_cookie(name
|
60
|
-
|
61
|
-
|
62
|
-
|
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
|
131
|
+
def config.angular_rails_csrf_cookie_name
|
132
|
+
'CUSTOM-COOKIE-NAME'
|
133
|
+
end
|
70
134
|
yield
|
71
135
|
ensure
|
72
|
-
|
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
|
@@ -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
|
5
|
-
|
6
|
+
def index
|
7
|
+
head :ok
|
8
|
+
end
|
9
|
+
|
10
|
+
def create
|
11
|
+
head :ok
|
12
|
+
end
|
6
13
|
end
|
data/test/dummy/config.ru
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require File.expand_path('boot', __dir__)
|
4
|
+
|
5
|
+
require 'action_controller/railtie'
|
4
6
|
|
5
7
|
Bundler.require(:default, Rails.env)
|
6
|
-
require
|
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
|
-
|