Skip to content

Commit 5cd7a79

Browse files
committed
DEV: Fix new Rubocop offenses
1 parent cd51d8b commit 5cd7a79

File tree

5 files changed

+363
-333
lines changed

5 files changed

+363
-333
lines changed

Gemfile.lock

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,39 @@
11
GEM
22
remote: https://rubygems.org/
33
specs:
4+
activesupport (7.1.3.2)
5+
base64
6+
bigdecimal
7+
concurrent-ruby (~> 1.0, >= 1.0.2)
8+
connection_pool (>= 2.2.5)
9+
drb
10+
i18n (>= 1.6, < 2)
11+
minitest (>= 5.1)
12+
mutex_m
13+
tzinfo (~> 2.0)
414
ast (2.4.2)
15+
base64 (0.2.0)
16+
bigdecimal (3.1.6)
17+
concurrent-ruby (1.2.3)
18+
connection_pool (2.4.1)
19+
drb (2.2.1)
20+
i18n (1.14.3)
21+
concurrent-ruby (~> 1.0)
22+
racc (~> 1.7)
523
json (2.7.1)
624
language_server-protocol (3.17.0.3)
25+
minitest (5.22.2)
26+
mutex_m (0.2.0)
727
parallel (1.24.0)
8-
parser (3.3.0.4)
28+
parser (3.3.0.5)
929
ast (~> 2.4.1)
1030
racc
1131
prettier_print (1.2.1)
1232
racc (1.7.3)
1333
rainbow (3.1.1)
1434
regexp_parser (2.9.0)
1535
rexml (3.2.6)
16-
rubocop (1.60.0)
36+
rubocop (1.61.0)
1737
json (~> 2.3)
1838
language_server-protocol (>= 3.17.0)
1939
parallel (~> 1.10)
@@ -24,22 +44,27 @@ GEM
2444
rubocop-ast (>= 1.30.0, < 2.0)
2545
ruby-progressbar (~> 1.7)
2646
unicode-display_width (>= 2.4.0, < 3.0)
27-
rubocop-ast (1.30.0)
28-
parser (>= 3.2.1.0)
47+
rubocop-ast (1.31.1)
48+
parser (>= 3.3.0.4)
2949
rubocop-capybara (2.20.0)
3050
rubocop (~> 1.41)
31-
rubocop-discourse (3.6.0)
51+
rubocop-discourse (3.7.1)
52+
activesupport (>= 6.1)
3253
rubocop (>= 1.59.0)
54+
rubocop-capybara (>= 2.0.0)
55+
rubocop-factory_bot (>= 2.0.0)
3356
rubocop-rspec (>= 2.25.0)
3457
rubocop-factory_bot (2.25.1)
3558
rubocop (~> 1.41)
36-
rubocop-rspec (2.26.1)
59+
rubocop-rspec (2.27.1)
3760
rubocop (~> 1.40)
3861
rubocop-capybara (~> 2.17)
3962
rubocop-factory_bot (~> 2.22)
4063
ruby-progressbar (1.13.0)
4164
syntax_tree (6.2.0)
4265
prettier_print (>= 1.2.0)
66+
tzinfo (2.0.6)
67+
concurrent-ruby (~> 1.0)
4368
unicode-display_width (2.5.0)
4469

4570
PLATFORMS

lib/oauth2_basic_authenticator.rb

Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
# frozen_string_literal: true
2+
3+
class OAuth2BasicAuthenticator < Auth::ManagedAuthenticator
4+
def name
5+
"oauth2_basic"
6+
end
7+
8+
def can_revoke?
9+
SiteSetting.oauth2_allow_association_change
10+
end
11+
12+
def can_connect_existing_user?
13+
SiteSetting.oauth2_allow_association_change
14+
end
15+
16+
def register_middleware(omniauth)
17+
omniauth.provider :oauth2_basic,
18+
name: name,
19+
setup:
20+
lambda { |env|
21+
opts = env["omniauth.strategy"].options
22+
opts[:client_id] = SiteSetting.oauth2_client_id
23+
opts[:client_secret] = SiteSetting.oauth2_client_secret
24+
opts[:provider_ignores_state] = SiteSetting.oauth2_disable_csrf
25+
opts[:client_options] = {
26+
authorize_url: SiteSetting.oauth2_authorize_url,
27+
token_url: SiteSetting.oauth2_token_url,
28+
token_method: SiteSetting.oauth2_token_url_method.downcase.to_sym,
29+
}
30+
opts[:authorize_options] = SiteSetting
31+
.oauth2_authorize_options
32+
.split("|")
33+
.map(&:to_sym)
34+
35+
if SiteSetting.oauth2_authorize_signup_url.present? &&
36+
ActionDispatch::Request.new(env).params["signup"].present?
37+
opts[:client_options][
38+
:authorize_url
39+
] = SiteSetting.oauth2_authorize_signup_url
40+
end
41+
42+
if SiteSetting.oauth2_send_auth_header? &&
43+
SiteSetting.oauth2_send_auth_body?
44+
# For maximum compatibility we include both header and body auth by default
45+
# This is a little unusual, and utilising multiple authentication methods
46+
# is technically disallowed by the spec (RFC2749 Section 5.2)
47+
opts[:client_options][:auth_scheme] = :request_body
48+
opts[:token_params] = {
49+
headers: {
50+
"Authorization" => basic_auth_header,
51+
},
52+
}
53+
elsif SiteSetting.oauth2_send_auth_header?
54+
opts[:client_options][:auth_scheme] = :basic_auth
55+
else
56+
opts[:client_options][:auth_scheme] = :request_body
57+
end
58+
59+
unless SiteSetting.oauth2_scope.blank?
60+
opts[:scope] = SiteSetting.oauth2_scope
61+
end
62+
63+
opts[:client_options][:connection_build] = lambda do |builder|
64+
if SiteSetting.oauth2_debug_auth && defined?(OAuth2FaradayFormatter)
65+
builder.response :logger,
66+
Rails.logger,
67+
{ bodies: true, formatter: OAuth2FaradayFormatter }
68+
end
69+
70+
builder.request :url_encoded # form-encode POST params
71+
builder.adapter FinalDestination::FaradayAdapter # make requests with FinalDestination::HTTP
72+
end
73+
}
74+
end
75+
76+
def basic_auth_header
77+
"Basic " +
78+
Base64.strict_encode64("#{SiteSetting.oauth2_client_id}:#{SiteSetting.oauth2_client_secret}")
79+
end
80+
81+
def walk_path(fragment, segments, seg_index = 0)
82+
first_seg = segments[seg_index]
83+
return if first_seg.blank? || fragment.blank?
84+
return nil unless fragment.is_a?(Hash) || fragment.is_a?(Array)
85+
first_seg = segments[seg_index].scan(/([\d+])/).length > 0 ? first_seg.split("[")[0] : first_seg
86+
if fragment.is_a?(Hash)
87+
deref = fragment[first_seg] || fragment[first_seg.to_sym]
88+
else
89+
array_index = 0
90+
if (seg_index > 0)
91+
last_index = segments[seg_index - 1].scan(/([\d+])/).flatten() || [0]
92+
array_index = last_index.length > 0 ? last_index[0].to_i : 0
93+
end
94+
if fragment.any? && fragment.length >= array_index - 1
95+
deref = fragment[array_index][first_seg]
96+
else
97+
deref = nil
98+
end
99+
end
100+
101+
if (deref.blank? || seg_index == segments.size - 1)
102+
deref
103+
else
104+
seg_index += 1
105+
walk_path(deref, segments, seg_index)
106+
end
107+
end
108+
109+
def json_walk(result, user_json, prop, custom_path: nil)
110+
path = custom_path || SiteSetting.public_send("oauth2_json_#{prop}_path")
111+
if path.present?
112+
#this.[].that is the same as this.that, allows for both this[0].that and this.[0].that path styles
113+
path = path.gsub(".[].", ".").gsub(".[", "[")
114+
segments = parse_segments(path)
115+
val = walk_path(user_json, segments)
116+
result[prop] = val if val.present?
117+
end
118+
end
119+
120+
def parse_segments(path)
121+
segments = [+""]
122+
quoted = false
123+
escaped = false
124+
125+
path
126+
.split("")
127+
.each do |char|
128+
next_char_escaped = false
129+
if !escaped && (char == '"')
130+
quoted = !quoted
131+
elsif !escaped && !quoted && (char == ".")
132+
segments.append +""
133+
elsif !escaped && (char == '\\')
134+
next_char_escaped = true
135+
else
136+
segments.last << char
137+
end
138+
escaped = next_char_escaped
139+
end
140+
141+
segments
142+
end
143+
144+
def log(info)
145+
Rails.logger.warn("OAuth2 Debugging: #{info}") if SiteSetting.oauth2_debug_auth
146+
end
147+
148+
def fetch_user_details(token, id)
149+
user_json_url = SiteSetting.oauth2_user_json_url.sub(":token", token.to_s).sub(":id", id.to_s)
150+
user_json_method = SiteSetting.oauth2_user_json_url_method.downcase.to_sym
151+
152+
bearer_token = "Bearer #{token}"
153+
connection = Faraday.new { |f| f.adapter FinalDestination::FaradayAdapter }
154+
headers = { "Authorization" => bearer_token, "Accept" => "application/json" }
155+
user_json_response = connection.run_request(user_json_method, user_json_url, nil, headers)
156+
157+
log <<-LOG
158+
user_json request: #{user_json_method} #{user_json_url}
159+
160+
request headers: #{headers}
161+
162+
response status: #{user_json_response.status}
163+
164+
response body:
165+
#{user_json_response.body}
166+
LOG
167+
168+
if user_json_response.status == 200
169+
user_json = JSON.parse(user_json_response.body)
170+
171+
log("user_json:\n#{user_json.to_yaml}")
172+
173+
result = {}
174+
if user_json.present?
175+
json_walk(result, user_json, :user_id)
176+
json_walk(result, user_json, :username)
177+
json_walk(result, user_json, :name)
178+
json_walk(result, user_json, :email)
179+
json_walk(result, user_json, :email_verified)
180+
json_walk(result, user_json, :avatar)
181+
182+
DiscoursePluginRegistry.oauth2_basic_additional_json_paths.each do |detail|
183+
prop = "extra:#{detail}"
184+
json_walk(result, user_json, prop, custom_path: detail)
185+
end
186+
end
187+
result
188+
else
189+
nil
190+
end
191+
end
192+
193+
def primary_email_verified?(auth)
194+
return true if SiteSetting.oauth2_email_verified
195+
verified = auth["info"]["email_verified"]
196+
verified = true if verified == "true"
197+
verified = false if verified == "false"
198+
verified
199+
end
200+
201+
def always_update_user_email?
202+
SiteSetting.oauth2_overrides_email
203+
end
204+
205+
def after_authenticate(auth, existing_account: nil)
206+
log <<-LOG
207+
after_authenticate response:
208+
209+
creds:
210+
#{auth["credentials"].to_hash.to_yaml}
211+
212+
uid: #{auth["uid"]}
213+
214+
info:
215+
#{auth["info"].to_hash.to_yaml}
216+
217+
extra:
218+
#{auth["extra"].to_hash.to_yaml}
219+
LOG
220+
221+
if SiteSetting.oauth2_fetch_user_details? && SiteSetting.oauth2_user_json_url.present?
222+
if fetched_user_details = fetch_user_details(auth["credentials"]["token"], auth["uid"])
223+
auth["uid"] = fetched_user_details[:user_id] if fetched_user_details[:user_id]
224+
auth["info"]["nickname"] = fetched_user_details[:username] if fetched_user_details[
225+
:username
226+
]
227+
auth["info"]["image"] = fetched_user_details[:avatar] if fetched_user_details[:avatar]
228+
%w[name email email_verified].each do |property|
229+
auth["info"][property] = fetched_user_details[property.to_sym] if fetched_user_details[
230+
property.to_sym
231+
]
232+
end
233+
234+
DiscoursePluginRegistry.oauth2_basic_additional_json_paths.each do |detail|
235+
auth["extra"][detail] = fetched_user_details["extra:#{detail}"]
236+
end
237+
238+
DiscoursePluginRegistry.oauth2_basic_required_json_paths.each do |x|
239+
if fetched_user_details[x[:path]] != x[:required_value]
240+
result = Auth::Result.new
241+
result.failed = true
242+
result.failed_reason = x[:error_message]
243+
return result
244+
end
245+
end
246+
else
247+
result = Auth::Result.new
248+
result.failed = true
249+
result.failed_reason = I18n.t("login.authenticator_error_fetch_user_details")
250+
return result
251+
end
252+
end
253+
254+
super(auth, existing_account: existing_account)
255+
end
256+
257+
def enabled?
258+
SiteSetting.oauth2_enabled
259+
end
260+
end

lib/oauth2_faraday_formatter.rb

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# frozen_string_literal: true
2+
3+
require "faraday/logging/formatter"
4+
5+
class OAuth2FaradayFormatter < Faraday::Logging::Formatter
6+
def request(env)
7+
warn <<~LOG
8+
OAuth2 Debugging: request #{env.method.upcase} #{env.url}
9+
10+
Headers:
11+
#{env.request_headers.to_yaml}
12+
13+
Body:
14+
#{env[:body].to_yaml}
15+
LOG
16+
end
17+
18+
def response(env)
19+
warn <<~LOG
20+
OAuth2 Debugging: response status #{env.status}
21+
22+
From #{env.method.upcase} #{env.url}
23+
24+
Headers:
25+
#{env.request_headers.to_yaml}
26+
27+
Body:
28+
#{env[:body].to_yaml}
29+
LOG
30+
end
31+
end

0 commit comments

Comments
 (0)