yes-read-api 2.4.5 → 2.5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d39b9ae08398487d3eff34bf24f91e25ad03921f74df6ec0a55d06195256282a
4
- data.tar.gz: e20a9c3cb5149a6e45940eb18f91d26fac6600dbd9c001a3355b846ee5e5fce9
3
+ metadata.gz: ffccff57e5b684bd457f73ee8812236bdfff0adef1179e040600a8f60ae9e477
4
+ data.tar.gz: '09c52031f2424362d6437a0f0e3fbb7697a78389990c0da1436f6ac84aa2daf5'
5
5
  SHA512:
6
- metadata.gz: a238fe0e409db52025aa776c4395fe1afc8ed09f246c1a368bd4ab92a26b9c719b29639d01941836f5f24996ea8ee6a3203026bba599fc61eec551898815160b
7
- data.tar.gz: ea069356f20ace8d83696c76539e7752843ab173da85b69b4cc437cfe6bdeb0a62e7998f8c85122d950a38e26e95cbdffcbfe066708e3faad939701a44d9df35
6
+ metadata.gz: da42b024bd288d04c96b97e06806008d9365adfc77f5d9d1488951a0988730ce529cf8a0198fa15e6c3d84cdb102677b6d5b6dbd91f7526ce4e07cf2329f902f
7
+ data.tar.gz: 78e070d987e4c42bee1f905d364380e4446aa26e6cb879bc1ddec1a989f4ab80e41fee3f02adfeba210c9537ce9399631db87bd5e46fdf5b1f7f58565221407e
@@ -10,6 +10,10 @@ module Yes
10
10
  before_action :validate_advanced_payload, only: :advanced
11
11
  before_action :process_own_filter, only: :call
12
12
 
13
+ IGNORED_FILTER_PARAM = { basic: :filter_definition, advanced: :filters }.freeze
14
+
15
+ # Covers AuthenticationError raised outside #authenticate_with_token, e.g. by consumer
16
+ # request authorizers, filters or serializers.
13
17
  rescue_from(Yes::Core::AuthenticationError, with: :auth_error_response)
14
18
 
15
19
  rescue_from(
@@ -43,7 +47,7 @@ module Yes
43
47
  end
44
48
 
45
49
  def response_json(filter_type: :basic, persisted_filter: nil)
46
- filter_options = persisted_filter&.body&.deep_symbolize_keys&.merge(model: params[:model]) || params
50
+ filter_options = applied_filter_options(persisted_filter:, filter_type:)
47
51
 
48
52
  request_authorizer.call(filter_options, auth_data)
49
53
 
@@ -56,6 +60,19 @@ module Yes
56
60
  serialize(paginated_records, filter_options)
57
61
  end
58
62
 
63
+ # The options the query is built from. They are what the request authorizer is handed too, so
64
+ # it never authorizes a parameter the query does not apply: a basic query applies `filters` and
65
+ # an advanced one `filter_definition`.
66
+ #
67
+ # @param persisted_filter [#body, nil] stored filter whose body replaces the request params
68
+ # @param filter_type [Symbol] `:basic` or `:advanced`
69
+ # @return [Hash] the params without the key the filter type ignores
70
+ def applied_filter_options(persisted_filter:, filter_type:)
71
+ options = persisted_filter&.body&.deep_symbolize_keys&.merge(model: params[:model]) || params
72
+
73
+ options.except(IGNORED_FILTER_PARAM.fetch(filter_type))
74
+ end
75
+
59
76
  def request_authorizer
60
77
  authorizer_class = "ReadModels::#{read_model_name.classify}::RequestAuthorizer"
61
78
  Kernel.const_get(authorizer_class)
@@ -93,15 +110,26 @@ module Yes
93
110
  @params ||= request.parameters.deep_symbolize_keys
94
111
  end
95
112
 
113
+ # Authenticates the request using the configured auth adapter and stores the returned auth data.
114
+ # Rescues at call time (not via rescue_from) because the adapter, and with it its error
115
+ # classes, is configured in an initializer that may run after this controller loads.
116
+ #
117
+ # @return [void]
96
118
  def authenticate_with_token
97
119
  adapter = Yes::Core.configuration.auth_adapter
98
120
  raise Yes::Core::AuthenticationError, 'No auth adapter configured' if adapter.nil?
99
121
 
100
122
  @auth_data = adapter.authenticate(request)
123
+ rescue *Yes::Core.configuration.auth_error_classes => e
124
+ auth_error_response(e)
101
125
  end
102
126
 
103
127
  attr_reader :auth_data
104
128
 
129
+ # Renders a 401 for an authentication failure.
130
+ #
131
+ # @param error [StandardError] the error raised by the auth adapter
132
+ # @return [void]
105
133
  def auth_error_response(error)
106
134
  render(
107
135
  json: { title: 'Auth Token Invalid', detail: error.message }.to_json,
data/config/routes.rb CHANGED
@@ -50,7 +50,7 @@ module Yes
50
50
  request.body.rewind # restore the cursor to the beginning able read again body
51
51
 
52
52
  auth_token = env['HTTP_AUTHORIZATION'] || ''
53
- auth_data = auth_token.present? ? JWT.decode(auth_token.gsub('Bearer ', ''), nil, false) : {}
53
+ auth_data = unverified_auth_data(auth_token)
54
54
  {
55
55
  auth_token:,
56
56
  auth_data: auth_data.to_json,
@@ -58,9 +58,23 @@ module Yes
58
58
  }.stringify_keys
59
59
  end
60
60
 
61
+ # Decodes the bearer token without verifying it, only to enrich the span. A missing or
62
+ # undecodable token yields an empty result: span enrichment must never decide the outcome
63
+ # of the request, the controller answers with 401 for such tokens.
64
+ #
65
+ # @param auth_token [String] the raw Authorization header value (may be empty)
66
+ # @return [Array<Hash>, Hash] the decoded JWT payload and header, or an empty hash
67
+ def unverified_auth_data(auth_token)
68
+ return {} if auth_token.blank?
69
+
70
+ JWT.decode(auth_token.gsub('Bearer ', ''), nil, false)
71
+ rescue JWT::DecodeError
72
+ {}
73
+ end
74
+
61
75
  def get_otl_auth_data(request, env)
62
76
  auth_token = env['HTTP_AUTHORIZATION'] || ''
63
- auth_data = auth_token.present? ? JWT.decode(auth_token.gsub('Bearer ', ''), nil, false) : {}
77
+ auth_data = unverified_auth_data(auth_token)
64
78
  {
65
79
  auth_token:,
66
80
  auth_data: auth_data.to_json,
@@ -3,7 +3,7 @@
3
3
  module Yes
4
4
  module Read
5
5
  module Api
6
- VERSION = '2.4.5'
6
+ VERSION = '2.5.0'
7
7
  end
8
8
  end
9
9
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yes-read-api
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.4.5
4
+ version: 2.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nico Ritsche