diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 693970d14a4..e524c232d9a 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -502,7 +502,14 @@ class ApplicationController < ActionController::Base result.find_by(find_opts) elsif params[:external_id] external_id = params[:external_id].chomp('.json') - SingleSignOnRecord.find_by(external_id: external_id).try(:user) + if provider_name = params[:external_provider] + raise Discourse::InvalidAccess unless guardian.is_admin? # external_id might be something sensitive + provider = Discourse.enabled_authenticators.find { |a| a.name == provider_name } + raise Discourse::NotFound if !provider&.is_managed? # Only managed authenticators use UserAssociatedAccount + UserAssociatedAccount.find_by(provider_name: provider_name, provider_uid: external_id)&.user + else + SingleSignOnRecord.find_by(external_id: external_id).try(:user) + end end raise Discourse::NotFound if user.blank? diff --git a/config/routes.rb b/config/routes.rb index 6787eb30528..b9df3de5516 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -492,6 +492,7 @@ Discourse::Application.routes.draw do get "#{root_path}/:username/notifications/:filter" => "users#show", constraints: { username: RouteFormat.username } delete "#{root_path}/:username" => "users#destroy", constraints: { username: RouteFormat.username } get "#{root_path}/by-external/:external_id" => "users#show", constraints: { external_id: /[^\/]+/ } + get "#{root_path}/by-external/:external_provider/:external_id" => "users#show", constraints: { external_id: /[^\/]+/ } get "#{root_path}/:username/flagged-posts" => "users#show", constraints: { username: RouteFormat.username } get "#{root_path}/:username/deleted-posts" => "users#show", constraints: { username: RouteFormat.username } get "#{root_path}/:username/topic-tracking-state" => "users#topic_tracking_state", constraints: { username: RouteFormat.username } diff --git a/lib/auth/managed_authenticator.rb b/lib/auth/managed_authenticator.rb index 8cb182094a9..b0477c5f087 100644 --- a/lib/auth/managed_authenticator.rb +++ b/lib/auth/managed_authenticator.rb @@ -1,6 +1,12 @@ # frozen_string_literal: true class Auth::ManagedAuthenticator < Auth::Authenticator + def is_managed? + # Tells core that it can safely assume this authenticator + # uses UserAssociatedAccount + true + end + def description_for_user(user) associated_account = UserAssociatedAccount.find_by(provider_name: name, user_id: user.id) return "" if associated_account.nil? diff --git a/spec/requests/users_controller_spec.rb b/spec/requests/users_controller_spec.rb index b455b0dc2f9..261bd59d793 100644 --- a/spec/requests/users_controller_spec.rb +++ b/spec/requests/users_controller_spec.rb @@ -3246,6 +3246,37 @@ describe UsersController do get "/u/by-external/99.json" expect(response).not_to be_successful end + + context "for an external provider" do + before do + sign_in(Fabricate(:admin)) + SiteSetting.enable_google_oauth2_logins = true + UserAssociatedAccount.create!(user: user, provider_uid: 'myuid', provider_name: 'google_oauth2') + end + + it "doesn't work for non-admin" do + sign_in(user) + get "/u/by-external/google_oauth2/myuid.json" + expect(response.status).to eq(403) + end + + it "can fetch the user" do + get "/u/by-external/google_oauth2/myuid.json" + expect(response.status).to eq(200) + expect(response.parsed_body["user"]["username"]).to eq(user.username) + end + + it "fails for disabled provider" do + SiteSetting.enable_google_oauth2_logins = false + get "/u/by-external/google_oauth2/myuid.json" + expect(response.status).to eq(404) + end + + it "returns 404 for missing user" do + get "/u/by-external/google_oauth2/myotheruid.json" + expect(response.status).to eq(404) + end + end end describe "include_post_count_for" do