fe 2.3.2 → 2.4.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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +147 -3
  3. data/app/controllers/concerns/fe/access_control_concern.rb +39 -0
  4. data/app/controllers/concerns/fe/admin/question_sheets_controller_concern.rb +2 -1
  5. data/app/controllers/concerns/fe/answer_pages_controller_concern.rb +3 -1
  6. data/app/controllers/concerns/fe/answer_sheets_controller_concern.rb +4 -1
  7. data/app/controllers/fe/admin/elements_controller.rb +4 -3
  8. data/app/controllers/fe/admin/email_templates_controller.rb +2 -1
  9. data/app/controllers/fe/admin/question_pages_controller.rb +2 -1
  10. data/app/controllers/fe/reference_pages_controller.rb +4 -0
  11. data/app/controllers/fe/reference_sheets_controller.rb +8 -2
  12. data/app/controllers/fe/references_controller.rb +4 -0
  13. data/app/controllers/fe/submit_pages_controller.rb +2 -0
  14. data/app/views/fe/answer_pages/update.js.erb +2 -1
  15. data/app/views/fe/questions/fe/_attachment_field.html.erb +8 -8
  16. data/lib/fe/access_control.rb +72 -0
  17. data/lib/fe/engine.rb +4 -0
  18. data/lib/fe/version.rb +1 -1
  19. data/lib/fe.rb +1 -0
  20. data/spec/controllers/fe/admin/elements_controller_spec.rb +29 -0
  21. data/spec/controllers/fe/admin/email_templates_controller_spec.rb +8 -0
  22. data/spec/controllers/fe/admin/question_pages_controller_spec.rb +9 -0
  23. data/spec/controllers/fe/admin/question_sheets_controller_spec.rb +30 -0
  24. data/spec/controllers/fe/answer_pages_controller_spec.rb +142 -0
  25. data/spec/controllers/fe/answer_sheets_controller_spec.rb +114 -0
  26. data/spec/controllers/fe/reference_pages_controller_spec.rb +32 -0
  27. data/spec/controllers/fe/reference_sheets_controller_spec.rb +88 -1
  28. data/spec/controllers/fe/references_controller_spec.rb +29 -1
  29. data/spec/controllers/fe/submit_pages_controller_spec.rb +44 -1
  30. data/spec/dummy/app/controllers/application_controller.rb +8 -0
  31. data/spec/dummy/config/initializers/fe.rb +2 -0
  32. data/spec/lib/fe/access_control_spec.rb +173 -0
  33. metadata +10 -8
  34. data/spec/controllers/fe/reference_pages_controller.rb +0 -4
  35. data/spec/dummy/log/test.log +0 -9115
  36. data/spec/dummy/tmp/local_secret.txt +0 -1
@@ -0,0 +1,173 @@
1
+ require 'rails_helper'
2
+
3
+ describe Fe do
4
+ describe '.allowed_answer_sheet_types' do
5
+ after { Fe.answer_sheet_types = nil }
6
+
7
+ it 'defaults to the answer sheet class and reference sheet' do
8
+ expect(Fe.allowed_answer_sheet_types).to eq(['Fe::Application', 'Fe::ReferenceSheet'])
9
+ end
10
+
11
+ it 'uses answer_sheet_types when set' do
12
+ Fe.answer_sheet_types = ['Foo']
13
+ expect(Fe.allowed_answer_sheet_types).to eq(['Foo'])
14
+ end
15
+ end
16
+
17
+ describe '.answer_sheet_class_for' do
18
+ it 'returns the default answer sheet class for a blank name' do
19
+ expect(Fe.answer_sheet_class_for(nil)).to eq(Fe::Application)
20
+ expect(Fe.answer_sheet_class_for('')).to eq(Fe::Application)
21
+ end
22
+
23
+ it 'returns an allowed class' do
24
+ expect(Fe.answer_sheet_class_for('Fe::ReferenceSheet')).to eq(Fe::ReferenceSheet)
25
+ end
26
+
27
+ ['Kernel', 'Fe::Element', '::Fe::Application', ' Fe::Application', 'fe::application'].each do |name|
28
+ it "rejects #{name.inspect}" do
29
+ expect { Fe.answer_sheet_class_for(name) }.to raise_error(ActiveRecord::RecordNotFound)
30
+ end
31
+ end
32
+ end
33
+
34
+ describe '.element_class_for' do
35
+ it 'returns an allowed class' do
36
+ expect(Fe.element_class_for('Fe::TextField')).to eq(Fe::TextField)
37
+ end
38
+
39
+ ['Fe::Question', 'Fe::Application', 'Kernel'].each do |name|
40
+ it "rejects #{name.inspect}" do
41
+ expect { Fe.element_class_for(name) }.to raise_error(ActiveRecord::RecordNotFound)
42
+ end
43
+ end
44
+ end
45
+
46
+ describe '.access_key_matches?' do
47
+ let(:sheet) { build(:reference_sheet, access_key: 'abc123') }
48
+
49
+ it 'is true for the sheet key' do
50
+ expect(Fe.access_key_matches?(sheet, 'abc123')).to be true
51
+ end
52
+
53
+ ['wrong', nil, '', ['abc123'], { 'abc123' => 'v' }].each do |key|
54
+ it "is false for #{key.inspect}" do
55
+ expect(Fe.access_key_matches?(sheet, key)).to be false
56
+ end
57
+ end
58
+
59
+ it 'is false when the sheet has no access key' do
60
+ sheet.access_key = nil
61
+ expect(Fe.access_key_matches?(sheet, 'abc123')).to be false
62
+ sheet.access_key = ''
63
+ expect(Fe.access_key_matches?(sheet, '')).to be false
64
+ end
65
+ end
66
+
67
+ describe '.verify_access_hooks!' do
68
+ let(:answer_hook) { :fe_can_use_answer_sheet? }
69
+
70
+ def stub_lists(answer: [], admin: [])
71
+ stub_const('Fe::ANSWER_SHEET_CONTROLLERS', answer)
72
+ stub_const('Fe::ADMIN_CONTROLLERS', admin)
73
+ end
74
+
75
+ it 'passes for the dummy app with the real lists' do
76
+ expect(Fe.verify_access_hooks!).to be_nil
77
+ end
78
+
79
+ it 'raises when an answer sheet controller has no hook' do
80
+ stub_const('FeNoHooksController', Class.new)
81
+ stub_lists(answer: %w[FeNoHooksController])
82
+
83
+ expect { Fe.verify_access_hooks! }.to raise_error(Fe::MissingAccessHook) { |e|
84
+ expect(e.message).to include('fe_can_use_answer_sheet?', 'FeNoHooksController', 'Access control')
85
+ }
86
+ end
87
+
88
+ it 'raises when an admin controller has no fe_admin?' do
89
+ stub_const('FeNoAdminController', Class.new)
90
+ stub_lists(admin: %w[FeNoAdminController])
91
+
92
+ expect { Fe.verify_access_hooks! }.to raise_error(Fe::MissingAccessHook) { |e|
93
+ expect(e.message).to include('fe_admin?', 'FeNoAdminController')
94
+ }
95
+ end
96
+
97
+ it 'requires fe_admin? on the answer sheets controller' do
98
+ klass = Class.new do
99
+ protected
100
+
101
+ def fe_can_use_answer_sheet?(*) = true
102
+ end
103
+ stub_const('Fe::AnswerSheetsController', klass)
104
+ stub_lists(answer: %w[Fe::AnswerSheetsController])
105
+
106
+ expect { Fe.verify_access_hooks! }.to raise_error(Fe::MissingAccessHook) { |e|
107
+ expect(e.message).to include('fe_admin?', 'Fe::AnswerSheetsController')
108
+ expect(e.message).not_to include('fe_can_use_answer_sheet?')
109
+ }
110
+ end
111
+
112
+ it 'names every missing method and class in one error' do
113
+ stub_const('FeNoHooksController', Class.new)
114
+ stub_const('FeNoAdminController', Class.new)
115
+ stub_lists(answer: %w[FeNoHooksController], admin: %w[FeNoAdminController])
116
+
117
+ expect { Fe.verify_access_hooks! }.to raise_error(Fe::MissingAccessHook) { |e|
118
+ expect(e.message).to include('fe_can_use_answer_sheet?', 'FeNoHooksController', 'fe_admin?', 'FeNoAdminController')
119
+ }
120
+ end
121
+
122
+ it 'passes when both hooks are inherited from a parent' do
123
+ parent = Class.new do
124
+ protected
125
+
126
+ def fe_can_use_answer_sheet?(*) = true
127
+ def fe_admin? = true
128
+ end
129
+ stub_const('FeChildAnswerController', Class.new(parent))
130
+ stub_const('FeChildAdminController', Class.new(parent))
131
+ stub_lists(answer: %w[FeChildAnswerController], admin: %w[FeChildAdminController])
132
+
133
+ expect(Fe.verify_access_hooks!).to be_nil
134
+ end
135
+
136
+ it 'passes when the hook comes from a prepended module' do
137
+ mod = Module.new do
138
+ def fe_can_use_answer_sheet?(*) = true
139
+ end
140
+ klass = Class.new
141
+ klass.prepend(mod)
142
+ stub_const('FePrependedController', klass)
143
+ stub_lists(answer: %w[FePrependedController])
144
+
145
+ expect(Fe.verify_access_hooks!).to be_nil
146
+ end
147
+
148
+ it 'passes when the hook is a protected method on the class itself' do
149
+ klass = Class.new do
150
+ protected
151
+
152
+ def fe_admin? = true
153
+ end
154
+ stub_const('FeProtectedController', klass)
155
+ stub_lists(admin: %w[FeProtectedController])
156
+
157
+ expect(Fe.verify_access_hooks!).to be_nil
158
+ end
159
+
160
+ it 'passes when the hooks are private methods' do
161
+ klass = Class.new do
162
+ private
163
+
164
+ def fe_can_use_answer_sheet?(*) = true
165
+ def fe_admin? = true
166
+ end
167
+ stub_const('FePrivateController', klass)
168
+ stub_lists(answer: %w[FePrivateController], admin: %w[FePrivateController])
169
+
170
+ expect(Fe.verify_access_hooks!).to be_nil
171
+ end
172
+ end
173
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fe
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.2
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - CruGlobal
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-10-30 00:00:00.000000000 Z
11
+ date: 2026-11-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -322,6 +322,7 @@ files:
322
322
  - app/assets/stylesheets/fe/fe.screen.css.scss.erb
323
323
  - app/assets/stylesheets/fe/reset.css
324
324
  - app/assets/stylesheets/fe/validation.css
325
+ - app/controllers/concerns/fe/access_control_concern.rb
325
326
  - app/controllers/concerns/fe/admin/question_sheets_controller_concern.rb
326
327
  - app/controllers/concerns/fe/answer_pages_controller_concern.rb
327
328
  - app/controllers/concerns/fe/answer_sheets_controller_concern.rb
@@ -533,6 +534,7 @@ files:
533
534
  - lib/access_key_generator.rb
534
535
  - lib/distinct_distinct_patch.rb
535
536
  - lib/fe.rb
537
+ - lib/fe/access_control.rb
536
538
  - lib/fe/engine.rb
537
539
  - lib/fe/version.rb
538
540
  - lib/tasks/fe_tasks.rake
@@ -543,7 +545,7 @@ files:
543
545
  - spec/controllers/fe/answer_pages_controller_spec.rb
544
546
  - spec/controllers/fe/answer_sheets_controller_spec.rb
545
547
  - spec/controllers/fe/optimistic_concurrency_spec.rb
546
- - spec/controllers/fe/reference_pages_controller.rb
548
+ - spec/controllers/fe/reference_pages_controller_spec.rb
547
549
  - spec/controllers/fe/reference_sheets_controller_spec.rb
548
550
  - spec/controllers/fe/references_controller_spec.rb
549
551
  - spec/controllers/fe/submit_pages_controller_spec.rb
@@ -573,6 +575,7 @@ files:
573
575
  - spec/dummy/config/initializers/backtrace_silencers.rb
574
576
  - spec/dummy/config/initializers/cookies_serializer.rb
575
577
  - spec/dummy/config/initializers/fast_gettext.rb
578
+ - spec/dummy/config/initializers/fe.rb
576
579
  - spec/dummy/config/initializers/filter_parameter_logging.rb
577
580
  - spec/dummy/config/initializers/inflections.rb
578
581
  - spec/dummy/config/initializers/mime_types.rb
@@ -611,12 +614,10 @@ files:
611
614
  - spec/dummy/db/migrate/20160204164613_add_visible_and_visibility_cache_key_to_reference_sheets.fe_engine.rb
612
615
  - spec/dummy/db/migrate/20181108201746_create_versions.rb
613
616
  - spec/dummy/db/schema.rb
614
- - spec/dummy/log/test.log
615
617
  - spec/dummy/public/404.html
616
618
  - spec/dummy/public/422.html
617
619
  - spec/dummy/public/500.html
618
620
  - spec/dummy/public/favicon.ico
619
- - spec/dummy/tmp/local_secret.txt
620
621
  - spec/factories/answer_sheet_question_sheets.rb
621
622
  - spec/factories/answer_sheets.rb
622
623
  - spec/factories/answers.rb
@@ -639,6 +640,7 @@ files:
639
640
  - spec/factories/reference_questions.rb
640
641
  - spec/factories/reference_sheets.rb
641
642
  - spec/jobs/fe/update_reference_sheet_visibility_job_spec.rb
643
+ - spec/lib/fe/access_control_spec.rb
642
644
  - spec/mailers/fe/notifier_spec.rb
643
645
  - spec/models/fe/answer_sheet_question_sheet_spec.rb
644
646
  - spec/models/fe/answer_spec.rb
@@ -701,7 +703,7 @@ test_files:
701
703
  - spec/controllers/fe/answer_pages_controller_spec.rb
702
704
  - spec/controllers/fe/answer_sheets_controller_spec.rb
703
705
  - spec/controllers/fe/optimistic_concurrency_spec.rb
704
- - spec/controllers/fe/reference_pages_controller.rb
706
+ - spec/controllers/fe/reference_pages_controller_spec.rb
705
707
  - spec/controllers/fe/reference_sheets_controller_spec.rb
706
708
  - spec/controllers/fe/references_controller_spec.rb
707
709
  - spec/controllers/fe/submit_pages_controller_spec.rb
@@ -730,6 +732,7 @@ test_files:
730
732
  - spec/dummy/config/initializers/backtrace_silencers.rb
731
733
  - spec/dummy/config/initializers/cookies_serializer.rb
732
734
  - spec/dummy/config/initializers/fast_gettext.rb
735
+ - spec/dummy/config/initializers/fe.rb
733
736
  - spec/dummy/config/initializers/filter_parameter_logging.rb
734
737
  - spec/dummy/config/initializers/inflections.rb
735
738
  - spec/dummy/config/initializers/mime_types.rb
@@ -769,12 +772,10 @@ test_files:
769
772
  - spec/dummy/db/migrate/20160204164613_add_visible_and_visibility_cache_key_to_reference_sheets.fe_engine.rb
770
773
  - spec/dummy/db/migrate/20181108201746_create_versions.rb
771
774
  - spec/dummy/db/schema.rb
772
- - spec/dummy/log/test.log
773
775
  - spec/dummy/public/404.html
774
776
  - spec/dummy/public/422.html
775
777
  - spec/dummy/public/500.html
776
778
  - spec/dummy/public/favicon.ico
777
- - spec/dummy/tmp/local_secret.txt
778
779
  - spec/factories/answer_sheet_question_sheets.rb
779
780
  - spec/factories/answer_sheets.rb
780
781
  - spec/factories/answers.rb
@@ -797,6 +798,7 @@ test_files:
797
798
  - spec/factories/reference_questions.rb
798
799
  - spec/factories/reference_sheets.rb
799
800
  - spec/jobs/fe/update_reference_sheet_visibility_job_spec.rb
801
+ - spec/lib/fe/access_control_spec.rb
800
802
  - spec/mailers/fe/notifier_spec.rb
801
803
  - spec/models/fe/answer_sheet_question_sheet_spec.rb
802
804
  - spec/models/fe/answer_spec.rb
@@ -1,4 +0,0 @@
1
- require 'rails_helper'
2
-
3
- describe Fe::ReferencePages do
4
- end