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

Sign up to get free protection for your applications and to get access to all the features.
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