summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorClaire <claire.github-309c@sitedethib.com>2024-02-14 15:16:07 +0100
committerGitHub <noreply@github.com>2024-02-14 15:16:07 +0100
commit6f36b633a7545a2cbbe5f28dc5c8e512aeb98ea9 (patch)
tree1ea6f95c68f0d648b96c556bce3f2adf260f7dd2
parentd807b3960e96dc29669b7767cea1246ac68d508d (diff)
Merge pull request from GHSA-vm39-j3vx-pch3
* Prevent different identities from a same SSO provider from accessing a same account * Lock auth provider changes behind `ALLOW_UNSAFE_AUTH_PROVIDER_REATTACH=true` * Rename methods to avoid confusion between OAuth and OmniAuth
-rw-r--r--app/controllers/auth/omniauth_callbacks_controller.rb2
-rw-r--r--app/models/concerns/omniauthable.rb52
-rw-r--r--app/models/identity.rb2
-rw-r--r--spec/models/identity_spec.rb6
4 files changed, 43 insertions, 19 deletions
diff --git a/app/controllers/auth/omniauth_callbacks_controller.rb b/app/controllers/auth/omniauth_callbacks_controller.rb
index 3d7962de56c..3c4984b3f38 100644
--- a/app/controllers/auth/omniauth_callbacks_controller.rb
+++ b/app/controllers/auth/omniauth_callbacks_controller.rb
@@ -5,7 +5,7 @@ class Auth::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def self.provides_callback_for(provider)
define_method provider do
- @user = User.find_for_oauth(request.env['omniauth.auth'], current_user)
+ @user = User.find_for_omniauth(request.env['omniauth.auth'], current_user)
if @user.persisted?
LoginActivity.create(
diff --git a/app/models/concerns/omniauthable.rb b/app/models/concerns/omniauthable.rb
index 7d54e9d6de0..df6b207607f 100644
--- a/app/models/concerns/omniauthable.rb
+++ b/app/models/concerns/omniauthable.rb
@@ -19,17 +19,18 @@ module Omniauthable
end
class_methods do
- def find_for_oauth(auth, signed_in_resource = nil)
+ def find_for_omniauth(auth, signed_in_resource = nil)
# EOLE-SSO Patch
auth.uid = (auth.uid[0][:uid] || auth.uid[0][:user]) if auth.uid.is_a? Hashie::Array
- identity = Identity.find_for_oauth(auth)
+ identity = Identity.find_for_omniauth(auth)
# If a signed_in_resource is provided it always overrides the existing user
# to prevent the identity being locked with accidentally created accounts.
# Note that this may leave zombie accounts (with no associated identity) which
# can be cleaned up at a later date.
user = signed_in_resource || identity.user
- user ||= create_for_oauth(auth)
+ user ||= reattach_for_auth(auth)
+ user ||= create_for_auth(auth)
if identity.user.nil?
identity.user = user
@@ -39,19 +40,35 @@ module Omniauthable
user
end
- def create_for_oauth(auth)
- # Check if the user exists with provided email. If no email was provided,
- # we assign a temporary email and ask the user to verify it on
- # the next step via Auth::SetupController.show
+ private
- strategy = Devise.omniauth_configs[auth.provider.to_sym].strategy
- assume_verified = strategy&.security&.assume_email_is_verified
- email_is_verified = auth.info.verified || auth.info.verified_email || auth.info.email_verified || assume_verified
- email = auth.info.verified_email || auth.info.email
+ def reattach_for_auth(auth)
+ # If allowed, check if a user exists with the provided email address,
+ # and return it if they does not have an associated identity with the
+ # current authentication provider.
+
+ # This can be used to provide a choice of alternative auth providers
+ # or provide smooth gradual transition between multiple auth providers,
+ # but this is discouraged because any insecure provider will put *all*
+ # local users at risk, regardless of which provider they registered with.
+
+ return unless ENV['ALLOW_UNSAFE_AUTH_PROVIDER_REATTACH'] == 'true'
- user = User.find_by(email: email) if email_is_verified
+ email, email_is_verified = email_from_auth(auth)
+ return unless email_is_verified
- return user unless user.nil?
+ user = User.find_by(email: email)
+ return if user.nil? || Identity.exists?(provider: auth.provider, user_id: user.id)
+
+ user
+ end
+
+ def create_for_auth(auth)
+ # Create a user for the given auth params. If no email was provided,
+ # we assign a temporary email and ask the user to verify it on
+ # the next step via Auth::SetupController.show
+
+ email, email_is_verified = email_from_auth(auth)
user = User.new(user_params_from_auth(email, auth))
@@ -68,7 +85,14 @@ module Omniauthable
user
end
- private
+ def email_from_auth(auth)
+ strategy = Devise.omniauth_configs[auth.provider.to_sym].strategy
+ assume_verified = strategy&.security&.assume_email_is_verified
+ email_is_verified = auth.info.verified || auth.info.verified_email || auth.info.email_verified || assume_verified
+ email = auth.info.verified_email || auth.info.email
+
+ [email, email_is_verified]
+ end
def user_params_from_auth(email, auth)
{
diff --git a/app/models/identity.rb b/app/models/identity.rb
index 869f06372da..a396d762343 100644
--- a/app/models/identity.rb
+++ b/app/models/identity.rb
@@ -16,7 +16,7 @@ class Identity < ApplicationRecord
validates :uid, presence: true, uniqueness: { scope: :provider }
validates :provider, presence: true
- def self.find_for_oauth(auth)
+ def self.find_for_omniauth(auth)
find_or_create_by(uid: auth.uid, provider: auth.provider)
end
end
diff --git a/spec/models/identity_spec.rb b/spec/models/identity_spec.rb
index 689c9b797f4..081c254d820 100644
--- a/spec/models/identity_spec.rb
+++ b/spec/models/identity_spec.rb
@@ -1,16 +1,16 @@
require 'rails_helper'
RSpec.describe Identity, type: :model do
- describe '.find_for_oauth' do
+ describe '.find_for_omniauth' do
let(:auth) { Fabricate(:identity, user: Fabricate(:user)) }
it 'calls .find_or_create_by' do
expect(described_class).to receive(:find_or_create_by).with(uid: auth.uid, provider: auth.provider)
- described_class.find_for_oauth(auth)
+ described_class.find_for_omniauth(auth)
end
it 'returns an instance of Identity' do
- expect(described_class.find_for_oauth(auth)).to be_instance_of Identity
+ expect(described_class.find_for_omniauth(auth)).to be_instance_of Identity
end
end
end