inline_forms 8.1.55 → 8.1.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +26 -0
- data/app/controllers/concerns/file_slots_concern.rb +6 -0
- data/app/controllers/inline_forms_controller.rb +39 -25
- data/lib/inline_forms/gem_files.rb +3 -2
- data/lib/inline_forms/version.rb +1 -1
- data/test/dummy/app/controllers/application_controller.rb +5 -2
- data/test/dummy/app/controllers/daily_reports_controller.rb +4 -0
- data/test/dummy/app/models/daily_report.rb +21 -0
- data/test/dummy/app/models/machine.rb +1 -0
- data/test/dummy/config/routes.rb +5 -0
- data/test/gem_package_modes_test.rb +1 -1
- data/test/integration/html_refusal_test.rb +152 -0
- data/test/integration_test_helper.rb +11 -0
- metadata +7 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7ecd64e62be7265a5989cc6012533cdac80dbd62f82ab8b009c17bd4c12343dd
|
|
4
|
+
data.tar.gz: fb677f7996a52a2a8d571f65264f22225a9ff9ed8c451657a2ac59c268d396d5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a5d4ed8bbb6569904df70ebbbd30b57c4847bb478088dfe70419363c58caf11d19dc6dfb5c67da2fbdaad8a85555c5b74b68847d19484f272d7c5fcbebcc30cf
|
|
7
|
+
data.tar.gz: a5b41fd5b309e7fd73bec6c652609f775493c1b3efdf977d3aed1503c6e6656d4e8d7e695bac99ca751b914bb2a47be64bdb244233820253d2426e87240515ee
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,32 @@ All notable changes to this project are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [8.1.56] - 2026-10-05
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **HTML requests the engine refuses get an explicit answer instead of falling through to implicit rendering.** Several `InlineFormsController` actions registered `format.html` only behind a guard, or registered it with a block that rendered nothing on the refused path. Rails then rendered implicitly: `ActionController::MissingExactTemplate` (a 500 for browser requests), `UnknownFormat` (406) or a bare 204. That meant error pages and exception-notifier noise. StProject hit it as a 500 on `GET /dagrapportages` by a normal user: `Dagrapportage` is `not_accessible_through_html?` and only exists nested in a Client tab. Now:
|
|
12
|
+
- `index` of a `not_accessible_through_html?` model without `parent_class` → **404**. The resource has no top-level page.
|
|
13
|
+
- `new`, `create`, row `show`, `destroy`, `soft_delete`, `soft_restore` and `revert` without the frame context the gem needs (`update`, plus `parent_class` or a nested row frame for `not_accessible_through_html?` models) → **400**. The request is malformed for this UI rather than aimed at a missing resource, so 400 rather than 404. A guard that does not match the request is a refusal of the request.
|
|
14
|
+
- `destroy` by a user who may not hard-destroy (`destroy_permitted?`, i.e. not superadmin) → **403**, the same answer `revert` already gave. It used to render nothing.
|
|
15
|
+
- `create`, `destroy`, `soft_delete`, `soft_restore` and `revert` now refuse **before writing**. Before, `create` saved the record, and `destroy`/`revert` changed data, and only then failed to respond.
|
|
16
|
+
- The refusals happen after `load_and_authorize_resource` / `authorize!`, so a host's CanCan `check_authorization` stays satisfied. The working paths are unchanged, including the turbo_stream responses (open-after-create, revert), nested lists, forms and rows, and accessible models' lists and rows.
|
|
17
|
+
- **File-slot actions under `check_authorization`.** `file_slot_request_permitted?` answered 400 for an unlisted attribute before any `authorize!`, so a host with CanCan's `check_authorization` raised `CanCan::AuthorizationNotPerformed` (500) instead. It now authorizes the record first, then the attribute.
|
|
18
|
+
|
|
19
|
+
### Tests
|
|
20
|
+
|
|
21
|
+
- `test/integration/html_refusal_test.rb`, using new dummy model `DailyReport` (a `not_accessible_through_html?` child of `Machine`):
|
|
22
|
+
- top-level index → 404; show, new and revert without a nested frame → 400; create → 400 with nothing created; destroy → 400 with the record kept;
|
|
23
|
+
- nested list, new, create, row, destroy and revert still work;
|
|
24
|
+
- an accessible model's list and row still render, and its new/create without `update` → 400;
|
|
25
|
+
- a non-superadmin destroy → 403;
|
|
26
|
+
- a refused file-slot request still counts as authorized.
|
|
27
|
+
- Every refusal also asserts that authorization ran.
|
|
28
|
+
|
|
29
|
+
### Lockstep
|
|
30
|
+
|
|
31
|
+
- validation_hints 8.1.56, inline_forms_installer 8.1.56, inline_forms_schema_edit 8.1.56.
|
|
32
|
+
|
|
7
33
|
## [8.1.55] - 2026-10-04
|
|
8
34
|
|
|
9
35
|
### Fixed
|
|
@@ -136,10 +136,16 @@ module FileSlotsConcern
|
|
|
136
136
|
# Validates the attribute (declared slot shown as :simple_file_field in the
|
|
137
137
|
# attribute list), then authorizes +action+ on it. Renders and returns false
|
|
138
138
|
# when refused.
|
|
139
|
+
#
|
|
140
|
+
# The record is authorized first (these actions skip
|
|
141
|
+
# load_and_authorize_resource): a 400 for an unlisted attribute must still
|
|
142
|
+
# count as an authorization check, or a host with CanCan's
|
|
143
|
+
# +check_authorization+ raises AuthorizationNotPerformed (500) instead.
|
|
139
144
|
def file_slot_request_permitted?(action)
|
|
140
145
|
@attribute = params[:attribute].to_s
|
|
141
146
|
@form_element = "simple_file_field"
|
|
142
147
|
@update_span = params[:update]
|
|
148
|
+
authorize!(action, @object) if cancan_enabled?
|
|
143
149
|
unless inline_forms_file_slot_listed?(@object, @attribute)
|
|
144
150
|
head :bad_request
|
|
145
151
|
return false
|
|
@@ -84,6 +84,11 @@ class InlineFormsController < ApplicationController
|
|
|
84
84
|
format.html do
|
|
85
85
|
if @parent_class.present?
|
|
86
86
|
render_nested_associated_list_html
|
|
87
|
+
else
|
|
88
|
+
# No top-level page for this resource. An explicit answer: an
|
|
89
|
+
# empty block would fall through to implicit rendering and raise
|
|
90
|
+
# MissingExactTemplate (500).
|
|
91
|
+
head :not_found
|
|
87
92
|
end
|
|
88
93
|
end
|
|
89
94
|
else
|
|
@@ -116,7 +121,7 @@ class InlineFormsController < ApplicationController
|
|
|
116
121
|
|
|
117
122
|
@object.inline_forms_attribute_list = @inline_forms_attribute_list if @inline_forms_attribute_list
|
|
118
123
|
respond_to do |format|
|
|
119
|
-
format.html { render_turbo_new
|
|
124
|
+
format.html { html_list_flow_allowed? ? render_turbo_new : head(:bad_request) }
|
|
120
125
|
end
|
|
121
126
|
end
|
|
122
127
|
|
|
@@ -139,6 +144,11 @@ class InlineFormsController < ApplicationController
|
|
|
139
144
|
def create
|
|
140
145
|
@object ||= @Klass.new
|
|
141
146
|
@update_span = params[:update]
|
|
147
|
+
@parent_class = params[:parent_class]
|
|
148
|
+
# Refuse before anything is written: the responses below only exist for
|
|
149
|
+
# a request from a list or nested-list frame.
|
|
150
|
+
return head :bad_request unless html_list_flow_allowed?
|
|
151
|
+
|
|
142
152
|
attributes = @inline_forms_attribute_list || @object.inline_forms_attribute_list
|
|
143
153
|
attributes.each do | attribute, form_element |
|
|
144
154
|
# Skip a row whose column is not migrated yet (model edited, migration
|
|
@@ -149,7 +159,6 @@ class InlineFormsController < ApplicationController
|
|
|
149
159
|
InlineForms.assert_plain_text_column!(object: @object, attribute: attribute, form_element: form_element)
|
|
150
160
|
send("#{form_element}_update", @object, attribute) unless form_element == :associated || (cancan_enabled? && cannot?(:read, @object, attribute))
|
|
151
161
|
end
|
|
152
|
-
@parent_class = params[:parent_class]
|
|
153
162
|
@parent_id = params[:parent_id]
|
|
154
163
|
# See #index for the order/search/parent-fk decomposition.
|
|
155
164
|
fk_conditions = nil
|
|
@@ -171,19 +180,17 @@ class InlineFormsController < ApplicationController
|
|
|
171
180
|
created_object = @object
|
|
172
181
|
@object = nil
|
|
173
182
|
respond_to do |format|
|
|
174
|
-
if
|
|
175
|
-
|
|
176
|
-
format.turbo_stream { render_created_row_open_streams(created_object) }
|
|
177
|
-
end
|
|
178
|
-
format.html { render_list_frame_after_save }
|
|
183
|
+
if open_created_row?(created_object, attributes)
|
|
184
|
+
format.turbo_stream { render_created_row_open_streams(created_object) }
|
|
179
185
|
end
|
|
186
|
+
format.html { render_list_frame_after_save }
|
|
180
187
|
end
|
|
181
188
|
else
|
|
182
189
|
flash.now[:header] = [ "Kan #{@object.class.to_s.underscore} niet aanmaken." ]
|
|
183
190
|
flash.now[:error] = @object.errors.to_a
|
|
184
191
|
respond_to do |format|
|
|
185
192
|
@object.inline_forms_attribute_list = attributes
|
|
186
|
-
format.html { render_turbo_new }
|
|
193
|
+
format.html { render_turbo_new }
|
|
187
194
|
end
|
|
188
195
|
end
|
|
189
196
|
end
|
|
@@ -257,10 +264,10 @@ class InlineFormsController < ApplicationController
|
|
|
257
264
|
if @attribute.nil?
|
|
258
265
|
respond_to do |format|
|
|
259
266
|
@attributes = @object.inline_forms_attribute_list
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
267
|
+
format.html do
|
|
268
|
+
next head(:bad_request) unless row_html_turbo_allowed?
|
|
269
|
+
|
|
270
|
+
render_row_turbo(close ? :close : :show)
|
|
264
271
|
end
|
|
265
272
|
end
|
|
266
273
|
else
|
|
@@ -274,9 +281,11 @@ class InlineFormsController < ApplicationController
|
|
|
274
281
|
def soft_delete
|
|
275
282
|
@update_span = params[:update]
|
|
276
283
|
@object = referenced_object
|
|
284
|
+
return head :bad_request unless row_html_turbo_allowed?
|
|
285
|
+
|
|
277
286
|
@object.soft_delete(current_user)
|
|
278
287
|
respond_to do |format|
|
|
279
|
-
format.html { render_row_turbo(:close) }
|
|
288
|
+
format.html { render_row_turbo(:close) }
|
|
280
289
|
end
|
|
281
290
|
end
|
|
282
291
|
|
|
@@ -284,9 +293,11 @@ class InlineFormsController < ApplicationController
|
|
|
284
293
|
def soft_restore
|
|
285
294
|
@update_span = params[:update]
|
|
286
295
|
@object = referenced_object
|
|
296
|
+
return head :bad_request unless row_html_turbo_allowed?
|
|
297
|
+
|
|
287
298
|
@object.soft_restore
|
|
288
299
|
respond_to do |format|
|
|
289
|
-
format.html { render_row_turbo(:close) }
|
|
300
|
+
format.html { render_row_turbo(:close) }
|
|
290
301
|
end
|
|
291
302
|
end
|
|
292
303
|
|
|
@@ -294,14 +305,17 @@ class InlineFormsController < ApplicationController
|
|
|
294
305
|
def destroy
|
|
295
306
|
@update_span = params[:update]
|
|
296
307
|
@object = referenced_object
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
308
|
+
# Both refusals answer before anything is destroyed (they used to render
|
|
309
|
+
# nothing and fall through to implicit rendering: a 500).
|
|
310
|
+
return head :forbidden unless destroy_permitted?
|
|
311
|
+
return head :bad_request unless row_html_turbo_allowed?
|
|
312
|
+
|
|
313
|
+
@object.destroy
|
|
314
|
+
# Capture after destroy: `.last` before destroy was the latest *update*
|
|
315
|
+
# (e.g. a plain_text_area edit), so undo reified the pre-edit state.
|
|
316
|
+
@undo_version = @object.versions.last
|
|
317
|
+
respond_to do |format|
|
|
318
|
+
format.html { render_row_turbo_destroyed }
|
|
305
319
|
end
|
|
306
320
|
end
|
|
307
321
|
|
|
@@ -327,6 +341,7 @@ class InlineFormsController < ApplicationController
|
|
|
327
341
|
# has the Devise+roles system, otherwise no extra gate beyond CanCan.
|
|
328
342
|
return head :forbidden unless destroy_permitted?
|
|
329
343
|
return head :not_found unless @parent
|
|
344
|
+
return head :bad_request unless row_html_turbo_allowed?
|
|
330
345
|
|
|
331
346
|
@object = reify_for_revert(@version)
|
|
332
347
|
# PaperTrail::Version#reify returns nil for `create` events because
|
|
@@ -354,8 +369,7 @@ class InlineFormsController < ApplicationController
|
|
|
354
369
|
@parent = item || @parent
|
|
355
370
|
return head :not_found unless @parent
|
|
356
371
|
end
|
|
357
|
-
return render_revert_response
|
|
358
|
-
return
|
|
372
|
+
return render_revert_response
|
|
359
373
|
end
|
|
360
374
|
if defined?(ActionText::RichText) && @object.is_a?(ActionText::RichText)
|
|
361
375
|
@rich_text_record = @object
|
|
@@ -374,7 +388,7 @@ class InlineFormsController < ApplicationController
|
|
|
374
388
|
end
|
|
375
389
|
@parent.reload
|
|
376
390
|
end
|
|
377
|
-
render_revert_response
|
|
391
|
+
render_revert_response
|
|
378
392
|
end
|
|
379
393
|
|
|
380
394
|
private
|
|
@@ -27,8 +27,9 @@ module InlineFormsGemFiles
|
|
|
27
27
|
# the token leaked into `gem build` on the release machine. Exclude it hard,
|
|
28
28
|
# independent of any ignore configuration.
|
|
29
29
|
#
|
|
30
|
-
# Same for
|
|
31
|
-
#
|
|
30
|
+
# Same for Claude Code files: .claude/ (untracked, per checkout:
|
|
31
|
+
# launch.json, settings.local.json, worktrees) and CLAUDE.md (committed so
|
|
32
|
+
# worktrees see it, but repo instructions, not gem content).
|
|
32
33
|
EXCLUDED_FILE_PREFIXES = %w[
|
|
33
34
|
stuff/
|
|
34
35
|
.claude/
|
data/lib/inline_forms/version.rb
CHANGED
|
@@ -5,10 +5,13 @@ class ApplicationController < ActionController::Base
|
|
|
5
5
|
# create/new HTML responses) calls `current_user.name` and
|
|
6
6
|
# `current_user.role?`, so a nil current_user cannot render it. The stub is
|
|
7
7
|
# superadmin so `destroy_permitted?` allows hard destroy, matching the
|
|
8
|
-
# generated apps' test user.
|
|
8
|
+
# generated apps' test user. A test can switch that off with
|
|
9
|
+
# dummy_user_superadmin (reset in InlineFormsIntegrationTestCase#setup).
|
|
10
|
+
mattr_accessor :dummy_user_superadmin, default: true
|
|
11
|
+
|
|
9
12
|
DummyUser = Struct.new(:id, :name) do
|
|
10
13
|
def role?(role)
|
|
11
|
-
role.to_sym == :superadmin
|
|
14
|
+
role.to_sym == :superadmin && ApplicationController.dummy_user_superadmin
|
|
12
15
|
end
|
|
13
16
|
end
|
|
14
17
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Child of Machine that has no top-level HTML surface: it is only shown
|
|
4
|
+
# nested in its machine (like StProject's Dagrapportage inside a Client tab).
|
|
5
|
+
class DailyReport < ApplicationRecord
|
|
6
|
+
belongs_to :machine, optional: true
|
|
7
|
+
|
|
8
|
+
def self.not_accessible_through_html?
|
|
9
|
+
true
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def _presentation
|
|
13
|
+
"#{name}"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def inline_forms_attribute_list
|
|
17
|
+
@inline_forms_attribute_list ||= [
|
|
18
|
+
[ :name, :text_field ]
|
|
19
|
+
]
|
|
20
|
+
end
|
|
21
|
+
end
|
data/test/dummy/config/routes.rb
CHANGED
|
@@ -20,6 +20,11 @@ Rails.application.routes.draw do
|
|
|
20
20
|
get "list_versions", on: :member
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
+
resources :daily_reports do
|
|
24
|
+
post "revert", on: :member
|
|
25
|
+
get "list_versions", on: :member
|
|
26
|
+
end
|
|
27
|
+
|
|
23
28
|
resources :gizmos do
|
|
24
29
|
post "revert", on: :member
|
|
25
30
|
get "list_versions", on: :member
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../integration_test_helper"
|
|
4
|
+
|
|
5
|
+
# Every HTML request the engine decides not to serve gets an explicit answer:
|
|
6
|
+
# a top-level list of a not_accessible_through_html? model is a 404, a
|
|
7
|
+
# new/create/show/destroy/revert without the frame context the gem needs is a
|
|
8
|
+
# 400. Before, the format.html block rendered nothing (or was not registered),
|
|
9
|
+
# so Rails fell through to implicit rendering: ActionController::
|
|
10
|
+
# MissingExactTemplate (500; StProject's GET /dagrapportages) or UnknownFormat
|
|
11
|
+
# (406). create, destroy and revert also refuse before writing anything.
|
|
12
|
+
#
|
|
13
|
+
# DailyReport is the not_accessible_through_html? child of Machine; Machine is
|
|
14
|
+
# a normal (accessible) model.
|
|
15
|
+
class HtmlRefusalTest < InlineFormsIntegrationTestCase
|
|
16
|
+
setup do
|
|
17
|
+
@machine = Machine.create!(name: "Press")
|
|
18
|
+
@report = DailyReport.create!(name: "Monday", machine: @machine)
|
|
19
|
+
@list_frame = "machine_#{@machine.id}_daily_reports"
|
|
20
|
+
@row_frame = "machine_#{@machine.id}_daily_report_#{@report.id}"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def nested_params(update = @list_frame)
|
|
24
|
+
{ update: update, parent_class: "Machine", parent_id: @machine.id }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def stream_headers(frame_id)
|
|
28
|
+
frame_headers(frame_id).merge("Accept" => "text/vnd.turbo-stream.html")
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# The refusal ran after authorization, so CanCan's check_authorization (an
|
|
32
|
+
# after_action in host apps) is satisfied.
|
|
33
|
+
def assert_refused(status)
|
|
34
|
+
assert_response status
|
|
35
|
+
assert controller.instance_variable_defined?(:@_authorized),
|
|
36
|
+
"the refused request must still count as authorized (check_authorization)"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# ---- not_accessible_through_html? model, top level -----------------------
|
|
40
|
+
|
|
41
|
+
test "the top-level list of a not_accessible_through_html? model is a 404" do
|
|
42
|
+
get daily_reports_path
|
|
43
|
+
assert_refused :not_found
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
test "show without a nested row frame is a 400" do
|
|
47
|
+
get daily_report_path(@report)
|
|
48
|
+
assert_refused :bad_request
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
test "new without update and parent_class is a 400" do
|
|
52
|
+
get new_daily_report_path
|
|
53
|
+
assert_refused :bad_request
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
test "create without update and parent_class is a 400 and creates nothing" do
|
|
57
|
+
assert_no_difference -> { DailyReport.count } do
|
|
58
|
+
post daily_reports_path, params: { name: "Tuesday" }
|
|
59
|
+
end
|
|
60
|
+
assert_refused :bad_request
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
test "destroy without a nested row frame is a 400 and keeps the record" do
|
|
64
|
+
delete daily_report_path(@report)
|
|
65
|
+
assert_refused :bad_request
|
|
66
|
+
assert DailyReport.exists?(@report.id)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
test "revert without a nested row frame is a 400 and changes nothing" do
|
|
70
|
+
@report.update!(name: "Changed")
|
|
71
|
+
version = @report.versions.where(event: "update").last
|
|
72
|
+
|
|
73
|
+
post revert_daily_report_path(version.id, update: "daily_report_#{@report.id}"),
|
|
74
|
+
headers: stream_headers("daily_report_#{@report.id}")
|
|
75
|
+
assert_refused :bad_request
|
|
76
|
+
assert_equal "Changed", @report.reload.name
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# ---- the nested forms still work (regression guard) ----------------------
|
|
80
|
+
|
|
81
|
+
test "the nested list, new form, create, row and destroy still render" do
|
|
82
|
+
get daily_reports_path(nested_params), headers: frame_headers(@list_frame)
|
|
83
|
+
assert_response :success
|
|
84
|
+
assert_includes response.body, "Monday"
|
|
85
|
+
|
|
86
|
+
get new_daily_report_path(nested_params), headers: frame_headers(@list_frame)
|
|
87
|
+
assert_response :success
|
|
88
|
+
|
|
89
|
+
assert_difference -> { DailyReport.count }, +1 do
|
|
90
|
+
post daily_reports_path(nested_params), params: { name: "Tuesday" }, headers: frame_headers(@list_frame)
|
|
91
|
+
end
|
|
92
|
+
assert_response :success
|
|
93
|
+
assert_equal @machine.id, DailyReport.find_by!(name: "Tuesday").machine_id
|
|
94
|
+
|
|
95
|
+
get daily_report_path(@report, update: @row_frame), headers: frame_headers(@row_frame)
|
|
96
|
+
assert_response :success
|
|
97
|
+
assert_includes response.body, %(<turbo-frame id="#{@row_frame}">)
|
|
98
|
+
|
|
99
|
+
delete daily_report_path(@report, update: @row_frame), headers: frame_headers(@row_frame)
|
|
100
|
+
assert_response :success
|
|
101
|
+
refute DailyReport.exists?(@report.id)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
test "a nested revert still restores the record" do
|
|
105
|
+
@report.update!(name: "Changed")
|
|
106
|
+
version = @report.versions.where(event: "update").last
|
|
107
|
+
|
|
108
|
+
post revert_daily_report_path(version.id, update: @row_frame), headers: stream_headers(@row_frame)
|
|
109
|
+
assert_response :success
|
|
110
|
+
assert_equal "Monday", @report.reload.name
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# ---- accessible model ------------------------------------------------------
|
|
114
|
+
|
|
115
|
+
test "an accessible model's list and row still render" do
|
|
116
|
+
get machines_path
|
|
117
|
+
assert_response :success
|
|
118
|
+
assert_includes response.body, "Press"
|
|
119
|
+
|
|
120
|
+
get machine_path(@machine)
|
|
121
|
+
assert_response :success
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
test "an accessible model's new and create without update are a 400; nothing is created" do
|
|
125
|
+
get new_machine_path
|
|
126
|
+
assert_refused :bad_request
|
|
127
|
+
|
|
128
|
+
assert_no_difference -> { Machine.count } do
|
|
129
|
+
post machines_path, params: { name: "Lathe" }
|
|
130
|
+
end
|
|
131
|
+
assert_refused :bad_request
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
test "destroy by a user who may not hard-destroy is a 403 and keeps the record" do
|
|
135
|
+
ApplicationController.dummy_user_superadmin = false
|
|
136
|
+
frame = "machine_#{@machine.id}"
|
|
137
|
+
|
|
138
|
+
delete machine_path(@machine, update: frame), headers: frame_headers(frame)
|
|
139
|
+
assert_refused :forbidden
|
|
140
|
+
assert Machine.exists?(@machine.id)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# ---- file slots ------------------------------------------------------------
|
|
144
|
+
|
|
145
|
+
test "a refused file-slot request still counts as authorized" do
|
|
146
|
+
document = Document.create!(title: "Plan")
|
|
147
|
+
frame = "document_#{document.id}_filename"
|
|
148
|
+
|
|
149
|
+
post remove_file_document_path(document, attribute: "title", update: frame), headers: stream_headers(frame)
|
|
150
|
+
assert_refused :bad_request
|
|
151
|
+
end
|
|
152
|
+
end
|
|
@@ -49,6 +49,15 @@ ActiveRecord::Schema.define do
|
|
|
49
49
|
end
|
|
50
50
|
add_index :parts, :machine_id
|
|
51
51
|
|
|
52
|
+
# DailyReport: a not_accessible_through_html? child of Machine (shaped like
|
|
53
|
+
# StProject's Dagrapportage). See test/integration/html_refusal_test.rb.
|
|
54
|
+
create_table :daily_reports, force: true do |t|
|
|
55
|
+
t.string :name
|
|
56
|
+
t.integer :machine_id
|
|
57
|
+
t.timestamps
|
|
58
|
+
end
|
|
59
|
+
add_index :daily_reports, :machine_id
|
|
60
|
+
|
|
52
61
|
# Gizmo intentionally has NO `pending_note` column: its attribute_list
|
|
53
62
|
# references one, modeling the inline_forms_addto pre-migrate window that
|
|
54
63
|
# InlineForms.attribute_pending_migration? gates. See
|
|
@@ -163,9 +172,11 @@ class InlineFormsIntegrationTestCase < ActionDispatch::IntegrationTest
|
|
|
163
172
|
|
|
164
173
|
setup do
|
|
165
174
|
Ability.restrictions = nil
|
|
175
|
+
ApplicationController.dummy_user_superadmin = true
|
|
166
176
|
Widget.delete_all
|
|
167
177
|
Kind.delete_all
|
|
168
178
|
Part.delete_all
|
|
179
|
+
DailyReport.delete_all
|
|
169
180
|
Machine.delete_all
|
|
170
181
|
Gizmo.delete_all
|
|
171
182
|
Dossier.delete_all
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: inline_forms
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 8.1.
|
|
4
|
+
version: 8.1.56
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ace Suares
|
|
@@ -282,6 +282,7 @@ files:
|
|
|
282
282
|
- test/archived_form_elements_test.rb
|
|
283
283
|
- test/attribute_list_test.rb
|
|
284
284
|
- test/dummy/app/controllers/application_controller.rb
|
|
285
|
+
- test/dummy/app/controllers/daily_reports_controller.rb
|
|
285
286
|
- test/dummy/app/controllers/documents_controller.rb
|
|
286
287
|
- test/dummy/app/controllers/dossiers_controller.rb
|
|
287
288
|
- test/dummy/app/controllers/gizmos_controller.rb
|
|
@@ -292,6 +293,7 @@ files:
|
|
|
292
293
|
- test/dummy/app/helpers/application_helper.rb
|
|
293
294
|
- test/dummy/app/models/ability.rb
|
|
294
295
|
- test/dummy/app/models/application_record.rb
|
|
296
|
+
- test/dummy/app/models/daily_report.rb
|
|
295
297
|
- test/dummy/app/models/document.rb
|
|
296
298
|
- test/dummy/app/models/dossier.rb
|
|
297
299
|
- test/dummy/app/models/gizmo.rb
|
|
@@ -314,6 +316,7 @@ files:
|
|
|
314
316
|
- test/inline_forms_generator_test.rb
|
|
315
317
|
- test/integration/field_authorization_test.rb
|
|
316
318
|
- test/integration/file_slots_test.rb
|
|
319
|
+
- test/integration/html_refusal_test.rb
|
|
317
320
|
- test/integration/inline_forms_crud_test.rb
|
|
318
321
|
- test/integration/layout_title_test.rb
|
|
319
322
|
- test/integration/legacy_elements_test.rb
|
|
@@ -363,6 +366,7 @@ test_files:
|
|
|
363
366
|
- test/archived_form_elements_test.rb
|
|
364
367
|
- test/attribute_list_test.rb
|
|
365
368
|
- test/dummy/app/controllers/application_controller.rb
|
|
369
|
+
- test/dummy/app/controllers/daily_reports_controller.rb
|
|
366
370
|
- test/dummy/app/controllers/documents_controller.rb
|
|
367
371
|
- test/dummy/app/controllers/dossiers_controller.rb
|
|
368
372
|
- test/dummy/app/controllers/gizmos_controller.rb
|
|
@@ -373,6 +377,7 @@ test_files:
|
|
|
373
377
|
- test/dummy/app/helpers/application_helper.rb
|
|
374
378
|
- test/dummy/app/models/ability.rb
|
|
375
379
|
- test/dummy/app/models/application_record.rb
|
|
380
|
+
- test/dummy/app/models/daily_report.rb
|
|
376
381
|
- test/dummy/app/models/document.rb
|
|
377
382
|
- test/dummy/app/models/dossier.rb
|
|
378
383
|
- test/dummy/app/models/gizmo.rb
|
|
@@ -395,6 +400,7 @@ test_files:
|
|
|
395
400
|
- test/inline_forms_generator_test.rb
|
|
396
401
|
- test/integration/field_authorization_test.rb
|
|
397
402
|
- test/integration/file_slots_test.rb
|
|
403
|
+
- test/integration/html_refusal_test.rb
|
|
398
404
|
- test/integration/inline_forms_crud_test.rb
|
|
399
405
|
- test/integration/layout_title_test.rb
|
|
400
406
|
- test/integration/legacy_elements_test.rb
|