rockstart 0.1.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 +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
|