DEV: Introduce syntax_tree for ruby formatting (#75)

This commit is contained in:
David Taylor 2022-12-29 12:33:21 +00:00 committed by GitHub
parent 4a5259d81e
commit ccf9d5d32c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 235 additions and 190 deletions

View File

@ -55,3 +55,12 @@ jobs:
- name: Rubocop - name: Rubocop
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
run: bundle exec rubocop . run: bundle exec rubocop .
- name: Syntax Tree
if: ${{ !cancelled() }}
run: |
if test -f .streerc; then
bundle exec stree check Gemfile $(git ls-files '*.rb') $(git ls-files '*.rake')
else
echo "Stree config not detected for this repository. Skipping."
fi

View File

@ -80,7 +80,7 @@ jobs:
- name: Get yarn cache directory - name: Get yarn cache directory
id: yarn-cache-dir id: yarn-cache-dir
run: echo "::set-output name=dir::$(yarn cache dir)" run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
- name: Yarn cache - name: Yarn cache
uses: actions/cache@v3 uses: actions/cache@v3
@ -130,7 +130,7 @@ jobs:
shell: bash shell: bash
run: | run: |
if [ 0 -lt $(find plugins/${{ github.event.repository.name }}/spec -type f -name "*.rb" 2> /dev/null | wc -l) ]; then if [ 0 -lt $(find plugins/${{ github.event.repository.name }}/spec -type f -name "*.rb" 2> /dev/null | wc -l) ]; then
echo "::set-output name=files_exist::true" echo "files_exist=true" >> $GITHUB_OUTPUT
fi fi
- name: Plugin RSpec - name: Plugin RSpec
@ -142,7 +142,7 @@ jobs:
shell: bash shell: bash
run: | run: |
if [ 0 -lt $(find plugins/${{ github.event.repository.name }}/test/javascripts -type f \( -name "*.js" -or -name "*.es6" \) 2> /dev/null | wc -l) ]; then if [ 0 -lt $(find plugins/${{ github.event.repository.name }}/test/javascripts -type f \( -name "*.js" -or -name "*.es6" \) 2> /dev/null | wc -l) ]; then
echo "::set-output name=files_exist::true" echo "files_exist=true" >> $GITHUB_OUTPUT
fi fi
- name: Plugin QUnit - name: Plugin QUnit

View File

@ -1,2 +1,2 @@
inherit_gem: inherit_gem:
rubocop-discourse: default.yml rubocop-discourse: stree-compat.yml

2
.streerc Normal file
View File

@ -0,0 +1,2 @@
--print-width=100
--plugins=plugin/trailing_comma

View File

@ -1,7 +1,8 @@
# frozen_string_literal: true # frozen_string_literal: true
source 'https://rubygems.org' source "https://rubygems.org"
group :development do group :development do
gem 'rubocop-discourse' gem "rubocop-discourse"
gem "syntax_tree"
end end

View File

@ -6,6 +6,7 @@ GEM
parallel (1.22.1) parallel (1.22.1)
parser (3.1.2.1) parser (3.1.2.1)
ast (~> 2.4.1) ast (~> 2.4.1)
prettier_print (1.2.0)
rainbow (3.1.1) rainbow (3.1.1)
regexp_parser (2.6.0) regexp_parser (2.6.0)
rexml (3.2.5) rexml (3.2.5)
@ -27,6 +28,8 @@ GEM
rubocop-rspec (2.13.2) rubocop-rspec (2.13.2)
rubocop (~> 1.33) rubocop (~> 1.33)
ruby-progressbar (1.11.0) ruby-progressbar (1.11.0)
syntax_tree (5.1.0)
prettier_print (>= 1.2.0)
unicode-display_width (2.3.0) unicode-display_width (2.3.0)
PLATFORMS PLATFORMS
@ -39,6 +42,7 @@ PLATFORMS
DEPENDENCIES DEPENDENCIES
rubocop-discourse rubocop-discourse
syntax_tree
BUNDLED WITH BUNDLED WITH
2.3.10 2.3.10

166
plugin.rb
View File

@ -13,19 +13,19 @@ class ::OmniAuth::Strategies::Oauth2Basic < ::OmniAuth::Strategies::OAuth2
option :name, "oauth2_basic" option :name, "oauth2_basic"
uid do uid do
if path = SiteSetting.oauth2_callback_user_id_path.split('.') if path = SiteSetting.oauth2_callback_user_id_path.split(".")
recurse(access_token, [*path]) if path.present? recurse(access_token, [*path]) if path.present?
end end
end end
info do info do
if paths = SiteSetting.oauth2_callback_user_info_paths.split('|') if paths = SiteSetting.oauth2_callback_user_info_paths.split("|")
result = Hash.new result = Hash.new
paths.each do |p| paths.each do |p|
segments = p.split(':') segments = p.split(":")
if segments.length == 2 if segments.length == 2
key = segments.first key = segments.first
path = [*segments.last.split('.')] path = [*segments.last.split(".")]
result[key] = recurse(access_token, path) result[key] = recurse(access_token, path)
end end
end end
@ -45,7 +45,7 @@ class ::OmniAuth::Strategies::Oauth2Basic < ::OmniAuth::Strategies::OAuth2
end end
end end
require 'faraday/logging/formatter' require "faraday/logging/formatter"
class OAuth2FaradayFormatter < Faraday::Logging::Formatter class OAuth2FaradayFormatter < Faraday::Logging::Formatter
def request(env) def request(env)
warn <<~LOG warn <<~LOG
@ -76,7 +76,7 @@ DiscoursePluginRegistry.define_filtered_register :oauth2_basic_additional_json_p
class ::OAuth2BasicAuthenticator < Auth::ManagedAuthenticator class ::OAuth2BasicAuthenticator < Auth::ManagedAuthenticator
def name def name
'oauth2_basic' "oauth2_basic"
end end
def can_revoke? def can_revoke?
@ -90,52 +90,66 @@ class ::OAuth2BasicAuthenticator < Auth::ManagedAuthenticator
def register_middleware(omniauth) def register_middleware(omniauth)
omniauth.provider :oauth2_basic, omniauth.provider :oauth2_basic,
name: name, name: name,
setup: lambda { |env| setup:
opts = env['omniauth.strategy'].options lambda { |env|
opts[:client_id] = SiteSetting.oauth2_client_id opts = env["omniauth.strategy"].options
opts[:client_secret] = SiteSetting.oauth2_client_secret opts[:client_id] = SiteSetting.oauth2_client_id
opts[:provider_ignores_state] = SiteSetting.oauth2_disable_csrf opts[:client_secret] = SiteSetting.oauth2_client_secret
opts[:client_options] = { opts[:provider_ignores_state] = SiteSetting.oauth2_disable_csrf
authorize_url: SiteSetting.oauth2_authorize_url, opts[:client_options] = {
token_url: SiteSetting.oauth2_token_url, authorize_url: SiteSetting.oauth2_authorize_url,
token_method: SiteSetting.oauth2_token_url_method.downcase.to_sym 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) }
opts[:authorize_options] = SiteSetting
.oauth2_authorize_options
.split("|")
.map(&:to_sym)
if SiteSetting.oauth2_authorize_signup_url.present? && if SiteSetting.oauth2_authorize_signup_url.present? &&
ActionDispatch::Request.new(env).params["signup"].present? ActionDispatch::Request.new(env).params["signup"].present?
opts[:client_options][:authorize_url] = SiteSetting.oauth2_authorize_signup_url opts[:client_options][
end :authorize_url
] = SiteSetting.oauth2_authorize_signup_url
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 { |builder|
if SiteSetting.oauth2_debug_auth && defined? OAuth2FaradayFormatter
builder.response :logger, Rails.logger, { bodies: true, formatter: OAuth2FaradayFormatter }
end end
builder.request :url_encoded # form-encode POST params if SiteSetting.oauth2_send_auth_header? &&
builder.adapter FinalDestination::FaradayAdapter # make requests with FinalDestination::HTTP 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 end
def basic_auth_header def basic_auth_header
"Basic " + Base64.strict_encode64("#{SiteSetting.oauth2_client_id}:#{SiteSetting.oauth2_client_secret}") "Basic " +
Base64.strict_encode64("#{SiteSetting.oauth2_client_id}:#{SiteSetting.oauth2_client_secret}")
end end
def walk_path(fragment, segments, seg_index = 0) def walk_path(fragment, segments, seg_index = 0)
@ -182,19 +196,21 @@ class ::OAuth2BasicAuthenticator < Auth::ManagedAuthenticator
quoted = false quoted = false
escaped = false escaped = false
path.split("").each do |char| path
next_char_escaped = false .split("")
if !escaped && (char == '"') .each do |char|
quoted = !quoted next_char_escaped = false
elsif !escaped && !quoted && (char == '.') if !escaped && (char == '"')
segments.append +"" quoted = !quoted
elsif !escaped && (char == '\\') elsif !escaped && !quoted && (char == ".")
next_char_escaped = true segments.append +""
else elsif !escaped && (char == '\\')
segments.last << char next_char_escaped = true
else
segments.last << char
end
escaped = next_char_escaped
end end
escaped = next_char_escaped
end
segments segments
end end
@ -204,14 +220,14 @@ class ::OAuth2BasicAuthenticator < Auth::ManagedAuthenticator
end end
def fetch_user_details(token, id) 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_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 user_json_method = SiteSetting.oauth2_user_json_url_method.downcase.to_sym
log("user_json_url: #{user_json_method} #{user_json_url}") log("user_json_url: #{user_json_method} #{user_json_url}")
bearer_token = "Bearer #{token}" bearer_token = "Bearer #{token}"
connection = Faraday.new { |f| f.adapter FinalDestination::FaradayAdapter } connection = Faraday.new { |f| f.adapter FinalDestination::FaradayAdapter }
headers = { 'Authorization' => bearer_token, 'Accept' => 'application/json' } headers = { "Authorization" => bearer_token, "Accept" => "application/json" }
user_json_response = connection.run_request(user_json_method, user_json_url, nil, headers) user_json_response = connection.run_request(user_json_method, user_json_url, nil, headers)
log("user_json_response: #{user_json_response.inspect}") log("user_json_response: #{user_json_response.inspect}")
@ -243,7 +259,7 @@ class ::OAuth2BasicAuthenticator < Auth::ManagedAuthenticator
def primary_email_verified?(auth) def primary_email_verified?(auth)
return true if SiteSetting.oauth2_email_verified return true if SiteSetting.oauth2_email_verified
verified = auth['info']['email_verified'] verified = auth["info"]["email_verified"]
verified = true if verified == "true" verified = true if verified == "true"
verified = false if verified == "false" verified = false if verified == "false"
verified verified
@ -254,19 +270,25 @@ class ::OAuth2BasicAuthenticator < Auth::ManagedAuthenticator
end end
def after_authenticate(auth, existing_account: nil) def after_authenticate(auth, existing_account: nil)
log("after_authenticate response: \n\ncreds: #{auth['credentials'].to_hash}\nuid: #{auth['uid']}\ninfo: #{auth['info'].to_hash}\nextra: #{auth['extra'].to_hash}") log(
"after_authenticate response: \n\ncreds: #{auth["credentials"].to_hash}\nuid: #{auth["uid"]}\ninfo: #{auth["info"].to_hash}\nextra: #{auth["extra"].to_hash}",
)
if SiteSetting.oauth2_fetch_user_details? if SiteSetting.oauth2_fetch_user_details?
if fetched_user_details = fetch_user_details(auth['credentials']['token'], auth['uid']) 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["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"]["nickname"] = fetched_user_details[:username] if fetched_user_details[
auth['info']['image'] = fetched_user_details[:avatar] if fetched_user_details[:avatar] :username
['name', 'email', 'email_verified'].each do |property| ]
auth['info'][property] = fetched_user_details[property.to_sym] if fetched_user_details[property.to_sym] 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 end
DiscoursePluginRegistry.oauth2_basic_additional_json_paths.each do |detail| DiscoursePluginRegistry.oauth2_basic_additional_json_paths.each do |detail|
auth['extra'][detail] = fetched_user_details["extra:#{detail}"] auth["extra"][detail] = fetched_user_details["extra:#{detail}"]
end end
else else
result = Auth::Result.new result = Auth::Result.new
@ -284,7 +306,9 @@ class ::OAuth2BasicAuthenticator < Auth::ManagedAuthenticator
end end
end end
auth_provider title_setting: "oauth2_button_title", auth_provider title_setting: "oauth2_button_title", authenticator: OAuth2BasicAuthenticator.new
authenticator: OAuth2BasicAuthenticator.new
load File.expand_path("../lib/validators/oauth2_basic/oauth2_fetch_user_details_validator.rb", __FILE__) load File.expand_path(
"../lib/validators/oauth2_basic/oauth2_fetch_user_details_validator.rb",
__FILE__,
)

View File

@ -6,7 +6,9 @@ describe "OAuth2 Overrides Email", type: :request do
fab!(:initial_email) { "initial@example.com" } fab!(:initial_email) { "initial@example.com" }
fab!(:new_email) { "new@example.com" } fab!(:new_email) { "new@example.com" }
fab!(:user) { Fabricate(:user, email: initial_email) } fab!(:user) { Fabricate(:user, email: initial_email) }
fab!(:uac) { UserAssociatedAccount.create!(user: user, provider_name: "oauth2_basic", provider_uid: "12345") } fab!(:uac) do
UserAssociatedAccount.create!(user: user, provider_name: "oauth2_basic", provider_uid: "12345")
end
before do before do
SiteSetting.oauth2_enabled = true SiteSetting.oauth2_enabled = true
@ -16,17 +18,13 @@ describe "OAuth2 Overrides Email", type: :request do
OmniAuth.config.test_mode = true OmniAuth.config.test_mode = true
OmniAuth.config.mock_auth[:oauth2_basic] = OmniAuth::AuthHash.new( OmniAuth.config.mock_auth[:oauth2_basic] = OmniAuth::AuthHash.new(
provider: 'oauth2_basic', provider: "oauth2_basic",
uid: '12345', uid: "12345",
info: OmniAuth::AuthHash::InfoHash.new( info: OmniAuth::AuthHash::InfoHash.new(email: new_email),
email: new_email
),
extra: { extra: {
raw_info: OmniAuth::AuthHash.new( raw_info: OmniAuth::AuthHash.new(email_verified: true),
email_verified: true
)
}, },
credentials: OmniAuth::AuthHash.new credentials: OmniAuth::AuthHash.new,
) )
end end
@ -40,7 +38,7 @@ describe "OAuth2 Overrides Email", type: :request do
expect(user.reload.email).to eq(initial_email) expect(user.reload.email).to eq(initial_email)
end end
it 'updates user email if enabled' do it "updates user email if enabled" do
SiteSetting.oauth2_overrides_email = true SiteSetting.oauth2_overrides_email = true
get "/auth/oauth2_basic/callback" get "/auth/oauth2_basic/callback"

View File

@ -1,31 +1,36 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'rails_helper' require "rails_helper"
describe OAuth2BasicAuthenticator do describe OAuth2BasicAuthenticator do
describe 'after_authenticate' do describe "after_authenticate" do
let(:user) { Fabricate(:user) } let(:user) { Fabricate(:user) }
let(:authenticator) { OAuth2BasicAuthenticator.new } let(:authenticator) { OAuth2BasicAuthenticator.new }
let(:auth) do let(:auth) do
OmniAuth::AuthHash.new('provider' => 'oauth2_basic', OmniAuth::AuthHash.new(
'credentials' => { 'token': 'token' }, "provider" => "oauth2_basic",
'uid' => '123456789', "credentials" => {
'info' => { id: 'id' }, token: "token",
'extra' => {}) },
"uid" => "123456789",
"info" => {
id: "id",
},
"extra" => {
},
)
end end
before(:each) do before(:each) { SiteSetting.oauth2_email_verified = true }
SiteSetting.oauth2_email_verified = true
end
it 'finds user by email' do it "finds user by email" do
authenticator.expects(:fetch_user_details).returns(email: user.email) authenticator.expects(:fetch_user_details).returns(email: user.email)
result = authenticator.after_authenticate(auth) result = authenticator.after_authenticate(auth)
expect(result.user).to eq(user) expect(result.user).to eq(user)
end end
it 'validates user email if provider has verified' do it "validates user email if provider has verified" do
SiteSetting.oauth2_email_verified = false SiteSetting.oauth2_email_verified = false
authenticator.stubs(:fetch_user_details).returns(email: user.email, email_verified: true) authenticator.stubs(:fetch_user_details).returns(email: user.email, email_verified: true)
result = authenticator.after_authenticate(auth) result = authenticator.after_authenticate(auth)
@ -46,13 +51,13 @@ describe OAuth2BasicAuthenticator do
expect(result.email_valid).to eq(true) expect(result.email_valid).to eq(true)
end end
it 'handles true/false strings from identity provider' do it "handles true/false strings from identity provider" do
SiteSetting.oauth2_email_verified = false SiteSetting.oauth2_email_verified = false
authenticator.stubs(:fetch_user_details).returns(email: user.email, email_verified: 'true') authenticator.stubs(:fetch_user_details).returns(email: user.email, email_verified: "true")
result = authenticator.after_authenticate(auth) result = authenticator.after_authenticate(auth)
expect(result.email_valid).to eq(true) expect(result.email_valid).to eq(true)
authenticator.stubs(:fetch_user_details).returns(email: user.email, email_verified: 'false') authenticator.stubs(:fetch_user_details).returns(email: user.email, email_verified: "false")
result = authenticator.after_authenticate(auth) result = authenticator.after_authenticate(auth)
expect(result.email_valid).to eq(false) expect(result.email_valid).to eq(false)
end end
@ -61,21 +66,16 @@ describe OAuth2BasicAuthenticator do
before(:each) do before(:each) do
SiteSetting.oauth2_fetch_user_details = true SiteSetting.oauth2_fetch_user_details = true
SiteSetting.oauth2_user_json_url = "https://provider.com/user" SiteSetting.oauth2_user_json_url = "https://provider.com/user"
SiteSetting.oauth2_user_json_url_method = 'GET' SiteSetting.oauth2_user_json_url_method = "GET"
SiteSetting.oauth2_json_email_path = 'account.email' SiteSetting.oauth2_json_email_path = "account.email"
end end
let(:success_response) do let(:success_response) do
{ { status: 200, body: '{"account":{"email":"newemail@example.com"}}' }
status: 200,
body: '{"account":{"email":"newemail@example.com"}}'
}
end end
let (:fail_response) do let (:fail_response) do
{ { status: 403 }
status: 403
}
end end
it "works" do it "works" do
@ -83,7 +83,7 @@ describe OAuth2BasicAuthenticator do
result = authenticator.after_authenticate(auth) result = authenticator.after_authenticate(auth)
expect(result.email).to eq("newemail@example.com") expect(result.email).to eq("newemail@example.com")
SiteSetting.oauth2_user_json_url_method = 'POST' SiteSetting.oauth2_user_json_url_method = "POST"
stub_request(:post, SiteSetting.oauth2_user_json_url).to_return(success_response) stub_request(:post, SiteSetting.oauth2_user_json_url).to_return(success_response)
result = authenticator.after_authenticate(auth) result = authenticator.after_authenticate(auth)
expect(result.email).to eq("newemail@example.com") expect(result.email).to eq("newemail@example.com")
@ -94,27 +94,27 @@ describe OAuth2BasicAuthenticator do
result = authenticator.after_authenticate(auth) result = authenticator.after_authenticate(auth)
expect(result.failed).to eq(true) expect(result.failed).to eq(true)
SiteSetting.oauth2_user_json_url_method = 'POST' SiteSetting.oauth2_user_json_url_method = "POST"
stub_request(:post, SiteSetting.oauth2_user_json_url).to_return(fail_response) stub_request(:post, SiteSetting.oauth2_user_json_url).to_return(fail_response)
result = authenticator.after_authenticate(auth) result = authenticator.after_authenticate(auth)
expect(result.failed).to eq(true) expect(result.failed).to eq(true)
end end
describe 'fetch custom attributes' do describe "fetch custom attributes" do
after { DiscoursePluginRegistry.reset_register!(:oauth2_basic_additional_json_paths) } after { DiscoursePluginRegistry.reset_register!(:oauth2_basic_additional_json_paths) }
let(:response) do let(:response) do
{ {
status: 200, status: 200,
body: '{"account":{"email":"newemail@example.com","custom_attr":"received"}}' body: '{"account":{"email":"newemail@example.com","custom_attr":"received"}}',
} }
end end
it 'stores custom attributes in the user associated account' do it "stores custom attributes in the user associated account" do
custom_path = 'account.custom_attr' custom_path = "account.custom_attr"
DiscoursePluginRegistry.register_oauth2_basic_additional_json_path( DiscoursePluginRegistry.register_oauth2_basic_additional_json_path(
custom_path, custom_path,
Plugin::Instance.new Plugin::Instance.new,
) )
stub_request(:get, SiteSetting.oauth2_user_json_url).to_return(response) stub_request(:get, SiteSetting.oauth2_user_json_url).to_return(response)
@ -126,7 +126,7 @@ describe OAuth2BasicAuthenticator do
end end
end end
describe 'avatar downloading' do describe "avatar downloading" do
before do before do
SiteSetting.queue_jobs = true SiteSetting.queue_jobs = true
SiteSetting.oauth2_fetch_user_details = true SiteSetting.oauth2_fetch_user_details = true
@ -136,61 +136,66 @@ describe OAuth2BasicAuthenticator do
let(:job_klass) { Jobs::DownloadAvatarFromUrl } let(:job_klass) { Jobs::DownloadAvatarFromUrl }
before do before do
png = Base64.decode64("R0lGODlhAQABALMAAAAAAIAAAACAAICAAAAAgIAAgACAgMDAwICAgP8AAAD/AP//AAAA//8A/wD//wBiZCH5BAEAAA8ALAAAAAABAAEAAAQC8EUAOw==") png =
stub_request(:get, "http://avatar.example.com/avatar.png").to_return(body: png, headers: { "Content-Type" => "image/png" }) Base64.decode64(
"R0lGODlhAQABALMAAAAAAIAAAACAAICAAAAAgIAAgACAgMDAwICAgP8AAAD/AP//AAAA//8A/wD//wBiZCH5BAEAAA8ALAAAAAABAAEAAAQC8EUAOw==",
)
stub_request(:get, "http://avatar.example.com/avatar.png").to_return(
body: png,
headers: {
"Content-Type" => "image/png",
},
)
end end
it 'enqueues a download_avatar_from_url job for existing user' do it "enqueues a download_avatar_from_url job for existing user" do
authenticator.expects(:fetch_user_details).returns( authenticator.expects(:fetch_user_details).returns(
email: user.email, email: user.email,
avatar: 'http://avatar.example.com/avatar.png' avatar: "http://avatar.example.com/avatar.png",
) )
expect { expect { authenticator.after_authenticate(auth) }.to change { job_klass.jobs.count }.by(1)
authenticator.after_authenticate(auth)
}.to change { job_klass.jobs.count }.by(1)
job_args = job_klass.jobs.last['args'].first job_args = job_klass.jobs.last["args"].first
expect(job_args['url']).to eq("http://avatar.example.com/avatar.png") expect(job_args["url"]).to eq("http://avatar.example.com/avatar.png")
expect(job_args['user_id']).to eq(user.id) expect(job_args["user_id"]).to eq(user.id)
expect(job_args['override_gravatar']).to eq(false) expect(job_args["override_gravatar"]).to eq(false)
end end
it 'enqueues a download_avatar_from_url job for new user' do it "enqueues a download_avatar_from_url job for new user" do
authenticator.expects(:fetch_user_details).returns( authenticator.expects(:fetch_user_details).returns(
email: "unknown@user.com", email: "unknown@user.com",
avatar: 'http://avatar.example.com/avatar.png' avatar: "http://avatar.example.com/avatar.png",
) )
auth_result = nil auth_result = nil
expect { expect { auth_result = authenticator.after_authenticate(auth) }.not_to change {
auth_result = authenticator.after_authenticate(auth) job_klass.jobs.count
}.not_to change { job_klass.jobs.count } }
expect { expect { authenticator.after_create_account(user, auth_result) }.to change {
authenticator.after_create_account(user, auth_result) job_klass.jobs.count
}.to change { job_klass.jobs.count }.by(1) }.by(1)
job_args = job_klass.jobs.last['args'].first job_args = job_klass.jobs.last["args"].first
expect(job_args['url']).to eq("http://avatar.example.com/avatar.png") expect(job_args["url"]).to eq("http://avatar.example.com/avatar.png")
expect(job_args['user_id']).to eq(user.id) expect(job_args["user_id"]).to eq(user.id)
expect(job_args['override_gravatar']).to eq(false) expect(job_args["override_gravatar"]).to eq(false)
end end
end end
end end
it 'can walk json' do it "can walk json" do
authenticator = OAuth2BasicAuthenticator.new authenticator = OAuth2BasicAuthenticator.new
json_string = '{"user":{"id":1234,"email":{"address":"test@example.com"}}}' json_string = '{"user":{"id":1234,"email":{"address":"test@example.com"}}}'
SiteSetting.oauth2_json_email_path = 'user.email.address' SiteSetting.oauth2_json_email_path = "user.email.address"
result = authenticator.json_walk({}, JSON.parse(json_string), :email) result = authenticator.json_walk({}, JSON.parse(json_string), :email)
expect(result).to eq "test@example.com" expect(result).to eq "test@example.com"
end end
it 'allows keys containing dots, if wrapped in quotes' do it "allows keys containing dots, if wrapped in quotes" do
authenticator = OAuth2BasicAuthenticator.new authenticator = OAuth2BasicAuthenticator.new
json_string = '{"www.example.com/uid": "myuid"}' json_string = '{"www.example.com/uid": "myuid"}'
SiteSetting.oauth2_json_user_id_path = '"www.example.com/uid"' SiteSetting.oauth2_json_user_id_path = '"www.example.com/uid"'
@ -199,7 +204,7 @@ describe OAuth2BasicAuthenticator do
expect(result).to eq "myuid" expect(result).to eq "myuid"
end end
it 'allows keys containing dots, if escaped' do it "allows keys containing dots, if escaped" do
authenticator = OAuth2BasicAuthenticator.new authenticator = OAuth2BasicAuthenticator.new
json_string = '{"www.example.com/uid": "myuid"}' json_string = '{"www.example.com/uid": "myuid"}'
SiteSetting.oauth2_json_user_id_path = 'www\.example\.com/uid' SiteSetting.oauth2_json_user_id_path = 'www\.example\.com/uid'
@ -208,7 +213,7 @@ describe OAuth2BasicAuthenticator do
expect(result).to eq "myuid" expect(result).to eq "myuid"
end end
it 'allows keys containing literal backslashes, if escaped' do it "allows keys containing literal backslashes, if escaped" do
authenticator = OAuth2BasicAuthenticator.new authenticator = OAuth2BasicAuthenticator.new
# This 'single quoted heredoc' syntax means we don't have to escape backslashes in Ruby # This 'single quoted heredoc' syntax means we don't have to escape backslashes in Ruby
# What you see is exactly what the user would enter in the site settings # What you see is exactly what the user would enter in the site settings
@ -222,107 +227,109 @@ describe OAuth2BasicAuthenticator do
expect(result).to eq "myuid" expect(result).to eq "myuid"
end end
it 'can walk json that contains an array' do it "can walk json that contains an array" do
authenticator = OAuth2BasicAuthenticator.new authenticator = OAuth2BasicAuthenticator.new
json_string = '{"email":"test@example.com","identities":[{"user_id":"123456789","provider":"auth0","isSocial":false}]}' json_string =
SiteSetting.oauth2_json_user_id_path = 'identities.[].user_id' '{"email":"test@example.com","identities":[{"user_id":"123456789","provider":"auth0","isSocial":false}]}'
SiteSetting.oauth2_json_user_id_path = "identities.[].user_id"
result = authenticator.json_walk({}, JSON.parse(json_string), :user_id) result = authenticator.json_walk({}, JSON.parse(json_string), :user_id)
expect(result).to eq "123456789" expect(result).to eq "123456789"
end end
it 'can walk json and handle an empty array' do it "can walk json and handle an empty array" do
authenticator = OAuth2BasicAuthenticator.new authenticator = OAuth2BasicAuthenticator.new
json_string = '{"email":"test@example.com","identities":[]}' json_string = '{"email":"test@example.com","identities":[]}'
SiteSetting.oauth2_json_user_id_path = 'identities.[].user_id' SiteSetting.oauth2_json_user_id_path = "identities.[].user_id"
result = authenticator.json_walk({}, JSON.parse(json_string), :user_id) result = authenticator.json_walk({}, JSON.parse(json_string), :user_id)
expect(result).to eq nil expect(result).to eq nil
end end
it 'can walk json and find values by index in an array' do it "can walk json and find values by index in an array" do
authenticator = OAuth2BasicAuthenticator.new authenticator = OAuth2BasicAuthenticator.new
json_string = '{"emails":[{"value":"test@example.com"},{"value":"test2@example.com"}]}' json_string = '{"emails":[{"value":"test@example.com"},{"value":"test2@example.com"}]}'
SiteSetting.oauth2_json_email_path = 'emails[1].value' SiteSetting.oauth2_json_email_path = "emails[1].value"
result = authenticator.json_walk({}, JSON.parse(json_string), :email) result = authenticator.json_walk({}, JSON.parse(json_string), :email)
expect(result).to eq "test2@example.com" expect(result).to eq "test2@example.com"
end end
it 'can walk json and download avatar' do it "can walk json and download avatar" do
authenticator = OAuth2BasicAuthenticator.new authenticator = OAuth2BasicAuthenticator.new
json_string = '{"user":{"avatar":"http://example.com/1.png"}}' json_string = '{"user":{"avatar":"http://example.com/1.png"}}'
SiteSetting.oauth2_json_avatar_path = 'user.avatar' SiteSetting.oauth2_json_avatar_path = "user.avatar"
result = authenticator.json_walk({}, JSON.parse(json_string), :avatar) result = authenticator.json_walk({}, JSON.parse(json_string), :avatar)
expect(result).to eq 'http://example.com/1.png' expect(result).to eq "http://example.com/1.png"
end end
describe 'token_callback' do describe "token_callback" do
let(:user) { Fabricate(:user) } let(:user) { Fabricate(:user) }
let(:strategy) { OmniAuth::Strategies::Oauth2Basic.new({}) } let(:strategy) { OmniAuth::Strategies::Oauth2Basic.new({}) }
let(:authenticator) { OAuth2BasicAuthenticator.new } let(:authenticator) { OAuth2BasicAuthenticator.new }
let(:auth) do let(:auth) do
OmniAuth::AuthHash.new( OmniAuth::AuthHash.new(
'provider' => 'oauth2_basic', "provider" => "oauth2_basic",
'credentials' => { "credentials" => {
'token' => 'token' "token" => "token",
}, },
'uid' => 'e028b1b918853eca7fba208a9d7e9d29a6e93c57', "uid" => "e028b1b918853eca7fba208a9d7e9d29a6e93c57",
'info' => { "info" => {
"name" => 'Sammy the Shark', "name" => "Sammy the Shark",
"email" => 'sammy@digitalocean.com' "email" => "sammy@digitalocean.com",
},
"extra" => {
}, },
'extra' => {}
) )
end end
let(:access_token) do let(:access_token) do
{ "params" => {
{ "info" => "params" => {
{ "info" => {
"name" => "Sammy the Shark", "name" => "Sammy the Shark",
"email" => "sammy@digitalocean.com", "email" => "sammy@digitalocean.com",
"uuid" => "e028b1b918853eca7fba208a9d7e9d29a6e93c57" "uuid" => "e028b1b918853eca7fba208a9d7e9d29a6e93c57",
} },
} },
} }
end end
before(:each) do before(:each) do
SiteSetting.oauth2_callback_user_id_path = 'params.info.uuid' SiteSetting.oauth2_callback_user_id_path = "params.info.uuid"
SiteSetting.oauth2_callback_user_info_paths = 'name:params.info.name|email:params.info.email' SiteSetting.oauth2_callback_user_info_paths = "name:params.info.name|email:params.info.email"
end end
it 'can retrieve user id from access token callback' do it "can retrieve user id from access token callback" do
strategy.stubs(:access_token).returns(access_token) strategy.stubs(:access_token).returns(access_token)
expect(strategy.uid).to eq 'e028b1b918853eca7fba208a9d7e9d29a6e93c57' expect(strategy.uid).to eq "e028b1b918853eca7fba208a9d7e9d29a6e93c57"
end end
it 'can retrieve user properties from access token callback' do it "can retrieve user properties from access token callback" do
strategy.stubs(:access_token).returns(access_token) strategy.stubs(:access_token).returns(access_token)
expect(strategy.info['name']).to eq 'Sammy the Shark' expect(strategy.info["name"]).to eq "Sammy the Shark"
expect(strategy.info['email']).to eq 'sammy@digitalocean.com' expect(strategy.info["email"]).to eq "sammy@digitalocean.com"
end end
it 'does apply user properties from access token callback in after_authenticate' do it "does apply user properties from access token callback in after_authenticate" do
SiteSetting.oauth2_fetch_user_details = true SiteSetting.oauth2_fetch_user_details = true
authenticator.stubs(:fetch_user_details).returns(email: 'sammy@digitalocean.com') authenticator.stubs(:fetch_user_details).returns(email: "sammy@digitalocean.com")
result = authenticator.after_authenticate(auth) result = authenticator.after_authenticate(auth)
expect(result.extra_data[:uid]).to eq 'e028b1b918853eca7fba208a9d7e9d29a6e93c57' expect(result.extra_data[:uid]).to eq "e028b1b918853eca7fba208a9d7e9d29a6e93c57"
expect(result.name).to eq 'Sammy the Shark' expect(result.name).to eq "Sammy the Shark"
expect(result.email).to eq 'sammy@digitalocean.com' expect(result.email).to eq "sammy@digitalocean.com"
end end
it 'does work if user details are not fetched' do it "does work if user details are not fetched" do
SiteSetting.oauth2_fetch_user_details = false SiteSetting.oauth2_fetch_user_details = false
result = authenticator.after_authenticate(auth) result = authenticator.after_authenticate(auth)
expect(result.extra_data[:uid]).to eq 'e028b1b918853eca7fba208a9d7e9d29a6e93c57' expect(result.extra_data[:uid]).to eq "e028b1b918853eca7fba208a9d7e9d29a6e93c57"
expect(result.name).to eq 'Sammy the Shark' expect(result.name).to eq "Sammy the Shark"
expect(result.email).to eq 'sammy@digitalocean.com' expect(result.email).to eq "sammy@digitalocean.com"
end end
end end
end end