rockstart 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +100 -0
- data/Rakefile +19 -0
- data/lib/generators/rockstart/USAGE +13 -0
- data/lib/generators/rockstart/devise/USAGE +9 -0
- data/lib/generators/rockstart/devise/devise_generator.rb +258 -0
- data/lib/generators/rockstart/devise/templates/controllers/passwords_controller.rb +56 -0
- data/lib/generators/rockstart/devise/templates/controllers/registrations_controller.rb +88 -0
- data/lib/generators/rockstart/devise/templates/controllers/sessions_controller.rb +32 -0
- data/lib/generators/rockstart/devise/templates/create_user_migration.rb.tt +11 -0
- data/lib/generators/rockstart/devise/templates/models/user.rb +42 -0
- data/lib/generators/rockstart/devise/templates/spec/factories/users.rb +17 -0
- data/lib/generators/rockstart/devise/templates/spec/models/user_spec.rb +64 -0
- data/lib/generators/rockstart/devise/templates/spec/requests/users/passwords_spec.rb +202 -0
- data/lib/generators/rockstart/devise/templates/spec/requests/users/registrations_spec.rb +445 -0
- data/lib/generators/rockstart/devise/templates/spec/requests/users/sessions_spec.rb +171 -0
- data/lib/generators/rockstart/devise/templates/spec/support/devise_request_spec_helper.rb +29 -0
- data/lib/generators/rockstart/devise/templates/translations.en.yml +4 -0
- data/lib/generators/rockstart/docker/USAGE +10 -0
- data/lib/generators/rockstart/docker/docker_generator.rb +86 -0
- data/lib/generators/rockstart/docker/templates/app/Dockerfile-app +47 -0
- data/lib/generators/rockstart/docker/templates/docker-compose.test.yml +29 -0
- data/lib/generators/rockstart/docker/templates/docker-compose.yml +47 -0
- data/lib/generators/rockstart/docker/templates/dockerignore +16 -0
- data/lib/generators/rockstart/docker/templates/dotenv.docker.tt +4 -0
- data/lib/generators/rockstart/docker/templates/localhost_domains.ext.tt +7 -0
- data/lib/generators/rockstart/docker/templates/setup-localhost.tt +27 -0
- data/lib/generators/rockstart/docker/templates/web/Dockerfile-web +15 -0
- data/lib/generators/rockstart/docker/templates/web/nginx.conf +62 -0
- data/lib/generators/rockstart/frontend_helpers/USAGE +8 -0
- data/lib/generators/rockstart/frontend_helpers/frontend_helpers_generator.rb +65 -0
- data/lib/generators/rockstart/frontend_helpers/templates/application_urls.rb +26 -0
- data/lib/generators/rockstart/frontend_helpers/templates/application_urls_helper.rb +20 -0
- data/lib/generators/rockstart/frontend_helpers/templates/titles.en.yml.tt +5 -0
- data/lib/generators/rockstart/logging/USAGE +8 -0
- data/lib/generators/rockstart/logging/logging_generator.rb +12 -0
- data/lib/generators/rockstart/logging/templates/rockstart/lograge_initializer.rb +50 -0
- data/lib/generators/rockstart/postgres/USAGE +8 -0
- data/lib/generators/rockstart/postgres/postgres_generator.rb +32 -0
- data/lib/generators/rockstart/postgres/templates/config/database.yml.tt +18 -0
- data/lib/generators/rockstart/postgres/templates/migration.rb.tt +7 -0
- data/lib/generators/rockstart/pundit/USAGE +8 -0
- data/lib/generators/rockstart/pundit/pundit_generator.rb +32 -0
- data/lib/generators/rockstart/pundit/templates/app/controllers/concerns/pundit_error_handling.rb +29 -0
- data/lib/generators/rockstart/pundit/templates/app/policies/application_policy.rb +71 -0
- data/lib/generators/rockstart/pundit/templates/app/policies/user_policy.rb +47 -0
- data/lib/generators/rockstart/pundit/templates/config/locales/pundit.en.yml +6 -0
- data/lib/generators/rockstart/pundit/templates/lib/templates/pundit/policy/policy.rb +36 -0
- data/lib/generators/rockstart/pundit/templates/lib/templates/rspec/policy/policy_spec.rb +58 -0
- data/lib/generators/rockstart/pundit/templates/spec/policies/user_policy_spec.rb +95 -0
- data/lib/generators/rockstart/pundit/templates/spec/support/pundit_matchers.rb +7 -0
- data/lib/generators/rockstart/quality/USAGE +10 -0
- data/lib/generators/rockstart/quality/quality_generator.rb +28 -0
- data/lib/generators/rockstart/quality/templates/quality.rake +4 -0
- data/lib/generators/rockstart/quality/templates/rubocop.rake +4 -0
- data/lib/generators/rockstart/quality/templates/rubocop.yml +45 -0
- data/lib/generators/rockstart/rockstart_generator.rb +77 -0
- data/lib/generators/rockstart/rspec/USAGE +8 -0
- data/lib/generators/rockstart/rspec/rspec_generator.rb +70 -0
- data/lib/generators/rockstart/rspec/templates/dotenv.development +1 -0
- data/lib/generators/rockstart/rspec/templates/dotenv.test +1 -0
- data/lib/generators/rockstart/rspec/templates/rspec_templates/model/model_spec.rb +13 -0
- data/lib/generators/rockstart/rspec/templates/support/factory_bot.rb +6 -0
- data/lib/generators/rockstart/rspec/templates/support/shoulda_matchers.rb +9 -0
- data/lib/generators/rockstart/rspec/templates/support/test_helpers.rb +9 -0
- data/lib/generators/rockstart/scaffold_templates/USAGE +8 -0
- data/lib/generators/rockstart/scaffold_templates/scaffold_templates_generator.rb +39 -0
- data/lib/generators/rockstart/scaffold_templates/templates/api_controller.rb.tt +96 -0
- data/lib/generators/rockstart/scaffold_templates/templates/controller.rb.tt +126 -0
- data/lib/generators/rockstart/scaffold_templates/templates/rspec/scaffold/api_request_spec.rb +139 -0
- data/lib/generators/rockstart/scaffold_templates/templates/rspec/scaffold/request_spec.rb +408 -0
- data/lib/generators/rockstart/security/USAGE +13 -0
- data/lib/generators/rockstart/security/security_generator.rb +108 -0
- data/lib/generators/rockstart/security/templates/brakeman.rake +6 -0
- data/lib/generators/rockstart/security/templates/bundler_audit.rake +4 -0
- data/lib/generators/rockstart/security/templates/cache_support.rb +18 -0
- data/lib/generators/rockstart/security/templates/content_security_policy_initializer.rb.tt +56 -0
- data/lib/generators/rockstart/security/templates/content_security_spec.rb.tt +83 -0
- data/lib/generators/rockstart/security/templates/csp_violations_controller.rb +39 -0
- data/lib/generators/rockstart/security/templates/rack_attack.rb +98 -0
- data/lib/generators/rockstart/security/templates/security.rake +9 -0
- data/lib/generators/rockstart/security/templates/session_store_initializer.rb.tt +7 -0
- data/lib/generators/rockstart/smtp_mailer/USAGE +8 -0
- data/lib/generators/rockstart/smtp_mailer/smtp_mailer_generator.rb +30 -0
- data/lib/generators/rockstart/smtp_mailer/templates/config/initializers/action_mailer.rb +10 -0
- data/lib/generators/rockstart/tailwindcss/USAGE +8 -0
- data/lib/generators/rockstart/tailwindcss/tailwindcss_generator.rb +30 -0
- data/lib/generators/rockstart/tailwindcss/templates/application.css +3 -0
- data/lib/generators/rockstart/tailwindcss/templates/postcss.config.js +32 -0
- data/lib/rockstart/base_generator.rb +32 -0
- data/lib/rockstart/env.rb +16 -0
- data/lib/rockstart/railtie.rb +6 -0
- data/lib/rockstart/version.rb +5 -0
- data/lib/rockstart.rb +9 -0
- data/lib/tasks/rockstart_tasks.rake +5 -0
- metadata +187 -0
@@ -0,0 +1,408 @@
|
|
1
|
+
<%- resource_path = name.underscore.pluralize -%>
|
2
|
+
<%- permitted_params = attributes.map { |a| ":#{a.name}" }.join(", ") -%>
|
3
|
+
# frozen_string_literal: true
|
4
|
+
|
5
|
+
require "rails_helper"
|
6
|
+
|
7
|
+
<% module_namespacing do -%>
|
8
|
+
RSpec.describe "<%= controller_class_name %>", <%= type_metatag(:request) %> do
|
9
|
+
<% unless options[:singleton] -%>
|
10
|
+
describe "GET /<%= resource_path %>" do
|
11
|
+
context "with an authenticated admin" do
|
12
|
+
let(:authenticated_admin) { create(:user, :admin) }
|
13
|
+
|
14
|
+
before do
|
15
|
+
sign_in(authenticated_admin)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "renders a successful response" do
|
19
|
+
create(:<%= file_name %>)
|
20
|
+
get <%= index_helper %>_url
|
21
|
+
expect(response).to be_successful
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context "with an authenticated user" do
|
26
|
+
let(:authenticated_user) { create(:user) }
|
27
|
+
|
28
|
+
before do
|
29
|
+
sign_in(authenticated_user)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "renders a successful response" do
|
33
|
+
create(:<%= file_name %>)
|
34
|
+
get <%= index_helper %>_url
|
35
|
+
expect(response).to be_successful
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context "with an unauthorized user" do
|
40
|
+
let(:unauthorized_user) do
|
41
|
+
skip("Provide a user where <%= class_name %>Policy.index? is not granted")
|
42
|
+
end
|
43
|
+
|
44
|
+
before do
|
45
|
+
sign_in(unauthorized_user)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "forbids access" do
|
49
|
+
create(:<%= file_name %>)
|
50
|
+
get <%= index_helper %>_url
|
51
|
+
expect(response).to redirect_to(url_for_user_dashboard)
|
52
|
+
|
53
|
+
follow_redirect!
|
54
|
+
expect(response.body).to have_selector(".alert-error", text: t("pundit.example_policy.index?", default: t("pundit.default")))
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
it "does not allow access to guests" do
|
59
|
+
create(:<%= file_name %>)
|
60
|
+
get <%= index_helper %>_url
|
61
|
+
expect(response).to redirect_to(url_for_authentication)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
<% end -%>
|
65
|
+
|
66
|
+
describe "GET /<%= resource_path %>/:id" do
|
67
|
+
context "with an authenticated admin" do
|
68
|
+
let(:authenticated_admin) { create(:user, :admin) }
|
69
|
+
|
70
|
+
before do
|
71
|
+
sign_in(authenticated_admin)
|
72
|
+
end
|
73
|
+
|
74
|
+
it "renders a successful response" do
|
75
|
+
<%= file_name %> = create(:<%= file_name %>)
|
76
|
+
get <%= show_helper.tr('@', '') %>
|
77
|
+
expect(response).to be_successful
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context "with an authenticated user" do
|
82
|
+
let(:authenticated_user) { create(:user) }
|
83
|
+
|
84
|
+
before do
|
85
|
+
sign_in(authenticated_user)
|
86
|
+
end
|
87
|
+
|
88
|
+
it "renders a successful response" do
|
89
|
+
<%= file_name %> = create(:<%= file_name %>)
|
90
|
+
get <%= show_helper.tr('@', '') %>
|
91
|
+
expect(response).to be_successful
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
context "with an unauthorized user" do
|
96
|
+
let(:unauthorized_user) do
|
97
|
+
skip("Provide a user where <%= class_name %>Policy.show? is not granted")
|
98
|
+
end
|
99
|
+
|
100
|
+
before do
|
101
|
+
sign_in(unauthorized_user)
|
102
|
+
end
|
103
|
+
|
104
|
+
it "forbids access" do
|
105
|
+
<%= file_name %> = create(:<%= file_name %>)
|
106
|
+
get <%= show_helper.tr('@', '') %>
|
107
|
+
expect(response).to redirect_to(<%= index_helper %>_url)
|
108
|
+
|
109
|
+
follow_redirect!
|
110
|
+
expect(response.body).to have_selector(".alert-error", text: t("pundit.example_policy.show?", default: t("pundit.default")))
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
it "does not allow access to guests" do
|
115
|
+
<%= file_name %> = create(:<%= file_name %>)
|
116
|
+
get <%= show_helper.tr('@', '') %>
|
117
|
+
expect(response).to redirect_to(url_for_authentication)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe "GET /<%= resource_path %>/new" do
|
122
|
+
context "with an authenticated admin" do
|
123
|
+
let(:authenticated_admin) { create(:user, :admin) }
|
124
|
+
|
125
|
+
before do
|
126
|
+
sign_in(authenticated_admin)
|
127
|
+
end
|
128
|
+
|
129
|
+
it "renders a successful response" do
|
130
|
+
get <%= new_helper %>
|
131
|
+
expect(response).to be_successful
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
context "with an authenticated user" do
|
136
|
+
let(:authenticated_user) { create(:user) }
|
137
|
+
|
138
|
+
before do
|
139
|
+
sign_in(authenticated_user)
|
140
|
+
end
|
141
|
+
|
142
|
+
it "forbids access" do
|
143
|
+
get <%= new_helper %>
|
144
|
+
expect(response).to redirect_to(<%= index_helper %>_url)
|
145
|
+
|
146
|
+
follow_redirect!
|
147
|
+
expect(response.body).to have_selector(".alert-error", text: t("pundit.example_policy.new?", default: t("pundit.default")))
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
it "does not allow access to guests" do
|
152
|
+
get <%= new_helper %>
|
153
|
+
expect(response).to redirect_to(url_for_authentication)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
describe "GET /<%= resource_path %>/:id/edit" do
|
158
|
+
context "with an authenticated admin" do
|
159
|
+
let(:authenticated_admin) { create(:user, :admin) }
|
160
|
+
|
161
|
+
before do
|
162
|
+
sign_in(authenticated_admin)
|
163
|
+
end
|
164
|
+
|
165
|
+
it "render a successful response" do
|
166
|
+
<%= file_name %> = create(:<%= file_name %>)
|
167
|
+
get <%= edit_helper.tr('@','') %>
|
168
|
+
expect(response).to be_successful
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
context "with an authenticated user" do
|
173
|
+
let(:authenticated_user) { create(:user) }
|
174
|
+
|
175
|
+
before do
|
176
|
+
sign_in(authenticated_user)
|
177
|
+
end
|
178
|
+
|
179
|
+
it "forbids access" do
|
180
|
+
<%= file_name %> = create(:<%= file_name %>)
|
181
|
+
get <%= edit_helper.tr('@','') %>
|
182
|
+
expect(response).to redirect_to(<%= show_helper.tr('@', '') %>)
|
183
|
+
|
184
|
+
follow_redirect!
|
185
|
+
expect(response.body).to have_selector(".alert-error", text: t("pundit.example_policy.edit?", default: t("pundit.default")))
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
it "does not allow access to guests" do
|
190
|
+
<%= file_name %> = create(:<%= file_name %>)
|
191
|
+
get <%= edit_helper.tr('@','') %>
|
192
|
+
expect(response).to redirect_to(url_for_authentication)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
describe "POST /<%= resource_path %>" do
|
197
|
+
context "with valid parameters" do
|
198
|
+
let(:valid_attributes) do
|
199
|
+
<%- if attributes.any? -%>
|
200
|
+
attributes_for(:<%= ns_file_name %>).slice(<%= permitted_params %>)
|
201
|
+
<%- else -%>
|
202
|
+
skip("Add a hash of attributes valid for your model")
|
203
|
+
<%- end -%>
|
204
|
+
end
|
205
|
+
|
206
|
+
context "with an authenticated admin" do
|
207
|
+
let(:authenticated_admin) { create(:user, :admin) }
|
208
|
+
|
209
|
+
before do
|
210
|
+
sign_in(authenticated_admin)
|
211
|
+
end
|
212
|
+
|
213
|
+
it "creates a new <%= class_name %>" do
|
214
|
+
expect do
|
215
|
+
post <%= index_helper %>_url, params: { <%= ns_file_name %>: valid_attributes }
|
216
|
+
end.to change(<%= class_name %>, :count).by(1)
|
217
|
+
|
218
|
+
<%= file_name %> = <%= class_name %>.last
|
219
|
+
<%- if attributes.any? -%>
|
220
|
+
<%- attributes.each do |attribute| -%>
|
221
|
+
expect(<%= file_name %>.<%= attribute.name %>).to eq(valid_attributes[:<%= attribute.name %>])
|
222
|
+
<%- end -%>
|
223
|
+
<%- else -%>
|
224
|
+
skip("Add assertions for created state")
|
225
|
+
<%- end -%>
|
226
|
+
end
|
227
|
+
|
228
|
+
it "redirects to the created <%= ns_file_name %>" do
|
229
|
+
post <%= index_helper %>_url, params: { <%= ns_file_name %>: valid_attributes }
|
230
|
+
expect(response).to redirect_to(<%= show_helper.gsub("\@#{file_name}", class_name+".last") %>)
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
context "with an authenticated user" do
|
235
|
+
let(:authenticated_user) { create(:user) }
|
236
|
+
|
237
|
+
before do
|
238
|
+
sign_in(authenticated_user)
|
239
|
+
end
|
240
|
+
|
241
|
+
it "forbids access" do
|
242
|
+
post <%= index_helper %>_url, params: { <%= ns_file_name %>: valid_attributes }
|
243
|
+
expect(response).to redirect_to(<%= index_helper %>_url)
|
244
|
+
|
245
|
+
follow_redirect!
|
246
|
+
expect(response.body).to have_selector(".alert-error", text: t("pundit.example_policy.create?", default: t("pundit.default")))
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
it "does not allow access to guests" do
|
251
|
+
post <%= index_helper %>_url, params: { <%= ns_file_name %>: valid_attributes }
|
252
|
+
expect(response).to redirect_to(url_for_authentication)
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
context "with invalid parameters" do
|
257
|
+
let(:authenticated_admin) { create(:user, :admin) }
|
258
|
+
|
259
|
+
let(:invalid_attributes) do
|
260
|
+
skip("Add a hash of attributes invalid for your model")
|
261
|
+
end
|
262
|
+
|
263
|
+
before do
|
264
|
+
sign_in(authenticated_admin)
|
265
|
+
end
|
266
|
+
|
267
|
+
it "does not create a new <%= class_name %>" do
|
268
|
+
expect do
|
269
|
+
post <%= index_helper %>_url, params: { <%= ns_file_name %>: invalid_attributes }
|
270
|
+
end.to change(<%= class_name %>, :count).by(0)
|
271
|
+
end
|
272
|
+
|
273
|
+
it "renders a successful response (i.e. to display the 'new' template)" do
|
274
|
+
post <%= index_helper %>_url, params: { <%= ns_file_name %>: invalid_attributes }
|
275
|
+
expect(response).to be_successful
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
describe "PATCH /<%= resource_path %>/:id" do
|
281
|
+
context "with valid parameters" do
|
282
|
+
let(:new_attributes) do
|
283
|
+
<%- if attributes.any? -%>
|
284
|
+
attributes_for(:<%= ns_file_name %>).slice(<%= permitted_params %>)
|
285
|
+
<%- else -%>
|
286
|
+
skip("Add a hash of attributes valid for your model")
|
287
|
+
<%- end -%>
|
288
|
+
end
|
289
|
+
|
290
|
+
context "with an authenticated admin" do
|
291
|
+
let(:authenticated_admin) { create(:user, :admin) }
|
292
|
+
|
293
|
+
before do
|
294
|
+
sign_in(authenticated_admin)
|
295
|
+
end
|
296
|
+
|
297
|
+
it "updates the requested <%= ns_file_name %>" do
|
298
|
+
<%= file_name %> = create(:<%= file_name %>)
|
299
|
+
patch <%= show_helper.tr('@', '') %>, params: { <%= singular_table_name %>: new_attributes }
|
300
|
+
|
301
|
+
<%= file_name %>.reload
|
302
|
+
<%- if attributes.any? -%>
|
303
|
+
<%- attributes.each do |attribute| -%>
|
304
|
+
expect(<%= file_name %>.<%= attribute.name %>).to eq(new_attributes[:<%= attribute.name %>])
|
305
|
+
<%- end -%>
|
306
|
+
<%- else -%>
|
307
|
+
skip("Add assertions for updated state")
|
308
|
+
<%- end -%>
|
309
|
+
end
|
310
|
+
|
311
|
+
it "redirects to the <%= ns_file_name %>" do
|
312
|
+
<%= file_name %> = create(:<%= file_name %>)
|
313
|
+
patch <%= show_helper.tr('@', '') %>, params: { <%= singular_table_name %>: new_attributes }
|
314
|
+
<%= file_name %>.reload
|
315
|
+
expect(response).to redirect_to(<%= singular_table_name %>_url(<%= file_name %>))
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
context "with an authenticated user" do
|
320
|
+
let(:authenticated_user) { create(:user) }
|
321
|
+
|
322
|
+
before do
|
323
|
+
sign_in(authenticated_user)
|
324
|
+
end
|
325
|
+
|
326
|
+
it "forbids access" do
|
327
|
+
<%= file_name %> = create(:<%= file_name %>)
|
328
|
+
patch <%= show_helper.tr('@', '') %>, params: { <%= singular_table_name %>: new_attributes }
|
329
|
+
expect(response).to redirect_to(<%= show_helper.tr('@', '') %>)
|
330
|
+
|
331
|
+
follow_redirect!
|
332
|
+
expect(response.body).to have_selector(".alert-error", text: t("pundit.example_policy.update?", default: t("pundit.default")))
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
it "does not allow access to guests" do
|
337
|
+
<%= file_name %> = create(:<%= file_name %>)
|
338
|
+
patch <%= show_helper.tr('@', '') %>, params: { <%= singular_table_name %>: new_attributes }
|
339
|
+
expect(response).to redirect_to(url_for_authentication)
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
context "with invalid parameters" do
|
344
|
+
let(:authenticated_admin) { create(:user, :admin) }
|
345
|
+
|
346
|
+
let(:invalid_attributes) do
|
347
|
+
skip("Add a hash of attributes invalid for your model")
|
348
|
+
end
|
349
|
+
|
350
|
+
before do
|
351
|
+
sign_in(authenticated_admin)
|
352
|
+
end
|
353
|
+
|
354
|
+
it "renders a successful response (i.e. to display the 'edit' template)" do
|
355
|
+
<%= file_name %> = create(:<%= file_name %>)
|
356
|
+
patch <%= show_helper.tr('@', '') %>, params: { <%= singular_table_name %>: invalid_attributes }
|
357
|
+
expect(response).to be_successful
|
358
|
+
end
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
describe "DELETE /<%= resource_path %>/:id" do
|
363
|
+
context "with an authenticated admin" do
|
364
|
+
let(:authenticated_admin) { create(:user, :admin) }
|
365
|
+
|
366
|
+
before do
|
367
|
+
sign_in(authenticated_admin)
|
368
|
+
end
|
369
|
+
|
370
|
+
it "destroys the requested <%= ns_file_name %>" do
|
371
|
+
<%= file_name %> = create(:<%= file_name %>)
|
372
|
+
expect do
|
373
|
+
delete <%= show_helper.tr('@', '') %>
|
374
|
+
end.to change(<%= class_name %>, :count).by(-1)
|
375
|
+
end
|
376
|
+
|
377
|
+
it "redirects to the <%= table_name %> list" do
|
378
|
+
<%= file_name %> = create(:<%= file_name %>)
|
379
|
+
delete <%= show_helper.tr('@', '') %>
|
380
|
+
expect(response).to redirect_to(<%= index_helper %>_url)
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
context "with an authenticated user" do
|
385
|
+
let(:authenticated_user) { create(:user) }
|
386
|
+
|
387
|
+
before do
|
388
|
+
sign_in(authenticated_user)
|
389
|
+
end
|
390
|
+
|
391
|
+
it "forbids access" do
|
392
|
+
<%= file_name %> = create(:<%= file_name %>)
|
393
|
+
delete <%= show_helper.tr('@', '') %>
|
394
|
+
expect(response).to redirect_to(<%= show_helper.tr('@', '') %>)
|
395
|
+
|
396
|
+
follow_redirect!
|
397
|
+
expect(response.body).to have_selector(".alert-error", text: t("pundit.example_policy.destroy?", default: t("pundit.default")))
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
it "does not allow access to guests" do
|
402
|
+
<%= file_name %> = create(:<%= file_name %>)
|
403
|
+
delete <%= show_helper.tr('@', '') %>
|
404
|
+
expect(response).to redirect_to(url_for_authentication)
|
405
|
+
end
|
406
|
+
end
|
407
|
+
end
|
408
|
+
<% end -%>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
Description:
|
2
|
+
Adds security checks and middleware to a Rails app
|
3
|
+
|
4
|
+
Example:
|
5
|
+
rails generate rockstart:security
|
6
|
+
|
7
|
+
This will create:
|
8
|
+
Installs bundler-audit to safely check gems
|
9
|
+
Installs brakeman to check for any potential exploits
|
10
|
+
Adds rack_attack with a simple policy to block most attacks
|
11
|
+
Configures a minimalist Content Security Policy with supporting test coverage
|
12
|
+
Ensures test (and unconfigured) environments are using an in-memory cache
|
13
|
+
Enforces secure HTTPS connections by defaults
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Rockstart::SecurityGenerator < Rails::Generators::Base
|
4
|
+
include Rails::Generators::AppName
|
5
|
+
source_root File.expand_path("templates", __dir__)
|
6
|
+
|
7
|
+
class_option :font_hosts, type: :array,
|
8
|
+
desc: "Known third-party hosts for Fonts",
|
9
|
+
default: []
|
10
|
+
|
11
|
+
class_option :image_hosts, type: :array,
|
12
|
+
desc: "Known third-party hosts for Images",
|
13
|
+
default: []
|
14
|
+
|
15
|
+
class_option :script_hosts, type: :array,
|
16
|
+
desc: "Known third-party hosts for (Java)Scripts",
|
17
|
+
default: []
|
18
|
+
|
19
|
+
class_option :style_hosts, type: :array,
|
20
|
+
desc: "Known third-party hosts for Stylesheets",
|
21
|
+
default: []
|
22
|
+
|
23
|
+
class_option :session_name, type: :string,
|
24
|
+
desc: "Name used for Rails Sessions",
|
25
|
+
default: Rockstart::Env.default_session_name
|
26
|
+
|
27
|
+
def install_bundler_audit
|
28
|
+
gem "bundler-audit", github: "rubysec/bundler-audit"
|
29
|
+
|
30
|
+
Bundler.clean_system("bundle install --quiet")
|
31
|
+
|
32
|
+
copy_file "bundler_audit.rake", "lib/tasks/bundler_audit.rake"
|
33
|
+
end
|
34
|
+
|
35
|
+
def install_brakeman
|
36
|
+
gem "brakeman", group: %i[development test]
|
37
|
+
|
38
|
+
Bundler.clean_system("bundle install --quiet")
|
39
|
+
|
40
|
+
copy_file "brakeman.rake", "lib/tasks/brakeman.rake"
|
41
|
+
|
42
|
+
append_to_file ".gitignore", "brakeman\n"
|
43
|
+
end
|
44
|
+
|
45
|
+
def add_security_rake_tasks
|
46
|
+
copy_file "security.rake", "lib/tasks/security.rake"
|
47
|
+
end
|
48
|
+
|
49
|
+
def install_rack_attack
|
50
|
+
gem "rack-attack"
|
51
|
+
|
52
|
+
Bundler.clean_system("bundle install --quiet")
|
53
|
+
|
54
|
+
copy_file "rack_attack.rb", "config/initializers/rack_attack.rb"
|
55
|
+
copy_file "cache_support.rb", "spec/support/cache.rb"
|
56
|
+
|
57
|
+
application do
|
58
|
+
<<~CACHE
|
59
|
+
# Use memory_store cache for testing and default configurations
|
60
|
+
config.cache_store = :memory_store
|
61
|
+
CACHE
|
62
|
+
end
|
63
|
+
comment_lines "config/environments/test.rb", "config.cache_store = "
|
64
|
+
end
|
65
|
+
|
66
|
+
def add_session_initializer
|
67
|
+
template "session_store_initializer.rb.tt", "config/initializers/session_store.rb"
|
68
|
+
end
|
69
|
+
|
70
|
+
def add_content_security_policy
|
71
|
+
template "content_security_policy_initializer.rb.tt",
|
72
|
+
"config/initializers/content_security_policy.rb"
|
73
|
+
|
74
|
+
copy_file "csp_violations_controller.rb", "app/controllers/csp_violations_controller.rb"
|
75
|
+
route "resources :csp_violations, only: [:create]"
|
76
|
+
|
77
|
+
template "content_security_spec.rb.tt", "spec/requests/content_security_spec.rb"
|
78
|
+
end
|
79
|
+
|
80
|
+
def enforce_ssl
|
81
|
+
gsub_file "config/environments/production.rb",
|
82
|
+
/config.force_ssl = .+$/,
|
83
|
+
'config.force_ssl = ENV["ALLOW_INSECURE_HTTP"].to_i != 1'
|
84
|
+
uncomment_lines "config/environments/production.rb", /config.force_ssl =/
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def font_hosts
|
90
|
+
options[:font_hosts] || []
|
91
|
+
end
|
92
|
+
|
93
|
+
def image_hosts
|
94
|
+
options[:image_hosts] || []
|
95
|
+
end
|
96
|
+
|
97
|
+
def script_hosts
|
98
|
+
options[:script_hosts] || []
|
99
|
+
end
|
100
|
+
|
101
|
+
def style_hosts
|
102
|
+
options[:style_hosts] || []
|
103
|
+
end
|
104
|
+
|
105
|
+
def session_name
|
106
|
+
options[:session_name]
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Support for tests that depend on Rails.cache
|
4
|
+
module CacheSupport
|
5
|
+
def clear_rails_cache
|
6
|
+
Rails.cache.clear
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
RSpec.configure do |config|
|
11
|
+
config.include CacheSupport
|
12
|
+
|
13
|
+
config.around(cache_testing: true) do |example|
|
14
|
+
clear_rails_cache
|
15
|
+
example.run
|
16
|
+
clear_rails_cache
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Be sure to restart your server when you modify this file.
|
4
|
+
|
5
|
+
# Define an application-wide content security policy
|
6
|
+
# For further information see the following documentation
|
7
|
+
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
|
8
|
+
|
9
|
+
# Configure any external domains used to host assets here
|
10
|
+
csp_connect_sources = []
|
11
|
+
csp_font_sources = <%= font_hosts.inspect %>
|
12
|
+
csp_image_sources = <%= image_hosts.inspect %>
|
13
|
+
csp_script_sources = <%= script_hosts.inspect %>
|
14
|
+
csp_style_sources = <%= style_hosts.inspect %>
|
15
|
+
|
16
|
+
# Allow the asset host to serve assets
|
17
|
+
if (asset_host = Rails.application.config.action_controller.asset_host.presence)
|
18
|
+
csp_font_sources.append(asset_host)
|
19
|
+
csp_image_sources.append(asset_host)
|
20
|
+
csp_script_sources.append(asset_host)
|
21
|
+
csp_style_sources.append(asset_host)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Allow webpacker to render styles inline
|
25
|
+
if Rails.env.development?
|
26
|
+
csp_connect_sources += %w[http://localhost:3035 ws://localhost:3035]
|
27
|
+
csp_style_sources.append(:unsafe_inline)
|
28
|
+
end
|
29
|
+
|
30
|
+
Rails.application.config.content_security_policy do |policy|
|
31
|
+
policy.default_src :none
|
32
|
+
policy.connect_src :self, *csp_connect_sources
|
33
|
+
policy.font_src :self, *csp_font_sources
|
34
|
+
policy.img_src :self, :data, *csp_image_sources
|
35
|
+
policy.object_src :none
|
36
|
+
policy.script_src :self, *csp_script_sources
|
37
|
+
policy.style_src :self, *csp_style_sources
|
38
|
+
|
39
|
+
# Prevent non secure resources from being loaded
|
40
|
+
policy.block_all_mixed_content
|
41
|
+
policy.upgrade_insecure_requests
|
42
|
+
|
43
|
+
# Specify URI for violation reports
|
44
|
+
policy.report_uri "/csp_violations"
|
45
|
+
end
|
46
|
+
|
47
|
+
# If you are using UJS then enable automatic nonce generation
|
48
|
+
# Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
|
49
|
+
|
50
|
+
# Set the nonce only to specific directives
|
51
|
+
# Rails.application.config.content_security_policy_nonce_directives = %w(script-src)
|
52
|
+
|
53
|
+
# Report CSP violations to a specified URI
|
54
|
+
# For further information see the following documentation:
|
55
|
+
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
|
56
|
+
# Rails.application.config.content_security_policy_report_only = true
|