diff --git a/app/controllers/auth/omniauth_callbacks_controller.rb b/app/controllers/auth/omniauth_callbacks_controller.rb
index d0112254e2..0507db299c 100644
--- a/app/controllers/auth/omniauth_callbacks_controller.rb
+++ b/app/controllers/auth/omniauth_callbacks_controller.rb
@@ -128,13 +128,10 @@ def try_login!
# If no identity exist, we want to check if it is a new user or an existing user using a new provider
# Try to find an existing user
user = find_user_in_institution
- # If we found an existing user, which already has an identity for this provider
- # This will require a manual intervention by the development team, notify the user and the team
- return redirect_duplicate_email_for_provider! if user&.providers&.exists?(id: provider.id)
# If we found an existing user with the same username or email within the same institution
# We will ask the user to verify if this was the user they wanted to sign in to
# if yes => redirect to a previously used provider for this user
- # if no => contact dodona for a manual intervention
+ # if no => create a new user or contact support
return redirect_to_known_provider!(user) if user.present?
# Try to find if the email address is already in use in an other institution
@@ -373,6 +370,9 @@ def redirect_with_flash!(message)
end
def redirect_to_known_provider!(user)
+ # information required if the user wants to create a new account
+ store_hash_in_session!
+ # information required if the user wants to link the new sign in method to an existing account
store_identity_in_session!
session[:auth_original_user_id] = user.id
@provider = provider
@@ -386,17 +386,6 @@ def redirect_to_confirm_new_user!
redirect_to confirm_new_user_path
end
- def redirect_duplicate_email_for_provider!
- ApplicationMailer.with(
- authinfo: auth_hash,
- errors: I18n.t('devise.omniauth_callbacks.duplicate_email_for_provider', email_address: auth_email, provider: provider.class.sym.to_s)
- ).user_unable_to_log_in.deliver_later
-
- set_flash_message :alert, :duplicate_email_for_provider, email_address: auth_email, provider: provider.class.sym.to_s
- flash[:options] = [{ url: contact_path, message: I18n.t('pages.contact.prompt') }]
- redirect_to root_path
- end
-
def flash_wrong_provider(tried_provider, user_provider)
set_flash_message :alert, :wrong_provider,
tried_email_address: auth_email,
diff --git a/app/models/user.rb b/app/models/user.rb
index 463fcc757f..2eebbfda38 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -120,8 +120,6 @@ class User < ApplicationRecord
devise :omniauthable, omniauth_providers: %i[google_oauth2 lti office365 oidc saml smartschool surf elixir]
- validates :username, uniqueness: { case_sensitive: false, allow_blank: true, scope: :institution }
- validates :email, uniqueness: { case_sensitive: false, allow_blank: true, scope: :institution }
validate :max_one_institution
validate :provider_allows_blank_email
diff --git a/app/views/auth/redirect_to_known_provider.html.erb b/app/views/auth/redirect_to_known_provider.html.erb
index c35b907f20..aa442569b7 100644
--- a/app/views/auth/redirect_to_known_provider.html.erb
+++ b/app/views/auth/redirect_to_known_provider.html.erb
@@ -16,6 +16,13 @@
<% end %>
+
<%= t ".new_account" %>
+
<%= t ".new_account_help"%>
+
+ <%= link_to t(".create_new_account"), confirm_new_user_path, method: :post, class: "btn btn-filled" %>
+
+
+
<%= t ".contact_support_html", form: link_to(t(".contact_form"), contact_path)%>
diff --git a/config/locales/views/auth/en.yml b/config/locales/views/auth/en.yml
index 5b13a5de71..06ccc8d3d5 100644
--- a/config/locales/views/auth/en.yml
+++ b/config/locales/views/auth/en.yml
@@ -22,6 +22,9 @@ en:
message_p1_personal: "A user with the same username (**%{username}**) or email address (**%{email}**) already exists on Dodona. This is probably you, but you have used a different sign in method on previous occasions."
message_p2: "Before you can access your data using **%{provider}**, we have to link your existing account. To proceed, click on one of the sign in methods below and sign in again."
providers_title: "Link accounts"
+ new_account: "Create new account"
+ new_account_help: "If the existing account is not yours, or if you want to create a new account, click on the button below."
+ create_new_account: "Create new account"
"contact_support_html": "If this isn't you or you keep having issues? Fill in the %{form} and we will assist you as soon as possible."
contact_form: "contact form"
privacy_prompt:
diff --git a/config/locales/views/auth/nl.yml b/config/locales/views/auth/nl.yml
index 31ed872a82..daa0aacd09 100644
--- a/config/locales/views/auth/nl.yml
+++ b/config/locales/views/auth/nl.yml
@@ -22,6 +22,9 @@ nl:
message_p1_personal: "Er bestaat al een gebruiker met dezelfde gebruikersnaam (**%{username}**) of hetzelfde e-mailadres (**%{email}**) op Dodona. Waarschijnlijk ben jij dit, maar gebruikte je vorige keer een andere loginmethode."
message_p2: "Om ook via **%{provider}** aan je gegevens te kunnen, moeten we even je bestaande account koppelen. Klik daarvoor op de aanmeldknop hieronder en log nogmaals in."
providers_title: "Accounts koppelen"
+ new_account: "Nieuwe account aanmaken"
+ new_account_help: "Als het bestaande account niet van jou is, of als je een nieuwe account wilt aanmaken, klik dan op de knop hieronder."
+ create_new_account: "Maak een nieuwe account"
"contact_support_html": "Ben jij dit niet of blijf je problemen hebben? Vul het %{form} in en we helpen je zo snel mogelijk verder."
contact_form: "contactformulier"
privacy_prompt:
diff --git a/db/migrate/20240911085152_remove_email_uniqueness.rb b/db/migrate/20240911085152_remove_email_uniqueness.rb
new file mode 100644
index 0000000000..9d26df3fdb
--- /dev/null
+++ b/db/migrate/20240911085152_remove_email_uniqueness.rb
@@ -0,0 +1,6 @@
+class RemoveEmailUniqueness < ActiveRecord::Migration[7.2]
+ def change
+ remove_index :users, name: "index_users_on_email_and_institution_id"
+ remove_index :users, name: "index_users_on_username_and_institution_id"
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 3eacf5b77b..1afaa8229c 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[7.1].define(version: 2024_06_26_093130) do
+ActiveRecord::Schema[7.2].define(version: 2024_09_11_085152) do
create_table "active_storage_attachments", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t|
t.string "name", null: false
t.string "record_type", null: false
@@ -529,10 +529,8 @@
t.datetime "sign_in_at", precision: nil
t.integer "open_questions_count", default: 0, null: false
t.integer "theme", default: 0, null: false
- t.index ["email", "institution_id"], name: "index_users_on_email_and_institution_id", unique: true
t.index ["institution_id"], name: "index_users_on_institution_id"
t.index ["token"], name: "index_users_on_token"
- t.index ["username", "institution_id"], name: "index_users_on_username_and_institution_id", unique: true
t.index ["username"], name: "index_users_on_username"
end
diff --git a/test/controllers/auth/omniauth_callbacks_controller_test.rb b/test/controllers/auth/omniauth_callbacks_controller_test.rb
index 5caa8ff223..c38e06180c 100644
--- a/test/controllers/auth/omniauth_callbacks_controller_test.rb
+++ b/test/controllers/auth/omniauth_callbacks_controller_test.rb
@@ -252,16 +252,15 @@ def omniauth_path(provider)
end
test 'update attributes should be checked for validity' do
- OAUTH_PROVIDERS.each do |provider_name|
+ EMAIL_REQUIRED_PROVIDERS.each do |provider_name|
# Setup.
provider = create provider_name
user = create :user, institution: provider.institution
- other_user = create :user, institution: provider.institution
identity = create :identity, provider: provider, user: user
omniauth_mock_identity identity,
info: {
- email: other_user.email
+ email: nil
}
assert_emails 1 do
@@ -275,7 +274,7 @@ def omniauth_path(provider)
assert_nil @controller.current_user
user.reload
- assert_not_equal other_user.email, user.email
+ assert_not_nil user.email
# Cleanup.
sign_out user
@@ -499,9 +498,8 @@ def omniauth_path(provider)
post omniauth_url(provider)
follow_redirect!
- # Assert successful authentication.
- assert_redirected_to root_path
- assert_not_equal @controller.current_user, user
+ # assert not signed in because of double account (first has to confirm new account)
+ assert_nil @controller.current_user
identity.reload
assert_equal('NEW-UID', identity.identifier)
@@ -608,8 +606,8 @@ def omniauth_path(provider)
post omniauth_url(provider)
follow_redirect!
- # Assert successful authentication.
- assert_redirected_to root_path
+ # assert not signed in because of double account (first has to confirm new account)
+ assert_nil @controller.current_user
assert_not_equal @controller.current_user, user
identity.reload
@@ -827,4 +825,47 @@ def omniauth_path(provider)
# Done.
sign_out user
end
+
+ test 'can create new user with same email and username as existing user' do
+ institution = create :institution
+ user = create :user, institution: institution, identities: []
+ first_provider = create :provider, institution: institution, mode: :prefer, identities: []
+ second_provider = create :provider, institution: institution, mode: :secondary, identities: []
+
+ # Link user to first provider.
+ create :identity, provider: first_provider, user: user
+
+ # Build, but don't save the identity for the second provider.
+ # This allows us to log in with the second provider for the 'first' time.
+ second_identity = build :identity, provider: second_provider, user: user
+ omniauth_mock_identity second_identity
+
+ # Simulate the user logging in with the second provider.
+ # It should not create a user.
+ assert_difference 'User.count', 0 do
+ post omniauth_url(second_provider)
+ follow_redirect!
+ end
+
+ # It should render the page where the user can choose.
+ assert_response :success
+
+ # It is actually the page we expect.
+ assert_select 'h1', t('auth.redirect_to_known_provider.title')
+ # There should be a button to create a new user
+ assert_select 'a', t('auth.redirect_to_known_provider.create_new_account')
+
+ # Confirming the new user should create a new user
+ assert_difference 'User.count', 1 do
+ post confirm_new_user_path
+ follow_redirect!
+ end
+
+ # After confirm a new user is created
+ assert_equal @controller.current_user.username, user.username
+ assert_equal @controller.current_user.email, user.email
+ assert_not_equal @controller.current_user.id, user.id
+
+ sign_out @controller.current_user
+ end
end
diff --git a/test/testhelpers/constants.rb b/test/testhelpers/constants.rb
index e4ee28e83a..0da19eace5 100644
--- a/test/testhelpers/constants.rb
+++ b/test/testhelpers/constants.rb
@@ -1,4 +1,5 @@
module Constants
OAUTH_PROVIDERS = %i[gsuite_provider office365_provider smartschool_provider].freeze
AUTH_PROVIDERS = %i[lti_provider saml_provider].concat(OAUTH_PROVIDERS).freeze
+ EMAIL_REQUIRED_PROVIDERS = AUTH_PROVIDERS - %i[lti_provider smartschool_provider].freeze
end