ree_lib 1.3.9 → 1.3.11

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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -0
  3. data/Gemfile.lock +2 -2
  4. data/lib/ree_lib/Packages.schema.json +4 -0
  5. data/lib/ree_lib/packages/ree_audit/.rspec +2 -0
  6. data/lib/ree_lib/packages/ree_audit/package/ree_audit/beans/audit.rb +83 -0
  7. data/lib/ree_lib/packages/ree_audit/package/ree_audit/config.rb +62 -0
  8. data/lib/ree_lib/packages/ree_audit/package/ree_audit/context.rb +44 -0
  9. data/lib/ree_lib/packages/ree_audit/package/ree_audit/event.rb +78 -0
  10. data/lib/ree_lib/packages/ree_audit/package/ree_audit/functions/filter_params.rb +75 -0
  11. data/lib/ree_lib/packages/ree_audit/package/ree_audit/sinks/logger_sink.rb +15 -0
  12. data/lib/ree_lib/packages/ree_audit/package/ree_audit/sinks/null_sink.rb +11 -0
  13. data/lib/ree_lib/packages/ree_audit/package/ree_audit/sinks/sink.rb +10 -0
  14. data/lib/ree_lib/packages/ree_audit/package/ree_audit.rb +37 -0
  15. data/lib/ree_lib/packages/ree_audit/spec/ree_audit/beans/audit_spec.rb +161 -0
  16. data/lib/ree_lib/packages/ree_audit/spec/ree_audit/context_spec.rb +47 -0
  17. data/lib/ree_lib/packages/ree_audit/spec/ree_audit/functions/filter_params_spec.rb +58 -0
  18. data/lib/ree_lib/packages/ree_audit/spec/spec_helper.rb +13 -0
  19. data/lib/ree_lib/packages/ree_dao/package/ree_dao/dataset_extensions.rb +15 -13
  20. data/lib/ree_lib/packages/ree_dao/spec/ree_dao/db_load_performance_mode_spec.rb +190 -0
  21. data/lib/ree_lib/packages/ree_dto/package/ree_dto/dto/dto_class_methods.rb +14 -0
  22. data/lib/ree_lib/packages/ree_dto/spec/ree_dto/build_from_attrs_spec.rb +48 -0
  23. data/lib/ree_lib/packages/ree_mapper/package/ree_mapper/db_load_compiler.rb +145 -0
  24. data/lib/ree_lib/packages/ree_mapper/package/ree_mapper/mapper.rb +25 -0
  25. data/lib/ree_lib/packages/ree_mapper/package/ree_mapper/mapper_strategy.rb +1 -3
  26. data/lib/ree_lib/packages/ree_mapper/package/ree_mapper/types/abstract_type.rb +9 -0
  27. data/lib/ree_lib/packages/ree_mapper/package/ree_mapper/types/any.rb +4 -0
  28. data/lib/ree_lib/packages/ree_mapper/package/ree_mapper/types/bool.rb +4 -0
  29. data/lib/ree_lib/packages/ree_mapper/package/ree_mapper/types/date.rb +5 -0
  30. data/lib/ree_lib/packages/ree_mapper/package/ree_mapper/types/date_time.rb +4 -0
  31. data/lib/ree_lib/packages/ree_mapper/package/ree_mapper/types/float.rb +4 -0
  32. data/lib/ree_lib/packages/ree_mapper/package/ree_mapper/types/integer.rb +4 -0
  33. data/lib/ree_lib/packages/ree_mapper/package/ree_mapper/types/rational.rb +4 -0
  34. data/lib/ree_lib/packages/ree_mapper/package/ree_mapper/types/string.rb +4 -0
  35. data/lib/ree_lib/packages/ree_mapper/package/ree_mapper/types/time.rb +5 -0
  36. data/lib/ree_lib/packages/ree_mapper/package/ree_mapper/wrappers/abstract_wrapper.rb +5 -0
  37. data/lib/ree_lib/packages/ree_mapper/package/ree_mapper.rb +6 -0
  38. data/lib/ree_lib/packages/ree_mapper/spec/ree_mapper/benchmarks/db_load_benchmark_spec.rb +218 -0
  39. data/lib/ree_lib/packages/ree_mapper/spec/ree_mapper/db_load_performance_mode_spec.rb +339 -0
  40. data/lib/ree_lib/packages/ree_roda/package/ree_roda/plugins/ree_routes.rb +29 -1
  41. data/lib/ree_lib/packages/ree_roda/package/ree_roda.rb +1 -0
  42. data/lib/ree_lib/packages/ree_roda/spec/ree_roda/plugins/ree_routes_audit_spec.rb +281 -0
  43. data/lib/ree_lib/packages/ree_roda/spec/spec_helper.rb +3 -0
  44. data/lib/ree_lib/packages/ree_routes/package/ree_routes/route.rb +11 -1
  45. data/lib/ree_lib/packages/ree_routes/package/ree_routes/route_builder.rb +7 -0
  46. data/lib/ree_lib/packages/ree_routes/spec/ree_routes/dsl_spec.rb +43 -0
  47. data/lib/ree_lib/version.rb +1 -1
  48. metadata +21 -1
@@ -0,0 +1,281 @@
1
+ require "rack/test"
2
+
3
+ package_require("ree_roda/app")
4
+ package_require("ree_actions/dsl")
5
+ package_require("ree_routes/dsl")
6
+ package_require("ree_roda/plugins/ree_routes")
7
+ package_require("ree_audit/beans/audit")
8
+
9
+ require "warden"
10
+
11
+ RSpec.describe "ree_routes audit" do
12
+ include Rack::Test::Methods
13
+
14
+ link :audit, from: :ree_audit
15
+
16
+ before :all do
17
+ Ree.enable_irb_mode
18
+
19
+ module ReeRodaAuditTest
20
+ include Ree::PackageDSL
21
+
22
+ package do
23
+ depends_on :ree_actions
24
+ depends_on :ree_errors
25
+ end
26
+ end
27
+
28
+ class ReeRodaAuditTest::OkCmd
29
+ include ReeActions::DSL
30
+
31
+ action :ok_cmd
32
+
33
+ def call(access, attrs)
34
+ {result: "ok"}
35
+ end
36
+ end
37
+
38
+ class ReeRodaAuditTest::DeniedCmd
39
+ include ReeActions::DSL
40
+
41
+ action :denied_cmd do
42
+ link :permission_error, from: :ree_errors
43
+ end
44
+
45
+ PermissionErr = permission_error(:no_access, msg: "not allowed")
46
+
47
+ def call(access, attrs)
48
+ raise PermissionErr
49
+ end
50
+ end
51
+
52
+ class ReeRodaAuditTest::BoomCmd
53
+ include ReeActions::DSL
54
+
55
+ action :boom_cmd
56
+
57
+ def call(access, attrs)
58
+ raise ArgumentError.new("boom")
59
+ end
60
+ end
61
+
62
+ class ReeRodaAuditTest::Routes
63
+ include ReeRoutes::DSL
64
+
65
+ opts = {from: :ree_roda_audit_test}
66
+
67
+ routes :routes do
68
+ default_warden_scope :audit_visitor
69
+
70
+ get "api/internal_default" do
71
+ summary "Internal route"
72
+ sections "Admin"
73
+ visibility :internal
74
+ action :ok_cmd, **opts
75
+ end
76
+
77
+ get "api/internal_silenced" do
78
+ summary "Internal route we do not want in the trail"
79
+ sections "Admin"
80
+ visibility :internal
81
+ audit false
82
+ action :ok_cmd, **opts
83
+ end
84
+
85
+ get "api/public_default" do
86
+ summary "Public route"
87
+ sections "Public"
88
+ action :ok_cmd, **opts
89
+ end
90
+
91
+ get "api/public_audited" do
92
+ summary "Public route touching someone else's data"
93
+ sections "Public"
94
+ audit true
95
+ action :ok_cmd, **opts
96
+ end
97
+
98
+ get "api/internal_denied" do
99
+ summary "Internal route refusing access"
100
+ sections "Admin"
101
+ visibility :internal
102
+ action :denied_cmd, **opts
103
+ end
104
+
105
+ get "api/internal_boom" do
106
+ summary "Internal route blowing up"
107
+ sections "Admin"
108
+ visibility :internal
109
+ action :boom_cmd, **opts
110
+ end
111
+ end
112
+ end
113
+
114
+ class AuditVisitorStrategy < Warden::Strategies::Base
115
+ include Ree::LinkDSL
116
+
117
+ def valid?
118
+ true
119
+ end
120
+
121
+ def authenticate!
122
+ success!({user: "visitor"})
123
+ end
124
+ end
125
+
126
+ Warden::Strategies.add(:audit_visitor, AuditVisitorStrategy)
127
+
128
+ audit_bean = audit
129
+
130
+ # Class.new keeps `audit_bean` in scope; a `class` keyword would open a new
131
+ # lexical scope and lose it.
132
+ audited_app = Class.new(ReeRoda::App) do
133
+ use Warden::Manager do |config|
134
+ config.default_strategies :audit_visitor
135
+ config.default_scope = :audit_visitor
136
+ config.scope_defaults :audit_visitor, strategies: [:audit_visitor], store: false
137
+
138
+ config.failure_app = -> (env) {
139
+ [401, {"Content-Type" => "text/plain"}, ["requires authentication"]]
140
+ }
141
+ end
142
+
143
+ plugin :ree_routes, audit: audit_bean
144
+
145
+ ree_routes ReeRodaAuditTest::Routes.new, api_url: "http://some.api.url:1337"
146
+
147
+ route do |r|
148
+ r.ree_routes
149
+ end
150
+ end
151
+
152
+ Object.const_set(:AuditedTestApp, audited_app)
153
+
154
+ # Same routes, no `audit:` option — the regression guard for every other
155
+ # project running on ree_lib.
156
+ plain_app = Class.new(ReeRoda::App) do
157
+ use Warden::Manager do |config|
158
+ config.default_strategies :audit_visitor
159
+ config.default_scope = :audit_visitor
160
+ config.scope_defaults :audit_visitor, strategies: [:audit_visitor], store: false
161
+
162
+ config.failure_app = -> (env) {
163
+ [401, {"Content-Type" => "text/plain"}, ["requires authentication"]]
164
+ }
165
+ end
166
+
167
+ plugin :ree_routes
168
+
169
+ ree_routes ReeRodaAuditTest::Routes.new, api_url: "http://some.api.url:1337"
170
+
171
+ route do |r|
172
+ r.ree_routes
173
+ end
174
+ end
175
+
176
+ Object.const_set(:PlainTestApp, plain_app)
177
+ end
178
+
179
+ after :all do
180
+ Ree.disable_irb_mode
181
+ end
182
+
183
+ let(:test_sink) {
184
+ Class.new(ReeAudit::Sink) do
185
+ attr_reader :events
186
+
187
+ def initialize
188
+ @events = []
189
+ end
190
+
191
+ def write(event)
192
+ @events << event
193
+ end
194
+ end.new
195
+ }
196
+
197
+ let(:app) { AuditedTestApp.app }
198
+
199
+ before {
200
+ audit.sink = test_sink
201
+ ReeAudit::Context.clear
202
+ }
203
+
204
+ after {
205
+ audit.sink = ReeAudit::NullSink.new
206
+ ReeAudit::Context.clear
207
+ }
208
+
209
+ it "writes an internal route to the trail" do
210
+ get "api/internal_default"
211
+
212
+ expect(last_response.status).to eq(200)
213
+ expect(last_response.body).to eq("{}")
214
+
215
+ expect(test_sink.events.size).to eq(1)
216
+
217
+ event = test_sink.events.first
218
+ expect(event.action_name).to eq(:ok_cmd)
219
+ expect(event.package_name).to eq(:ree_roda_audit_test)
220
+ expect(event.status).to eq(:ok)
221
+ expect(event.sections).to eq(["Admin"])
222
+ expect(event.request_method).to eq(:get)
223
+ expect(event.path).to eq("api/internal_default")
224
+ expect(event.request_path).to eq("/api/internal_default")
225
+ expect(event.accessor).to eq({user: "visitor"})
226
+ end
227
+
228
+ it "leaves a silenced internal route out of the trail" do
229
+ get "api/internal_silenced"
230
+
231
+ expect(last_response.status).to eq(200)
232
+ expect(test_sink.events).to be_empty
233
+ end
234
+
235
+ it "leaves a public route out of the trail" do
236
+ get "api/public_default"
237
+
238
+ expect(last_response.status).to eq(200)
239
+ expect(test_sink.events).to be_empty
240
+ end
241
+
242
+ it "writes a public route that asked to be audited" do
243
+ get "api/public_audited"
244
+
245
+ expect(last_response.status).to eq(200)
246
+ expect(test_sink.events.size).to eq(1)
247
+ expect(test_sink.events.first.status).to eq(:ok)
248
+ end
249
+
250
+ it "records a refusal and still answers 403" do
251
+ get "api/internal_denied"
252
+
253
+ expect(last_response.status).to eq(403)
254
+
255
+ event = test_sink.events.first
256
+ expect(event.status).to eq(:denied)
257
+ expect(event.error_message).to eq("not allowed")
258
+ end
259
+
260
+ it "records a failure and still lets the error handler answer 500" do
261
+ get "api/internal_boom"
262
+
263
+ expect(last_response.status).to eq(500)
264
+
265
+ event = test_sink.events.first
266
+ expect(event.status).to eq(:error)
267
+ expect(event.error_type).to eq("ArgumentError")
268
+ end
269
+
270
+ context "an application that never asked for audit" do
271
+ let(:app) { PlainTestApp.app }
272
+
273
+ it "behaves exactly as before" do
274
+ get "api/internal_default"
275
+
276
+ expect(last_response.status).to eq(200)
277
+ expect(last_response.body).to eq("{}")
278
+ expect(test_sink.events).to be_empty
279
+ end
280
+ end
281
+ end
@@ -1,3 +1,6 @@
1
1
  RSpec.configure do |config|
2
2
  config.extend Ree::RSpecLinkDSL
3
3
  end
4
+
5
+ # The audit plugin option is inert unless the package itself is enabled.
6
+ ENV['AUDIT_ENABLED'] = 'true'
@@ -3,7 +3,9 @@
3
3
  class ReeRoutes::Route
4
4
  attr_accessor :summary, :request_method, :serializer, :respond_to,
5
5
  :sections, :action, :route, :warden_scopes, :path, :override,
6
- :visibility
6
+ :visibility, :before, :redirect
7
+
8
+ attr_writer :audit
7
9
 
8
10
  # Routing always includes every route; visibility only affects generated docs.
9
11
  def public?
@@ -14,6 +16,14 @@ class ReeRoutes::Route
14
16
  !public?
15
17
  end
16
18
 
19
+ # Internal routes are audited unless told otherwise. `visibility` is
20
+ # mandatory for admin paths (see DSL#define_route), so this list can never
21
+ # drift away from the set of routes that actually touch client data — which a
22
+ # hand-maintained list of audited routes inevitably would.
23
+ def audited?
24
+ @audit.nil? ? internal? : @audit
25
+ end
26
+
17
27
  def valid?
18
28
  !action.nil? && !summary.nil? && !warden_scopes.nil? && !warden_scopes.empty?
19
29
  end
@@ -44,6 +44,13 @@ class ReeRoutes::RouteBuilder
44
44
  @route.summary = str
45
45
  end
46
46
 
47
+ # Needed in exactly two cases: silencing a noisy internal route, and turning
48
+ # the trail on for a public route that reaches into someone else's data.
49
+ contract Bool => Bool
50
+ def audit(value)
51
+ @route.audit = value
52
+ end
53
+
47
54
  contract Symbol => Symbol
48
55
  def visibility(value)
49
56
  if !VISIBILITIES.include?(value)
@@ -68,6 +68,38 @@ RSpec.describe ReeRoutes::DSL, type: [:autoclean] do
68
68
  end
69
69
  end
70
70
  end
71
+
72
+ class ReeRoutesTest::AuditRoutes
73
+ include ReeRoutes::DSL
74
+
75
+ routes :audit_routes do
76
+ default_warden_scope :user
77
+
78
+ get "internal_default" do
79
+ summary "Internal route, audited by default"
80
+ action :cmd, from: :ree_routes_test
81
+ visibility :internal
82
+ end
83
+
84
+ get "internal_silenced" do
85
+ summary "Internal route, deliberately not audited"
86
+ action :cmd, from: :ree_routes_test
87
+ visibility :internal
88
+ audit false
89
+ end
90
+
91
+ get "public_default" do
92
+ summary "Public route, not audited"
93
+ action :cmd, from: :ree_routes_test
94
+ end
95
+
96
+ get "public_audited" do
97
+ summary "Public route reaching into someone else's data"
98
+ action :cmd, from: :ree_routes_test
99
+ audit true
100
+ end
101
+ end
102
+ end
71
103
  end
72
104
 
73
105
  after :all do
@@ -92,4 +124,15 @@ RSpec.describe ReeRoutes::DSL, type: [:autoclean] do
92
124
 
93
125
  expect(csv_route.respond_to).to eq(:csv)
94
126
  }
127
+
128
+ it {
129
+ routes = ReeRoutesTest::AuditRoutes.new.each_with_object({}) { |r, acc|
130
+ acc[r.path] = r
131
+ }
132
+
133
+ expect(routes["internal_default"].audited?).to eq(true)
134
+ expect(routes["internal_silenced"].audited?).to eq(false)
135
+ expect(routes["public_default"].audited?).to eq(false)
136
+ expect(routes["public_audited"].audited?).to eq(true)
137
+ }
95
138
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ReeLib
4
- VERSION = "1.3.9"
4
+ VERSION = "1.3.11"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ree_lib
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.9
4
+ version: 1.3.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ruslan Gatiyatov
@@ -276,6 +276,20 @@ files:
276
276
  - lib/ree_lib/packages/ree_array/spec/ree_array/functions/split_by_spec.rb
277
277
  - lib/ree_lib/packages/ree_array/spec/ree_array/functions/wrap_spec.rb
278
278
  - lib/ree_lib/packages/ree_array/spec/spec_helper.rb
279
+ - lib/ree_lib/packages/ree_audit/.rspec
280
+ - lib/ree_lib/packages/ree_audit/package/ree_audit.rb
281
+ - lib/ree_lib/packages/ree_audit/package/ree_audit/beans/audit.rb
282
+ - lib/ree_lib/packages/ree_audit/package/ree_audit/config.rb
283
+ - lib/ree_lib/packages/ree_audit/package/ree_audit/context.rb
284
+ - lib/ree_lib/packages/ree_audit/package/ree_audit/event.rb
285
+ - lib/ree_lib/packages/ree_audit/package/ree_audit/functions/filter_params.rb
286
+ - lib/ree_lib/packages/ree_audit/package/ree_audit/sinks/logger_sink.rb
287
+ - lib/ree_lib/packages/ree_audit/package/ree_audit/sinks/null_sink.rb
288
+ - lib/ree_lib/packages/ree_audit/package/ree_audit/sinks/sink.rb
289
+ - lib/ree_lib/packages/ree_audit/spec/ree_audit/beans/audit_spec.rb
290
+ - lib/ree_lib/packages/ree_audit/spec/ree_audit/context_spec.rb
291
+ - lib/ree_lib/packages/ree_audit/spec/ree_audit/functions/filter_params_spec.rb
292
+ - lib/ree_lib/packages/ree_audit/spec/spec_helper.rb
279
293
  - lib/ree_lib/packages/ree_dao/.gitignore
280
294
  - lib/ree_lib/packages/ree_dao/.rspec
281
295
  - lib/ree_lib/packages/ree_dao/bin/console
@@ -303,6 +317,7 @@ files:
303
317
  - lib/ree_lib/packages/ree_dao/package/ree_dao/thread_parents.rb
304
318
  - lib/ree_lib/packages/ree_dao/package/ree_dao/wrappers/pg_array.rb
305
319
  - lib/ree_lib/packages/ree_dao/package/ree_dao/wrappers/pg_jsonb.rb
320
+ - lib/ree_lib/packages/ree_dao/spec/ree_dao/db_load_performance_mode_spec.rb
306
321
  - lib/ree_lib/packages/ree_dao/spec/ree_dao/functions/build_dto_spec.rb
307
322
  - lib/ree_lib/packages/ree_dao/spec/ree_dao/functions/build_pg_connection_spec.rb
308
323
  - lib/ree_lib/packages/ree_dao/spec/ree_dao/functions/build_sqlite_connection/max_connections_spec.rb
@@ -624,6 +639,7 @@ files:
624
639
  - lib/ree_lib/packages/ree_dto/package/ree_dto/entity_dsl.rb
625
640
  - lib/ree_lib/packages/ree_dto/package/ree_dto/functions/build_dto.rb
626
641
  - lib/ree_lib/packages/ree_dto/package/ree_dto/functions/build_dto_collection_class.rb
642
+ - lib/ree_lib/packages/ree_dto/spec/ree_dto/build_from_attrs_spec.rb
627
643
  - lib/ree_lib/packages/ree_dto/spec/ree_dto/dsl_spec.rb
628
644
  - lib/ree_lib/packages/ree_dto/spec/ree_dto/entity_dsl_spec.rb
629
645
  - lib/ree_lib/packages/ree_dto/spec/spec_helper.rb
@@ -763,6 +779,7 @@ files:
763
779
  - lib/ree_lib/packages/ree_mapper/.rspec
764
780
  - lib/ree_lib/packages/ree_mapper/bin/console
765
781
  - lib/ree_lib/packages/ree_mapper/package/ree_mapper.rb
782
+ - lib/ree_lib/packages/ree_mapper/package/ree_mapper/db_load_compiler.rb
766
783
  - lib/ree_lib/packages/ree_mapper/package/ree_mapper/default_factory.rb
767
784
  - lib/ree_lib/packages/ree_mapper/package/ree_mapper/dsl.rb
768
785
  - lib/ree_lib/packages/ree_mapper/package/ree_mapper/errors/argument_error.rb
@@ -797,7 +814,9 @@ files:
797
814
  - lib/ree_lib/packages/ree_mapper/package/ree_mapper/types/time.rb
798
815
  - lib/ree_lib/packages/ree_mapper/package/ree_mapper/wrappers/abstract_wrapper.rb
799
816
  - lib/ree_lib/packages/ree_mapper/package/ree_mapper/wrappers/array.rb
817
+ - lib/ree_lib/packages/ree_mapper/spec/ree_mapper/benchmarks/db_load_benchmark_spec.rb
800
818
  - lib/ree_lib/packages/ree_mapper/spec/ree_mapper/benchmarks/mapper_benchmark_spec.rb
819
+ - lib/ree_lib/packages/ree_mapper/spec/ree_mapper/db_load_performance_mode_spec.rb
801
820
  - lib/ree_lib/packages/ree_mapper/spec/ree_mapper/dsl/dsl_spec.rb
802
821
  - lib/ree_lib/packages/ree_mapper/spec/ree_mapper/functions/build_mapper_factory_spec.rb
803
822
  - lib/ree_lib/packages/ree_mapper/spec/ree_mapper/functions/build_mapper_strategy_spec.rb
@@ -892,6 +911,7 @@ files:
892
911
  - lib/ree_lib/packages/ree_roda/package/ree_roda/services/status_from_error.rb
893
912
  - lib/ree_lib/packages/ree_roda/spec/ree_roda/app_spec.rb
894
913
  - lib/ree_lib/packages/ree_roda/spec/ree_roda/plugins/ree_logger_spec.rb
914
+ - lib/ree_lib/packages/ree_roda/spec/ree_roda/plugins/ree_routes_audit_spec.rb
895
915
  - lib/ree_lib/packages/ree_roda/spec/ree_roda/services/build_routing_tree_spec.rb
896
916
  - lib/ree_lib/packages/ree_roda/spec/ree_roda/services/build_swagger_from_routes_spec.rb
897
917
  - lib/ree_lib/packages/ree_roda/spec/ree_roda/services/locales/en.yml