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.
- checksums.yaml +4 -4
- data/README.md +205 -117
- data/lhs.gemspec +1 -1
- data/lib/lhs/concerns/record/chainable.rb +4 -4
- data/lib/lhs/concerns/record/configuration.rb +23 -14
- data/lib/lhs/concerns/record/request.rb +29 -32
- data/lib/lhs/concerns/record/update.rb +17 -0
- data/lib/lhs/interceptors/auto_oauth/interceptor.rb +16 -1
- data/lib/lhs/record.rb +6 -3
- data/lib/lhs/version.rb +1 -1
- data/spec/auto_oauth_spec.rb +141 -23
- data/spec/dummy/app/controllers/application_controller.rb +9 -1
- data/spec/dummy/app/controllers/automatic_authentication_controller.rb +18 -0
- data/spec/dummy/app/models/dummy_record_with_auto_oauth_provider.rb +6 -0
- data/spec/dummy/app/models/dummy_record_with_multiple_oauth_providers1.rb +7 -0
- data/spec/dummy/app/models/dummy_record_with_multiple_oauth_providers2.rb +7 -0
- data/spec/dummy/app/models/dummy_record_with_multiple_providers_per_endpoint.rb +6 -0
- data/spec/dummy/app/models/providers/internal_services.rb +7 -0
- data/spec/dummy/config/routes.rb +2 -0
- data/spec/item/destroy_spec.rb +1 -1
- data/spec/proxy/record_identification_spec.rb +1 -1
- data/spec/record/all_spec.rb +1 -1
- data/spec/record/endpoints_spec.rb +1 -1
- data/spec/record/handle_includes_errors_spec.rb +1 -1
- data/spec/record/has_many_spec.rb +1 -1
- data/spec/record/has_one_spec.rb +1 -1
- data/spec/record/includes_first_page_spec.rb +727 -0
- data/spec/record/includes_spec.rb +545 -579
- data/spec/record/includes_warning_spec.rb +1 -1
- data/spec/record/mapping_spec.rb +2 -2
- data/spec/record/references_spec.rb +1 -1
- data/spec/record/relation_caching_spec.rb +3 -3
- data/spec/record/update_spec.rb +62 -0
- metadata +21 -10
- data/spec/dummy/config/initializers/lhs.rb +0 -3
- 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)
|
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 =
|
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
|
215
|
+
def expanded_data?(addition)
|
220
216
|
return false if addition.blank?
|
221
217
|
if addition.item?
|
222
|
-
(addition._raw.keys - [:href]).
|
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]).
|
223
|
+
(item._raw.keys - [:href]).any?
|
228
224
|
elsif item._raw.is_a?(Array)
|
229
|
-
item.any? { |item| (item._raw.keys - [:href]).
|
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
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
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
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
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:
|
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
|
data/lib/lhs/record.rb
CHANGED
@@ -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 :
|
49
|
-
'lhs/concerns/record/
|
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
|
78
|
+
include Update
|
76
79
|
|
77
80
|
delegate :_proxy, :_endpoint, :merge_raw!, :select, :becomes, :respond_to?, to: :_data
|
78
81
|
|
data/lib/lhs/version.rb
CHANGED
data/spec/auto_oauth_spec.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
34
|
-
|
35
|
-
|
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
|
-
|
42
|
-
|
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
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
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'
|
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,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
|