From 895db120e3e3f58354fd87e425e21c4066e3c07f Mon Sep 17 00:00:00 2001 From: Natalie Tay Date: Wed, 31 Jan 2024 20:19:18 +0800 Subject: [PATCH] FEATURE: Allow specifiying required paths when retrieving userinfo (#96) When retrieving userinfo with oauth2_fetch_user_details=true, we sometimes want to disallow account creation if an attribute is not fulfilled. This commit adds a new register oauth2_basic_required_json_path which will kick in when retrieving userinfo. --- plugin.rb | 21 ++++++++++++++++++++ spec/plugin_spec.rb | 48 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/plugin.rb b/plugin.rb index a7278d0..215b888 100644 --- a/plugin.rb +++ b/plugin.rb @@ -78,6 +78,18 @@ end # We'll store the value in the user associated account's extra attribute hash using the full path as the key. DiscoursePluginRegistry.define_filtered_register :oauth2_basic_additional_json_paths +# After authentication, we'll use this to confirm that the registered json paths are fulfilled, or display an error. +# This requires SiteSetting.oauth2_fetch_user_details? to be true, and can be used with +# DiscoursePluginRegistry.oauth2_basic_additional_json_paths. +# +# Example usage: +# DiscoursePluginRegistry.register_oauth2_basic_required_json_path({ +# path: "extra:data.is_allowed_user", +# required_value: true, +# error_message: I18n.t("auth.user_not_allowed") +# }, self) +DiscoursePluginRegistry.define_filtered_register :oauth2_basic_required_json_paths + class ::OAuth2BasicAuthenticator < Auth::ManagedAuthenticator def name "oauth2_basic" @@ -312,6 +324,15 @@ class ::OAuth2BasicAuthenticator < Auth::ManagedAuthenticator 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 diff --git a/spec/plugin_spec.rb b/spec/plugin_spec.rb index ebd55a8..29bb165 100644 --- a/spec/plugin_spec.rb +++ b/spec/plugin_spec.rb @@ -124,6 +124,54 @@ describe OAuth2BasicAuthenticator do expect(associated_account.extra[custom_path]).to eq("received") end end + + describe "required attributes" do + after { DiscoursePluginRegistry.reset_register!(:oauth2_basic_required_json_paths) } + + it "'authenticates' successfully if required json path is fulfilled" do + DiscoursePluginRegistry.register_oauth2_basic_additional_json_path( + "account.is_legit", + Plugin::Instance.new, + ) + DiscoursePluginRegistry.register_oauth2_basic_required_json_path( + { path: "extra:account.is_legit", required_value: true }, + Plugin::Instance.new, + ) + + response = { + status: 200, + body: '{"account":{"email":"newemail@example.com","is_legit":true}}', + } + stub_request(:get, SiteSetting.oauth2_user_json_url).to_return(response) + + result = authenticator.after_authenticate(auth) + expect(result.failed).to eq(false) + end + + it "fails 'authentication' if required json path is unfulfilled" do + DiscoursePluginRegistry.register_oauth2_basic_additional_json_path( + "account.is_legit", + Plugin::Instance.new, + ) + DiscoursePluginRegistry.register_oauth2_basic_required_json_path( + { + path: "extra:account.is_legit", + required_value: true, + error_message: "You're not legit", + }, + Plugin::Instance.new, + ) + response = { + status: 200, + body: '{"account":{"email":"newemail@example.com","is_legit":false}}', + } + stub_request(:get, SiteSetting.oauth2_user_json_url).to_return(response) + + result = authenticator.after_authenticate(auth) + expect(result.failed).to eq(true) + expect(result.failed_reason).to eq("You're not legit") + end + end end describe "avatar downloading" do