Skip to content

Commit

Permalink
DEV: Fix new Rubocop offenses
Browse files Browse the repository at this point in the history
  • Loading branch information
Flink committed Mar 6, 2024
1 parent cd51d8b commit 5cd7a79
Show file tree
Hide file tree
Showing 5 changed files with 363 additions and 333 deletions.
37 changes: 31 additions & 6 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,19 +1,39 @@
GEM
remote: https://rubygems.org/
specs:
activesupport (7.1.3.2)
base64
bigdecimal
concurrent-ruby (~> 1.0, >= 1.0.2)
connection_pool (>= 2.2.5)
drb
i18n (>= 1.6, < 2)
minitest (>= 5.1)
mutex_m
tzinfo (~> 2.0)
ast (2.4.2)
base64 (0.2.0)
bigdecimal (3.1.6)
concurrent-ruby (1.2.3)
connection_pool (2.4.1)
drb (2.2.1)
i18n (1.14.3)
concurrent-ruby (~> 1.0)
racc (~> 1.7)
json (2.7.1)
language_server-protocol (3.17.0.3)
minitest (5.22.2)
mutex_m (0.2.0)
parallel (1.24.0)
parser (3.3.0.4)
parser (3.3.0.5)
ast (~> 2.4.1)
racc
prettier_print (1.2.1)
racc (1.7.3)
rainbow (3.1.1)
regexp_parser (2.9.0)
rexml (3.2.6)
rubocop (1.60.0)
rubocop (1.61.0)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
parallel (~> 1.10)
Expand All @@ -24,22 +44,27 @@ GEM
rubocop-ast (>= 1.30.0, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.30.0)
parser (>= 3.2.1.0)
rubocop-ast (1.31.1)
parser (>= 3.3.0.4)
rubocop-capybara (2.20.0)
rubocop (~> 1.41)
rubocop-discourse (3.6.0)
rubocop-discourse (3.7.1)
activesupport (>= 6.1)
rubocop (>= 1.59.0)
rubocop-capybara (>= 2.0.0)
rubocop-factory_bot (>= 2.0.0)
rubocop-rspec (>= 2.25.0)
rubocop-factory_bot (2.25.1)
rubocop (~> 1.41)
rubocop-rspec (2.26.1)
rubocop-rspec (2.27.1)
rubocop (~> 1.40)
rubocop-capybara (~> 2.17)
rubocop-factory_bot (~> 2.22)
ruby-progressbar (1.13.0)
syntax_tree (6.2.0)
prettier_print (>= 1.2.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
unicode-display_width (2.5.0)

PLATFORMS
Expand Down
260 changes: 260 additions & 0 deletions lib/oauth2_basic_authenticator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
# frozen_string_literal: true

class OAuth2BasicAuthenticator < Auth::ManagedAuthenticator
def name
"oauth2_basic"
end

def can_revoke?
SiteSetting.oauth2_allow_association_change
end

def can_connect_existing_user?
SiteSetting.oauth2_allow_association_change
end

def register_middleware(omniauth)
omniauth.provider :oauth2_basic,
name: name,
setup:
lambda { |env|
opts = env["omniauth.strategy"].options
opts[:client_id] = SiteSetting.oauth2_client_id
opts[:client_secret] = SiteSetting.oauth2_client_secret
opts[:provider_ignores_state] = SiteSetting.oauth2_disable_csrf
opts[:client_options] = {
authorize_url: SiteSetting.oauth2_authorize_url,
token_url: SiteSetting.oauth2_token_url,
token_method: SiteSetting.oauth2_token_url_method.downcase.to_sym,
}
opts[:authorize_options] = SiteSetting
.oauth2_authorize_options
.split("|")
.map(&:to_sym)

if SiteSetting.oauth2_authorize_signup_url.present? &&
ActionDispatch::Request.new(env).params["signup"].present?
opts[:client_options][
:authorize_url
] = SiteSetting.oauth2_authorize_signup_url
end

if SiteSetting.oauth2_send_auth_header? &&
SiteSetting.oauth2_send_auth_body?
# For maximum compatibility we include both header and body auth by default
# This is a little unusual, and utilising multiple authentication methods
# is technically disallowed by the spec (RFC2749 Section 5.2)
opts[:client_options][:auth_scheme] = :request_body
opts[:token_params] = {
headers: {
"Authorization" => basic_auth_header,
},
}
elsif SiteSetting.oauth2_send_auth_header?
opts[:client_options][:auth_scheme] = :basic_auth
else
opts[:client_options][:auth_scheme] = :request_body
end

unless SiteSetting.oauth2_scope.blank?
opts[:scope] = SiteSetting.oauth2_scope
end

opts[:client_options][:connection_build] = lambda do |builder|
if SiteSetting.oauth2_debug_auth && defined?(OAuth2FaradayFormatter)
builder.response :logger,
Rails.logger,
{ bodies: true, formatter: OAuth2FaradayFormatter }
end

builder.request :url_encoded # form-encode POST params
builder.adapter FinalDestination::FaradayAdapter # make requests with FinalDestination::HTTP
end
}
end

def basic_auth_header
"Basic " +
Base64.strict_encode64("#{SiteSetting.oauth2_client_id}:#{SiteSetting.oauth2_client_secret}")
end

def walk_path(fragment, segments, seg_index = 0)
first_seg = segments[seg_index]
return if first_seg.blank? || fragment.blank?
return nil unless fragment.is_a?(Hash) || fragment.is_a?(Array)
first_seg = segments[seg_index].scan(/([\d+])/).length > 0 ? first_seg.split("[")[0] : first_seg
if fragment.is_a?(Hash)
deref = fragment[first_seg] || fragment[first_seg.to_sym]
else
array_index = 0
if (seg_index > 0)
last_index = segments[seg_index - 1].scan(/([\d+])/).flatten() || [0]
array_index = last_index.length > 0 ? last_index[0].to_i : 0
end
if fragment.any? && fragment.length >= array_index - 1
deref = fragment[array_index][first_seg]
else
deref = nil
end
end

if (deref.blank? || seg_index == segments.size - 1)
deref
else
seg_index += 1
walk_path(deref, segments, seg_index)
end
end

def json_walk(result, user_json, prop, custom_path: nil)
path = custom_path || SiteSetting.public_send("oauth2_json_#{prop}_path")
if path.present?
#this.[].that is the same as this.that, allows for both this[0].that and this.[0].that path styles
path = path.gsub(".[].", ".").gsub(".[", "[")
segments = parse_segments(path)
val = walk_path(user_json, segments)
result[prop] = val if val.present?
end
end

def parse_segments(path)
segments = [+""]
quoted = false
escaped = false

path
.split("")
.each do |char|
next_char_escaped = false
if !escaped && (char == '"')
quoted = !quoted
elsif !escaped && !quoted && (char == ".")
segments.append +""
elsif !escaped && (char == '\\')
next_char_escaped = true
else
segments.last << char
end
escaped = next_char_escaped
end

segments
end

def log(info)
Rails.logger.warn("OAuth2 Debugging: #{info}") if SiteSetting.oauth2_debug_auth
end

def fetch_user_details(token, id)
user_json_url = SiteSetting.oauth2_user_json_url.sub(":token", token.to_s).sub(":id", id.to_s)
user_json_method = SiteSetting.oauth2_user_json_url_method.downcase.to_sym

bearer_token = "Bearer #{token}"
connection = Faraday.new { |f| f.adapter FinalDestination::FaradayAdapter }
headers = { "Authorization" => bearer_token, "Accept" => "application/json" }
user_json_response = connection.run_request(user_json_method, user_json_url, nil, headers)

log <<-LOG
user_json request: #{user_json_method} #{user_json_url}
request headers: #{headers}
response status: #{user_json_response.status}
response body:
#{user_json_response.body}
LOG

if user_json_response.status == 200
user_json = JSON.parse(user_json_response.body)

log("user_json:\n#{user_json.to_yaml}")

result = {}
if user_json.present?
json_walk(result, user_json, :user_id)
json_walk(result, user_json, :username)
json_walk(result, user_json, :name)
json_walk(result, user_json, :email)
json_walk(result, user_json, :email_verified)
json_walk(result, user_json, :avatar)

DiscoursePluginRegistry.oauth2_basic_additional_json_paths.each do |detail|
prop = "extra:#{detail}"
json_walk(result, user_json, prop, custom_path: detail)
end
end
result
else
nil
end
end

def primary_email_verified?(auth)
return true if SiteSetting.oauth2_email_verified
verified = auth["info"]["email_verified"]
verified = true if verified == "true"
verified = false if verified == "false"
verified
end

def always_update_user_email?
SiteSetting.oauth2_overrides_email
end

def after_authenticate(auth, existing_account: nil)
log <<-LOG
after_authenticate response:
creds:
#{auth["credentials"].to_hash.to_yaml}
uid: #{auth["uid"]}
info:
#{auth["info"].to_hash.to_yaml}
extra:
#{auth["extra"].to_hash.to_yaml}
LOG

if SiteSetting.oauth2_fetch_user_details? && SiteSetting.oauth2_user_json_url.present?
if fetched_user_details = fetch_user_details(auth["credentials"]["token"], auth["uid"])
auth["uid"] = fetched_user_details[:user_id] if fetched_user_details[:user_id]
auth["info"]["nickname"] = fetched_user_details[:username] if fetched_user_details[
:username
]
auth["info"]["image"] = fetched_user_details[:avatar] if fetched_user_details[:avatar]
%w[name email email_verified].each do |property|
auth["info"][property] = fetched_user_details[property.to_sym] if fetched_user_details[
property.to_sym
]
end

DiscoursePluginRegistry.oauth2_basic_additional_json_paths.each do |detail|
auth["extra"][detail] = fetched_user_details["extra:#{detail}"]
end

DiscoursePluginRegistry.oauth2_basic_required_json_paths.each do |x|
if fetched_user_details[x[:path]] != x[:required_value]
result = Auth::Result.new
result.failed = true
result.failed_reason = x[:error_message]
return result
end
end
else
result = Auth::Result.new
result.failed = true
result.failed_reason = I18n.t("login.authenticator_error_fetch_user_details")
return result
end
end

super(auth, existing_account: existing_account)
end

def enabled?
SiteSetting.oauth2_enabled
end
end
31 changes: 31 additions & 0 deletions lib/oauth2_faraday_formatter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# frozen_string_literal: true

require "faraday/logging/formatter"

class OAuth2FaradayFormatter < Faraday::Logging::Formatter
def request(env)
warn <<~LOG
OAuth2 Debugging: request #{env.method.upcase} #{env.url}
Headers:
#{env.request_headers.to_yaml}
Body:
#{env[:body].to_yaml}
LOG
end

def response(env)
warn <<~LOG
OAuth2 Debugging: response status #{env.status}
From #{env.method.upcase} #{env.url}
Headers:
#{env.request_headers.to_yaml}
Body:
#{env[:body].to_yaml}
LOG
end
end
Loading

0 comments on commit 5cd7a79

Please sign in to comment.