lhs 21.3.0.pre.autoauth.1 → 22.1.1.pre

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 +205 -117
  3. data/lhs.gemspec +1 -1
  4. data/lib/lhs/concerns/record/chainable.rb +4 -4
  5. data/lib/lhs/concerns/record/configuration.rb +23 -14
  6. data/lib/lhs/concerns/record/request.rb +29 -32
  7. data/lib/lhs/concerns/record/update.rb +17 -0
  8. data/lib/lhs/interceptors/auto_oauth/interceptor.rb +16 -1
  9. data/lib/lhs/record.rb +6 -3
  10. data/lib/lhs/version.rb +1 -1
  11. data/spec/auto_oauth_spec.rb +141 -23
  12. data/spec/dummy/app/controllers/application_controller.rb +9 -1
  13. data/spec/dummy/app/controllers/automatic_authentication_controller.rb +18 -0
  14. data/spec/dummy/app/models/dummy_record_with_auto_oauth_provider.rb +6 -0
  15. data/spec/dummy/app/models/dummy_record_with_multiple_oauth_providers1.rb +7 -0
  16. data/spec/dummy/app/models/dummy_record_with_multiple_oauth_providers2.rb +7 -0
  17. data/spec/dummy/app/models/dummy_record_with_multiple_providers_per_endpoint.rb +6 -0
  18. data/spec/dummy/app/models/providers/internal_services.rb +7 -0
  19. data/spec/dummy/config/routes.rb +2 -0
  20. data/spec/item/destroy_spec.rb +1 -1
  21. data/spec/proxy/record_identification_spec.rb +1 -1
  22. data/spec/record/all_spec.rb +1 -1
  23. data/spec/record/endpoints_spec.rb +1 -1
  24. data/spec/record/handle_includes_errors_spec.rb +1 -1
  25. data/spec/record/has_many_spec.rb +1 -1
  26. data/spec/record/has_one_spec.rb +1 -1
  27. data/spec/record/includes_first_page_spec.rb +727 -0
  28. data/spec/record/includes_spec.rb +545 -579
  29. data/spec/record/includes_warning_spec.rb +1 -1
  30. data/spec/record/mapping_spec.rb +2 -2
  31. data/spec/record/references_spec.rb +1 -1
  32. data/spec/record/relation_caching_spec.rb +3 -3
  33. data/spec/record/update_spec.rb +62 -0
  34. metadata +21 -10
  35. data/spec/dummy/config/initializers/lhs.rb +0 -3
  36. data/spec/record/includes_all_spec.rb +0 -693
@@ -187,7 +187,7 @@ class LHS::Record
187
187
  options = extend_with_reference(options, reference)
188
188
  addition = load_include(options, data, sub_includes, reference)
189
189
  extend_raw_data!(data, addition, included)
190
- expand_addition!(data, included, options) if no_expanded_data?(addition)
190
+ expand_addition!(data, included, options) unless expanded_data?(addition)
191
191
  end
192
192
  end
193
193
 
@@ -208,25 +208,21 @@ class LHS::Record
208
208
  options = extend_with_reference(options, reference.except(:url))
209
209
  record = record_for_options(options) || self
210
210
  options = convert_options_to_endpoints(options) if record_for_options(options)
211
- expanded_data = begin
212
- record.request(options)
213
- rescue LHC::NotFound
214
- LHS::Data.new({}, data, record)
215
- end
211
+ expanded_data = record.request(options)
216
212
  extend_raw_data!(data, expanded_data, included)
217
213
  end
218
214
 
219
- def no_expanded_data?(addition)
215
+ def expanded_data?(addition)
220
216
  return false if addition.blank?
221
217
  if addition.item?
222
- (addition._raw.keys - [:href]).empty?
218
+ (addition._raw.keys - [:href]).any?
223
219
  elsif addition.collection?
224
220
  addition.all? do |item|
225
221
  next if item.blank?
226
222
  if item._raw.is_a?(Hash)
227
- (item._raw.keys - [:href]).empty?
223
+ (item._raw.keys - [:href]).any?
228
224
  elsif item._raw.is_a?(Array)
229
- item.any? { |item| (item._raw.keys - [:href]).empty? }
225
+ item.any? { |item| (item._raw.keys - [:href]).any? }
230
226
  end
231
227
  end
232
228
  end
@@ -351,15 +347,11 @@ class LHS::Record
351
347
  def load_include(options, data, sub_includes, references)
352
348
  record = record_for_options(options) || self
353
349
  options = convert_options_to_endpoints(options) if record_for_options(options)
354
- begin
355
- prepare_options_for_include_request!(options, sub_includes, references)
356
- if references && references[:all] # include all linked resources
357
- load_include_all!(options, record, sub_includes, references)
358
- else # simply request first page/batch
359
- load_include_simple!(options, record)
360
- end
361
- rescue LHC::NotFound
362
- LHS::Data.new({}, data, record)
350
+ prepare_options_for_include_request!(options, sub_includes, references)
351
+ if references && references[:all] # include all linked resources
352
+ load_include_all!(options, record, sub_includes, references)
353
+ else # simply request first page/batch
354
+ load_include_simple!(options, record)
363
355
  end
364
356
  end
365
357
 
@@ -522,19 +514,24 @@ class LHS::Record
522
514
  end
523
515
 
524
516
  def inject_interceptors!(options)
525
- inject_interceptor!(
526
- options,
527
- LHS::Interceptors::RequestCycleCache::Interceptor,
528
- LHC::Caching,
529
- "[WARNING] Can't enable request cycle cache as LHC::Caching interceptor is not enabled/configured (see https://github.com/local-ch/lhc/blob/master/README.md#caching-interceptor)!"
530
- ) if LHS.config.request_cycle_cache_enabled
531
-
532
- inject_interceptor!(
533
- options,
534
- LHS::Interceptors::AutoOauth::Interceptor,
535
- LHC::Auth,
536
- "[WARNING] Can't enable auto oauth as LHC::Auth interceptor is not enabled/configured (see https://github.com/local-ch/lhc/blob/master/README.md#authentication-interceptor)!"
537
- ) if self.auto_oauth?
517
+ if LHS.config.request_cycle_cache_enabled
518
+ inject_interceptor!(
519
+ options,
520
+ LHS::Interceptors::RequestCycleCache::Interceptor,
521
+ LHC::Caching,
522
+ "[WARNING] Can't enable request cycle cache as LHC::Caching interceptor is not enabled/configured (see https://github.com/local-ch/lhc/blob/master/README.md#caching-interceptor)!"
523
+ )
524
+ end
525
+
526
+ endpoint = find_endpoint(options[:params], options.fetch(:url, nil))
527
+ if auto_oauth? || (endpoint.options&.dig(:oauth) && LHS.config.auto_oauth) || options[:oauth]
528
+ inject_interceptor!(
529
+ options.merge!(record: self),
530
+ LHS::Interceptors::AutoOauth::Interceptor,
531
+ LHC::Auth,
532
+ "[WARNING] Can't enable auto oauth as LHC::Auth interceptor is not enabled/configured (see https://github.com/local-ch/lhc/blob/master/README.md#authentication-interceptor)!"
533
+ )
534
+ end
538
535
  end
539
536
 
540
537
  def inject_interceptor!(options, interceptor, dependecy, warning)
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+
5
+ class LHS::Record
6
+
7
+ module Update
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ class <<self
12
+ alias_method :update, :create
13
+ alias_method :update!, :create!
14
+ end
15
+ end
16
+ end
17
+ end
@@ -10,7 +10,22 @@ module LHS
10
10
  class Interceptor < LHC::Interceptor
11
11
 
12
12
  def before_request
13
- request.options[:auth] = { bearer: LHS::Interceptors::AutoOauth::ThreadRegistry.access_token }
13
+ request.options[:auth] = { bearer: token }
14
+ end
15
+
16
+ def tokens
17
+ @tokens ||= LHS::Interceptors::AutoOauth::ThreadRegistry.access_token
18
+ end
19
+
20
+ def token
21
+ if tokens.is_a?(Hash)
22
+ tokens.dig(
23
+ request.options[:oauth] ||
24
+ request.options[:record]&.auto_oauth
25
+ )
26
+ else
27
+ tokens
28
+ end
14
29
  end
15
30
  end
16
31
  end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class LHS::Record
4
+ autoload :AttributeAssignment,
5
+ 'lhs/concerns/record/attribute_assignment'
4
6
  autoload :Batch,
5
7
  'lhs/concerns/record/batch'
6
8
  autoload :Chainable,
@@ -45,9 +47,10 @@ class LHS::Record
45
47
  'lhs/concerns/record/scope'
46
48
  autoload :Tracing,
47
49
  'lhs/concerns/record/tracing'
48
- autoload :AttributeAssignment,
49
- 'lhs/concerns/record/attribute_assignment'
50
+ autoload :Update,
51
+ 'lhs/concerns/record/update'
50
52
 
53
+ include AttributeAssignment
51
54
  include Batch
52
55
  include Chainable
53
56
  include Configuration
@@ -72,7 +75,7 @@ class LHS::Record
72
75
  include Relations
73
76
  include Scope
74
77
  include Tracing
75
- include AttributeAssignment
78
+ include Update
76
79
 
77
80
  delegate :_proxy, :_endpoint, :merge_raw!, :select, :becomes, :respond_to?, to: :_data
78
81
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LHS
4
- VERSION = '21.3.0.pre.autoauth.1'
4
+ VERSION = '22.1.1.pre'
5
5
  end
@@ -6,6 +6,12 @@ describe 'Auto OAuth Authentication', type: :request, dummy_models: true do
6
6
 
7
7
  context 'without LHC::Auth interceptor enabled' do
8
8
 
9
+ before do
10
+ LHS.configure do |config|
11
+ config.auto_oauth = -> { access_token }
12
+ end
13
+ end
14
+
9
15
  it 'shows a warning that it can not perform auto authentication' do
10
16
  expect(lambda do
11
17
  get '/automatic_authentication/oauth'
@@ -16,36 +22,148 @@ describe 'Auto OAuth Authentication', type: :request, dummy_models: true do
16
22
  end
17
23
 
18
24
  context 'with LHC::Auth interceptor enabled' do
19
- let(:record_request) do
20
- stub_request(:get, "http://datastore/v2/records_with_oauth/1")
21
- .with(
22
- headers: { 'Authorization' => "Bearer #{ApplicationController::ACCESS_TOKEN}" }
23
- ).to_return(status: 200, body: { name: 'Record' }.to_json)
24
- end
25
25
 
26
- let(:records_request) do
27
- stub_request(:get, "http://datastore/v2/records_with_oauth?color=blue")
28
- .with(
29
- headers: { 'Authorization' => "Bearer #{ApplicationController::ACCESS_TOKEN}" }
30
- ).to_return(status: 200, body: { items: [ { name: 'Record' } ] }.to_json)
31
- end
26
+ context 'with only one auth provider' do
32
27
 
33
- before do
34
- LHC.configure do |config|
35
- config.interceptors = [LHC::Auth]
28
+ let(:token) { ApplicationController::ACCESS_TOKEN }
29
+
30
+ let(:record_request) do
31
+ stub_request(:get, "http://datastore/v2/records_with_oauth/1")
32
+ .with(
33
+ headers: { 'Authorization' => "Bearer #{token}" }
34
+ ).to_return(status: 200, body: { name: 'Record' }.to_json)
35
+ end
36
+
37
+ let(:records_request) do
38
+ stub_request(:get, "http://datastore/v2/records_with_oauth?color=blue")
39
+ .with(
40
+ headers: { 'Authorization' => "Bearer #{token}" }
41
+ ).to_return(status: 200, body: { items: [{ name: 'Record' }] }.to_json)
42
+ end
43
+
44
+ before do
45
+ LHS.configure do |config|
46
+ config.auto_oauth = -> { access_token }
47
+ end
48
+ LHC.configure do |config|
49
+ config.interceptors = [LHC::Auth]
50
+ end
51
+ record_request
52
+ records_request
53
+ end
54
+
55
+ after do
56
+ LHC.config.reset
57
+ end
58
+
59
+ it 'applies OAuth credentials for the individual request automatically' do
60
+ get '/automatic_authentication/oauth'
61
+ expect(record_request).to have_been_requested
62
+ expect(records_request).to have_been_requested
36
63
  end
37
- record_request
38
- records_request
39
64
  end
40
65
 
41
- after do
42
- LHC.config.reset
66
+ context 'with multiple auth providers' do
67
+
68
+ before do
69
+ LHS.configure do |config|
70
+ config.auto_oauth = proc do
71
+ {
72
+ provider1: access_token_provider_1,
73
+ provider2: access_token_provider_2
74
+ }
75
+ end
76
+ end
77
+ LHC.configure do |config|
78
+ config.interceptors = [LHC::Auth]
79
+ end
80
+ record_request_provider_1
81
+ records_request_provider_2
82
+ records_request_per_endpoint_provider_1
83
+ record_request_per_endpoint_provider_2
84
+ end
85
+
86
+ let(:token) { ApplicationController::ACCESS_TOKEN }
87
+
88
+ let(:record_request_provider_1) do
89
+ stub_request(:get, "http://datastore/v2/records_with_multiple_oauth_providers_1/1")
90
+ .with(
91
+ headers: { 'Authorization' => "Bearer #{token}_provider_1" }
92
+ ).to_return(status: 200, body: { name: 'Record' }.to_json)
93
+ end
94
+
95
+ let(:records_request_provider_2) do
96
+ stub_request(:get, "http://datastore/v2/records_with_multiple_oauth_providers_2?color=blue")
97
+ .with(
98
+ headers: { 'Authorization' => "Bearer #{token}_provider_2" }
99
+ ).to_return(status: 200, body: { items: [{ name: 'Record' }] }.to_json)
100
+ end
101
+
102
+ let(:records_request_per_endpoint_provider_1) do
103
+ stub_request(:get, "http://datastore/v2/records_with_multiple_oauth_providers_per_endpoint?color=blue")
104
+ .with(
105
+ headers: { 'Authorization' => "Bearer #{token}_provider_1" }
106
+ ).to_return(status: 200, body: { items: [{ name: 'Record' }] }.to_json)
107
+ end
108
+
109
+ let(:record_request_per_endpoint_provider_2) do
110
+ stub_request(:get, "http://datastore/v2/records_with_multiple_oauth_providers_per_endpoint/1")
111
+ .with(
112
+ headers: { 'Authorization' => "Bearer #{token}_provider_2" }
113
+ ).to_return(status: 200, body: { name: 'Record' }.to_json)
114
+ end
115
+
116
+ after do
117
+ LHC.config.reset
118
+ end
119
+
120
+ it 'applies OAuth credentials for the individual request automatically no matter how many auth providers are configured ' do
121
+ get '/automatic_authentication/oauth_with_multiple_providers'
122
+ expect(record_request_provider_1).to have_been_requested
123
+ expect(records_request_provider_2).to have_been_requested
124
+ expect(records_request_per_endpoint_provider_1).to have_been_requested
125
+ expect(record_request_per_endpoint_provider_2).to have_been_requested
126
+ end
43
127
  end
44
128
 
45
- it 'applies OAuth credentials for the individual request automatically' do
46
- get '/automatic_authentication/oauth'
47
- expect(record_request).to have_been_requested
48
- expect(records_request).to have_been_requested
129
+ context 'with provider enabled for auto oauth' do
130
+
131
+ let(:token) { ApplicationController::ACCESS_TOKEN }
132
+
133
+ let(:record_request) do
134
+ stub_request(:get, "http://internalservice/v2/records/1")
135
+ .with(
136
+ headers: { 'Authorization' => "Bearer #{token}" }
137
+ ).to_return(status: 200, body: { name: 'Record' }.to_json)
138
+ end
139
+
140
+ let(:records_request) do
141
+ stub_request(:get, "http://internalservice/v2/records?color=blue")
142
+ .with(
143
+ headers: { 'Authorization' => "Bearer #{token}" }
144
+ ).to_return(status: 200, body: { items: [{ name: 'Record' }] }.to_json)
145
+ end
146
+
147
+ before do
148
+ LHS.configure do |config|
149
+ config.auto_oauth = -> { access_token }
150
+ end
151
+ LHC.configure do |config|
152
+ config.interceptors = [LHC::Auth]
153
+ end
154
+ record_request
155
+ records_request
156
+ end
157
+
158
+ after do
159
+ LHC.config.reset
160
+ end
161
+
162
+ it 'applies OAuth credentials for the individual request automatically' do
163
+ get '/automatic_authentication/oauth_with_provider'
164
+ expect(record_request).to have_been_requested
165
+ expect(records_request).to have_been_requested
166
+ end
49
167
  end
50
168
  end
51
169
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  class ApplicationController < ActionController::Base
4
4
  include LHS::OAuth
5
- ACCESS_TOKEN = 'token-12345'.freeze
5
+ ACCESS_TOKEN = 'token-12345'
6
6
 
7
7
  # Prevent CSRF attacks by raising an exception.
8
8
  # For APIs, you may want to use :null_session instead.
@@ -15,4 +15,12 @@ class ApplicationController < ActionController::Base
15
15
  def access_token
16
16
  ACCESS_TOKEN
17
17
  end
18
+
19
+ def access_token_provider_1
20
+ "#{ACCESS_TOKEN}_provider_1"
21
+ end
22
+
23
+ def access_token_provider_2
24
+ "#{ACCESS_TOKEN}_provider_2"
25
+ end
18
26
  end
@@ -8,4 +8,22 @@ class AutomaticAuthenticationController < ApplicationController
8
8
  records: DummyRecordWithOauth.where(color: 'blue').as_json
9
9
  }
10
10
  end
11
+
12
+ def o_auth_with_multiple_providers
13
+ render json: {
14
+ record: DummyRecordWithMultipleOauthProviders1.find(1).as_json,
15
+ records: DummyRecordWithMultipleOauthProviders2.where(color: 'blue').as_json,
16
+ per_endpoint: {
17
+ record: DummyRecordWithMultipleOauthProvidersPerEndpoint.find(1).as_json,
18
+ records: DummyRecordWithMultipleOauthProvidersPerEndpoint.where(color: 'blue').as_json
19
+ }
20
+ }
21
+ end
22
+
23
+ def o_auth_with_provider
24
+ render json: {
25
+ record: DummyRecordWithAutoOauthProvider.find(1).as_json,
26
+ records: DummyRecordWithAutoOauthProvider.where(color: 'blue').as_json
27
+ }
28
+ end
11
29
  end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ class DummyRecordWithAutoOauthProvider < Providers::InternalServices
4
+ endpoint 'http://internalservice/v2/records'
5
+ endpoint 'http://internalservice/v2/records/{id}'
6
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class DummyRecordWithMultipleOauthProviders1 < LHS::Record
4
+ oauth(:provider1)
5
+ endpoint 'http://datastore/v2/records_with_multiple_oauth_providers_1'
6
+ endpoint 'http://datastore/v2/records_with_multiple_oauth_providers_1/{id}'
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class DummyRecordWithMultipleOauthProviders2 < LHS::Record
4
+ oauth(:provider2)
5
+ endpoint 'http://datastore/v2/records_with_multiple_oauth_providers_2'
6
+ endpoint 'http://datastore/v2/records_with_multiple_oauth_providers_2/{id}'
7
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ class DummyRecordWithMultipleOauthProvidersPerEndpoint < LHS::Record
4
+ endpoint 'http://datastore/v2/records_with_multiple_oauth_providers_per_endpoint', oauth: :provider1
5
+ endpoint 'http://datastore/v2/records_with_multiple_oauth_providers_per_endpoint/{id}', oauth: :provider2
6
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Providers
4
+ class InternalServices < LHS::Record
5
+ provider(oauth: true)
6
+ end
7
+ end