aaf-gumboot 1.0.0.pre.alpha.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +3 -0
- data/.rubocop.yml +15 -0
- data/Gemfile +4 -0
- data/Guardfile +18 -0
- data/LICENSE +202 -0
- data/README.md +1069 -0
- data/Rakefile +8 -0
- data/aaf-gumboot.gemspec +42 -0
- data/lib/aaf-gumboot.rb +1 -0
- data/lib/gumboot.rb +5 -0
- data/lib/gumboot/shared_examples/anonymous_controller.rb +17 -0
- data/lib/gumboot/shared_examples/api_constraints.rb +29 -0
- data/lib/gumboot/shared_examples/api_controller.rb +206 -0
- data/lib/gumboot/shared_examples/api_subjects.rb +44 -0
- data/lib/gumboot/shared_examples/application_controller.rb +223 -0
- data/lib/gumboot/shared_examples/database_schema.rb +45 -0
- data/lib/gumboot/shared_examples/foreign_keys.rb +65 -0
- data/lib/gumboot/shared_examples/permissions.rb +45 -0
- data/lib/gumboot/shared_examples/roles.rb +15 -0
- data/lib/gumboot/shared_examples/subjects.rb +29 -0
- data/lib/gumboot/strap.rb +121 -0
- data/lib/gumboot/version.rb +3 -0
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +3 -0
- data/spec/dummy/app/assets/images/.keep +0 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/controllers/api/api_controller.rb +78 -0
- data/spec/dummy/app/controllers/application_controller.rb +64 -0
- data/spec/dummy/app/controllers/concerns/.keep +0 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/.keep +0 -0
- data/spec/dummy/app/models/.keep +0 -0
- data/spec/dummy/app/models/api_subject.rb +23 -0
- data/spec/dummy/app/models/api_subject_role.rb +6 -0
- data/spec/dummy/app/models/concerns/.keep +0 -0
- data/spec/dummy/app/models/permission.rb +7 -0
- data/spec/dummy/app/models/role.rb +11 -0
- data/spec/dummy/app/models/subject.rb +20 -0
- data/spec/dummy/app/models/subject_role.rb +6 -0
- data/spec/dummy/app/views/dynamic_errors/forbidden.html.erb +0 -0
- data/spec/dummy/app/views/dynamic_errors/unauthorized.html.erb +0 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +18 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +5 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +32 -0
- data/spec/dummy/config/environments/production.rb +37 -0
- data/spec/dummy/config/environments/test.rb +33 -0
- data/spec/dummy/config/initializers/assets.rb +4 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +0 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +15 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +9 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +2 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/db/schema.rb +51 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/lib/api_constraints.rb +16 -0
- data/spec/dummy/lib/assets/.keep +0 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/factories/api_subjects.rb +20 -0
- data/spec/factories/permissions.rb +6 -0
- data/spec/factories/roles.rb +5 -0
- data/spec/factories/subjects.rb +24 -0
- data/spec/gumboot/api_constraints_spec.rb +18 -0
- data/spec/gumboot/api_controller_spec.rb +7 -0
- data/spec/gumboot/api_subjects_spec.rb +7 -0
- data/spec/gumboot/application_controller_spec.rb +7 -0
- data/spec/gumboot/foreign_keys_spec.rb +7 -0
- data/spec/gumboot/permissions_spec.rb +7 -0
- data/spec/gumboot/roles_spec.rb +7 -0
- data/spec/gumboot/subjects_spec.rb +7 -0
- data/spec/lib/gumboot/strap_spec.rb +330 -0
- data/spec/spec_helper.rb +45 -0
- metadata +387 -0
data/Rakefile
ADDED
data/aaf-gumboot.gemspec
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'gumboot/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'aaf-gumboot'
|
8
|
+
spec.version = Gumboot::VERSION
|
9
|
+
spec.authors = ['Bradley Beddoes']
|
10
|
+
spec.email = ['bradleybeddoes@aaf.edu.au']
|
11
|
+
spec.summary = 'Kick off subject and API structure for AAF applications'
|
12
|
+
spec.description = 'Provides a set of shared specs and base generators to' \
|
13
|
+
' ensure that all AAF ruby applications follow the' \
|
14
|
+
' same basic structure and implementation for' \
|
15
|
+
' subjects, RESTful APIs and access control.'
|
16
|
+
spec.homepage = 'http://www.aaf.edu.au'
|
17
|
+
spec.license = 'Apache-2.0'
|
18
|
+
|
19
|
+
spec.files = `git ls-files -z`.split("\x0")
|
20
|
+
|
21
|
+
spec.add_dependency 'accession', '~> 1.0'
|
22
|
+
|
23
|
+
spec.add_development_dependency 'bundler', '~> 1.7'
|
24
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
25
|
+
spec.add_development_dependency 'mysql2', '~> 0.3.20'
|
26
|
+
spec.add_development_dependency 'sqlite3'
|
27
|
+
spec.add_development_dependency 'valhammer'
|
28
|
+
spec.add_development_dependency 'factory_girl'
|
29
|
+
spec.add_development_dependency 'faker'
|
30
|
+
spec.add_development_dependency 'rspec-rails'
|
31
|
+
spec.add_development_dependency 'rubocop'
|
32
|
+
spec.add_development_dependency 'simplecov'
|
33
|
+
spec.add_development_dependency 'codeclimate-test-reporter'
|
34
|
+
spec.add_development_dependency 'rails-controller-testing'
|
35
|
+
|
36
|
+
spec.add_development_dependency 'guard'
|
37
|
+
spec.add_development_dependency 'guard-rspec'
|
38
|
+
spec.add_development_dependency 'guard-rubocop'
|
39
|
+
spec.add_development_dependency 'guard-bundler'
|
40
|
+
|
41
|
+
spec.add_development_dependency 'rails', '~> 4.2.6'
|
42
|
+
end
|
data/lib/aaf-gumboot.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'gumboot'
|
data/lib/gumboot.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
RSpec.shared_examples 'Anon controller' do
|
2
|
+
controller(described_class) do
|
3
|
+
def an_action
|
4
|
+
check_access!('required:permission')
|
5
|
+
head :ok
|
6
|
+
end
|
7
|
+
|
8
|
+
def bad_action
|
9
|
+
head :ok
|
10
|
+
end
|
11
|
+
|
12
|
+
def public
|
13
|
+
public_action
|
14
|
+
head :ok
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
RSpec.shared_examples 'API constraints' do
|
2
|
+
context 'AAF shared implementation' do
|
3
|
+
context '#matches?' do
|
4
|
+
context 'with default: false' do
|
5
|
+
subject { described_class.new(version: '1', default: false) }
|
6
|
+
|
7
|
+
it 'is true for a valid request' do
|
8
|
+
expect(subject.matches?(matching_request)).to be_truthy
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'is false for a non-matching request' do
|
12
|
+
expect(subject.matches?(non_matching_request)).to be_falsey
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'with default: true' do
|
17
|
+
subject { described_class.new(version: '1', default: true) }
|
18
|
+
|
19
|
+
it 'is true for a valid request' do
|
20
|
+
expect(subject.matches?(matching_request)).to be_truthy
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'is true for a non-matching request' do
|
24
|
+
expect(subject.matches?(non_matching_request)).to be_truthy
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,206 @@
|
|
1
|
+
require 'gumboot/shared_examples/anonymous_controller'
|
2
|
+
|
3
|
+
RSpec.shared_examples 'API base controller' do
|
4
|
+
context 'AAF shared implementation' do
|
5
|
+
include_examples 'Anon controller'
|
6
|
+
|
7
|
+
before do
|
8
|
+
@routes.draw do
|
9
|
+
get '/anonymous/an_action' => 'api/api#an_action'
|
10
|
+
get '/anonymous/bad_action' => 'api/api#bad_action'
|
11
|
+
get '/anonymous/public' => 'api/api#public'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
it { is_expected.to respond_to(:subject) }
|
16
|
+
|
17
|
+
context '#ensure_authenticated as before_action' do
|
18
|
+
subject { response }
|
19
|
+
let(:json) { JSON.parse(subject.body) }
|
20
|
+
|
21
|
+
context 'no x509 header set by nginx' do
|
22
|
+
before { get :an_action }
|
23
|
+
|
24
|
+
it { is_expected.to have_http_status(:unauthorized) }
|
25
|
+
|
26
|
+
context 'json within response' do
|
27
|
+
it 'has a message' do
|
28
|
+
expect(json['message']).to eq('SSL client failure.')
|
29
|
+
end
|
30
|
+
it 'has an error' do
|
31
|
+
expect(json['error']).to eq('Subject DN')
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'x509 header set to "(null)"' do
|
37
|
+
before do
|
38
|
+
request.env['HTTP_X509_DN'] = '(null)'
|
39
|
+
get :an_action
|
40
|
+
end
|
41
|
+
|
42
|
+
it { is_expected.to have_http_status(:unauthorized) }
|
43
|
+
context 'json within response' do
|
44
|
+
it 'has a message' do
|
45
|
+
expect(json['message']).to eq('SSL client failure.')
|
46
|
+
end
|
47
|
+
it 'has an error' do
|
48
|
+
expect(json['error']).to eq('Subject DN')
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'invalid x509 header set by nginx' do
|
54
|
+
before do
|
55
|
+
request.env['HTTP_X509_DN'] = "Z=#{Faker::Lorem.word}"
|
56
|
+
get :an_action
|
57
|
+
end
|
58
|
+
|
59
|
+
it { is_expected.to have_http_status(:unauthorized) }
|
60
|
+
context 'json within response' do
|
61
|
+
it 'has a message' do
|
62
|
+
expect(json['message']).to eq('SSL client failure.')
|
63
|
+
end
|
64
|
+
it 'has an error' do
|
65
|
+
expect(json['error']).to eq('Subject DN invalid')
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'without a CN component to DN' do
|
71
|
+
before do
|
72
|
+
request.env['HTTP_X509_DN'] = "O=#{Faker::Lorem.word}"
|
73
|
+
get :an_action
|
74
|
+
end
|
75
|
+
|
76
|
+
it { is_expected.to have_http_status(:unauthorized) }
|
77
|
+
context 'json within response' do
|
78
|
+
it 'has a message' do
|
79
|
+
expect(json['message']).to eq('SSL client failure.')
|
80
|
+
end
|
81
|
+
it 'has an error' do
|
82
|
+
expect(json['error']).to eq('Subject CN invalid')
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context 'with a CN that does not represent an APISubject' do
|
88
|
+
before do
|
89
|
+
request.env['HTTP_X509_DN'] = "CN=#{Faker::Lorem.word}/" \
|
90
|
+
"O=#{Faker::Lorem.word}"
|
91
|
+
get :an_action
|
92
|
+
end
|
93
|
+
|
94
|
+
it { is_expected.to have_http_status(:unauthorized) }
|
95
|
+
context 'json within response' do
|
96
|
+
it 'has a message' do
|
97
|
+
expect(json['message']).to eq('SSL client failure.')
|
98
|
+
end
|
99
|
+
it 'has an error' do
|
100
|
+
expect(json['error']).to eq('Subject invalid')
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
context 'with an APISubject that is not functioning' do
|
106
|
+
let(:api_subject) { create :api_subject, enabled: false }
|
107
|
+
|
108
|
+
before do
|
109
|
+
request.env['HTTP_X509_DN'] = "CN=#{api_subject.x509_cn}/" \
|
110
|
+
"O=#{Faker::Lorem.word}"
|
111
|
+
get :an_action
|
112
|
+
end
|
113
|
+
|
114
|
+
it { is_expected.to have_http_status(:unauthorized) }
|
115
|
+
context 'json within response' do
|
116
|
+
it 'has a message' do
|
117
|
+
expect(json['message']).to eq('SSL client failure.')
|
118
|
+
end
|
119
|
+
it 'has an error' do
|
120
|
+
expect(json['error']).to eq('Subject not functional')
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
context '#ensure_access_checked as after_action' do
|
127
|
+
subject(:api_subject) { create :api_subject }
|
128
|
+
let(:json) { JSON.parse(response.body) }
|
129
|
+
|
130
|
+
before do
|
131
|
+
request.env['HTTP_X509_DN'] = "CN=#{api_subject.x509_cn}/DC=example"
|
132
|
+
end
|
133
|
+
|
134
|
+
RSpec.shared_examples 'APIController base state' do
|
135
|
+
it 'fails request to incorrectly implemented action' do
|
136
|
+
msg = 'No access control performed by API::APIController#bad_action'
|
137
|
+
expect { get :bad_action }.to raise_error(msg)
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'completes request to a public action' do
|
141
|
+
get :public
|
142
|
+
expect(response).to have_http_status(:ok)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
context 'subject without permissions' do
|
147
|
+
include_examples 'APIController base state'
|
148
|
+
|
149
|
+
it 'has no permissions' do
|
150
|
+
expect(api_subject.permissions).to eq([])
|
151
|
+
end
|
152
|
+
|
153
|
+
context 'the request does not complete' do
|
154
|
+
before { get :an_action }
|
155
|
+
it 'should respond with status code :forbidden (403)' do
|
156
|
+
expect(response).to have_http_status(:forbidden)
|
157
|
+
end
|
158
|
+
it 'recieves a json message' do
|
159
|
+
expect(json['message'])
|
160
|
+
.to eq('The request was understood but explicitly denied.')
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
context 'subject with invalid permissions' do
|
166
|
+
subject(:api_subject) do
|
167
|
+
create :api_subject, :authorized, permission: 'invalid:permission'
|
168
|
+
end
|
169
|
+
|
170
|
+
include_examples 'APIController base state'
|
171
|
+
|
172
|
+
it 'has an invalid permission' do
|
173
|
+
expect(api_subject.permissions).to eq(['invalid:permission'])
|
174
|
+
end
|
175
|
+
|
176
|
+
context 'the request does not complete' do
|
177
|
+
before { get :an_action }
|
178
|
+
it 'should respond with status code :forbidden (403)' do
|
179
|
+
expect(response).to have_http_status(:forbidden)
|
180
|
+
end
|
181
|
+
it 'recieves a json message' do
|
182
|
+
expect(json['message'])
|
183
|
+
.to eq('The request was understood but explicitly denied.')
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
context 'subject with valid permission' do
|
189
|
+
subject(:api_subject) do
|
190
|
+
create :api_subject, :authorized, permission: 'required:permission'
|
191
|
+
end
|
192
|
+
|
193
|
+
include_examples 'APIController base state'
|
194
|
+
|
195
|
+
it 'has a valid permission' do
|
196
|
+
expect(api_subject.permissions).to eq(['required:permission'])
|
197
|
+
end
|
198
|
+
|
199
|
+
it 'completes request after permissions checked' do
|
200
|
+
get :an_action
|
201
|
+
expect(response).to have_http_status(:ok)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
RSpec.shared_examples 'API Subjects' do
|
2
|
+
context 'AAF shared implementation' do
|
3
|
+
subject { build :api_subject }
|
4
|
+
|
5
|
+
it { is_expected.to be_valid }
|
6
|
+
it { is_expected.to be_an(Accession::Principal) }
|
7
|
+
it { is_expected.to respond_to(:roles) }
|
8
|
+
it { is_expected.to respond_to(:permissions) }
|
9
|
+
it { is_expected.to respond_to(:permits?) }
|
10
|
+
it { is_expected.to respond_to(:functioning?) }
|
11
|
+
|
12
|
+
it 'is invalid without an x509_cn' do
|
13
|
+
subject.x509_cn = nil
|
14
|
+
expect(subject).not_to be_valid
|
15
|
+
end
|
16
|
+
it 'is invalid if an x509 value is not in the correct format' do
|
17
|
+
subject.x509_cn += '%^%&*'
|
18
|
+
expect(subject).not_to be_valid
|
19
|
+
end
|
20
|
+
it 'is valid if an x509 value is in the correct format' do
|
21
|
+
expect(subject).to be_valid
|
22
|
+
end
|
23
|
+
it 'is invalid if an x509 value is not unique' do
|
24
|
+
create(:api_subject, x509_cn: subject.x509_cn)
|
25
|
+
expect(subject).not_to be_valid
|
26
|
+
end
|
27
|
+
it 'is invalid without a description' do
|
28
|
+
subject.description = nil
|
29
|
+
expect(subject).not_to be_valid
|
30
|
+
end
|
31
|
+
it 'is invalid without a contact name' do
|
32
|
+
subject.contact_name = nil
|
33
|
+
expect(subject).not_to be_valid
|
34
|
+
end
|
35
|
+
it 'is invalid without a contact mail address' do
|
36
|
+
subject.contact_mail = nil
|
37
|
+
expect(subject).not_to be_valid
|
38
|
+
end
|
39
|
+
it 'is invalid without an enabled state' do
|
40
|
+
subject.enabled = nil
|
41
|
+
expect(subject).not_to be_valid
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,223 @@
|
|
1
|
+
require 'gumboot/shared_examples/anonymous_controller'
|
2
|
+
|
3
|
+
RSpec.shared_examples 'Application controller' do
|
4
|
+
context 'AAF shared implementation' do
|
5
|
+
include_examples 'Anon controller'
|
6
|
+
|
7
|
+
before do
|
8
|
+
@routes.draw do
|
9
|
+
get '/anonymous/an_action' => 'anonymous#an_action'
|
10
|
+
get '/anonymous/bad_action' => 'anonymous#bad_action'
|
11
|
+
get '/anonymous/public' => 'anonymous#public'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
context '#subject' do
|
16
|
+
context 'No subject ID is set in session' do
|
17
|
+
before do
|
18
|
+
session[:subject_id] = nil
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'returns nil' do
|
22
|
+
expect(subject.subject).to be_nil
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'session has subject_id that does not represent a Subject' do
|
27
|
+
before do
|
28
|
+
session[:subject_id] = -1
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'returns nil' do
|
32
|
+
expect(subject.subject).to be_nil
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'Subject that is not functioning' do
|
37
|
+
let(:current_subject) { create :subject, enabled: false }
|
38
|
+
|
39
|
+
before do
|
40
|
+
session[:subject_id] = current_subject.id
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'returns nil' do
|
44
|
+
expect(subject.subject).to be_nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'Subject is valid' do
|
49
|
+
let(:current_subject) { create :subject }
|
50
|
+
|
51
|
+
before do
|
52
|
+
session[:subject_id] = current_subject.id
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'returns subject' do
|
56
|
+
expect(subject.subject).to eq(current_subject)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context '#ensure_authenticated as before_action' do
|
62
|
+
subject { response }
|
63
|
+
|
64
|
+
context 'No subject ID is set in session' do
|
65
|
+
before do
|
66
|
+
session[:subject_id] = nil
|
67
|
+
get :an_action
|
68
|
+
end
|
69
|
+
|
70
|
+
it { is_expected.to have_http_status(:redirect) }
|
71
|
+
it { is_expected.to redirect_to('/auth/login') }
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'session has subject_id that does not represent a Subject' do
|
75
|
+
before do
|
76
|
+
session[:subject_id] = -1
|
77
|
+
get :an_action
|
78
|
+
end
|
79
|
+
|
80
|
+
it { is_expected.to have_http_status(:unauthorized) }
|
81
|
+
|
82
|
+
it do
|
83
|
+
is_expected.to render_template(
|
84
|
+
'dynamic_errors/unauthorized',
|
85
|
+
'layouts/application'
|
86
|
+
)
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'resets the session' do
|
90
|
+
expect(session[:subject_id]).to be_nil
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context 'Subject that is not functioning' do
|
95
|
+
let(:current_subject) { create :subject, enabled: false }
|
96
|
+
|
97
|
+
before do
|
98
|
+
session[:subject_id] = current_subject.id
|
99
|
+
get :an_action
|
100
|
+
end
|
101
|
+
|
102
|
+
it { is_expected.to have_http_status(:unauthorized) }
|
103
|
+
|
104
|
+
it do
|
105
|
+
is_expected.to render_template(
|
106
|
+
'dynamic_errors/unauthorized',
|
107
|
+
'layouts/application'
|
108
|
+
)
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'resets the session' do
|
112
|
+
expect(session[:subject_id]).to be_nil
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
context 'when request is session' do
|
117
|
+
it 'POST request should not create a uri session' do
|
118
|
+
post :an_action
|
119
|
+
expect(session).not_to include(:return_url)
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'GET request should not create a uri session' do
|
123
|
+
get :an_action
|
124
|
+
uri = URI.parse(session[:return_url])
|
125
|
+
expect(uri.path).to eq('/anonymous/an_action')
|
126
|
+
expect(uri.query).to be_blank
|
127
|
+
expect(uri.fragment).to be_blank
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'GET request should create a uri session including fragments' do
|
131
|
+
get :an_action, params: { time: 1000 }
|
132
|
+
uri = URI.parse(session[:return_url])
|
133
|
+
|
134
|
+
expect(uri.path).to eq('/anonymous/an_action')
|
135
|
+
expect(uri.query).to eq('time=1000')
|
136
|
+
expect(uri.fragment).to be_blank
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
context '#ensure_access_checked as after_action' do
|
142
|
+
before { session[:subject_id] = subject.id }
|
143
|
+
|
144
|
+
RSpec.shared_examples 'ApplicationController base state' do
|
145
|
+
it 'fails request to incorrectly implemented action' do
|
146
|
+
msg = 'No access control performed by AnonymousController#bad_action'
|
147
|
+
expect { get :bad_action }.to raise_error(msg)
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'completes request to a public action' do
|
151
|
+
get :public
|
152
|
+
expect(response).to have_http_status(:ok)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
context 'subject without permissions' do
|
157
|
+
subject(:subject) { create :subject }
|
158
|
+
|
159
|
+
include_examples 'ApplicationController base state'
|
160
|
+
|
161
|
+
it 'has no permissions' do
|
162
|
+
expect(subject.permissions).to eq([])
|
163
|
+
end
|
164
|
+
|
165
|
+
context 'the request does not complete' do
|
166
|
+
before { get :an_action }
|
167
|
+
it 'should respond with status code :forbidden (403)' do
|
168
|
+
expect(response).to have_http_status(:forbidden)
|
169
|
+
end
|
170
|
+
|
171
|
+
it 'renders forbidden template' do
|
172
|
+
expect(response).to render_template(
|
173
|
+
'dynamic_errors/forbidden',
|
174
|
+
'layouts/application'
|
175
|
+
)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
context 'subject with invalid permissions' do
|
181
|
+
subject(:subject) do
|
182
|
+
create :subject, :authorized, permission: 'invalid:permission'
|
183
|
+
end
|
184
|
+
|
185
|
+
include_examples 'ApplicationController base state'
|
186
|
+
|
187
|
+
it 'has an invalid permission' do
|
188
|
+
expect(subject.permissions).to eq(['invalid:permission'])
|
189
|
+
end
|
190
|
+
|
191
|
+
context 'the request does not complete' do
|
192
|
+
before { get :an_action }
|
193
|
+
it 'should respond with status code :forbidden (403)' do
|
194
|
+
expect(response).to have_http_status(:forbidden)
|
195
|
+
end
|
196
|
+
it 'renders forbidden template' do
|
197
|
+
expect(response).to render_template(
|
198
|
+
'dynamic_errors/forbidden',
|
199
|
+
'layouts/application'
|
200
|
+
)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
context 'subject with valid permission' do
|
206
|
+
subject(:subject) do
|
207
|
+
create :subject, :authorized, permission: 'required:permission'
|
208
|
+
end
|
209
|
+
|
210
|
+
include_examples 'ApplicationController base state'
|
211
|
+
|
212
|
+
it 'has a valid permission' do
|
213
|
+
expect(subject.permissions).to eq(['required:permission'])
|
214
|
+
end
|
215
|
+
|
216
|
+
it 'completes request after permissions checked' do
|
217
|
+
get :an_action
|
218
|
+
expect(response).to have_http_status(:ok)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|