aaf-gumboot 1.0.0.pre.alpha.2
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 +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
|