Skip to content

Commit 1840b7f

Browse files
authored
Wire identities into auth and tests
1 parent 36a9566 commit 1840b7f

7 files changed

Lines changed: 68 additions & 8 deletions

File tree

app/controllers/callbacks_controller.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def handle_callback(provider, config)
4545
end
4646
else
4747
scope = Devise::Mapping.find_scope!(@user)
48-
sign_in(scope, resource, {})
48+
sign_in(scope, @user, {})
4949
redirect_to_space(after_sign_in_path_for(@user), space)
5050
end
5151
end

app/controllers/identities_controller.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ def set_user
2727
end
2828

2929
def authorize_user
30-
handle_error(:forbidden) && return unless current_user == @user
30+
return if current_user == @user
31+
32+
handle_error(:forbidden)
3133
end
3234

3335
def set_breadcrumbs

app/models/user.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ def self.from_omniauth(auth)
207207

208208
if user.nil? && auth.info.email.present? && auth.provider.present?
209209
user = User.joins(:identities).where(email: auth.info.email, identities: { provider: auth.provider }).first
210+
user ||= User.where(provider: auth.provider, email: auth.info.email).first
210211
end
211212

212213
if user
@@ -346,6 +347,14 @@ def merge(*others)
346347
new_collaborations = []
347348
other_profiles = others.map(&:profile)
348349
others.each do |other|
350+
other.identities.each do |identity|
351+
existing_identity = identities.find_by(provider: identity.provider, uid: identity.uid)
352+
if existing_identity
353+
identity.destroy
354+
else
355+
identity.update!(user: self)
356+
end
357+
end
349358
other.reassign_resources(self)
350359
other.activities_as_owner.update_all(owner_id: id, owner_type: self.class.name)
351360
other.activities.update_all(trackable_id: id, trackable_type: self.class.name)

app/views/users/show.html.erb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@
9393
<% if TeSS::Config.feature['registration'] %>
9494
<% if !current_user.nil? and current_user == @user %>
9595
<%= link_to "Manage account", edit_user_registration_path, :class => 'btn btn-default center-block' %>
96-
<%= link_to "Manage identities", user_identities_path(@user), :class => 'btn btn-default center-block mt-2' %>
96+
<%= link_to "Manage identities", user_identities_path(@user), :class => 'btn btn-default center-block' %>
9797
<% end %>
9898
<% end %>
9999
</div>

db/schema.rb

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#
1111
# It's strongly recommended that you check this file into your version control system.
1212

13-
ActiveRecord::Schema[8.1].define(version: 2026_04_21_144919) do
13+
ActiveRecord::Schema[8.1].define(version: 2026_07_23_102000) do
1414
# These are extensions that must be enabled in order to support this database
1515
enable_extension "pg_catalog.plpgsql"
1616

@@ -268,6 +268,16 @@
268268
t.index ["sluggable_type"], name: "index_friendly_id_slugs_on_sluggable_type"
269269
end
270270

271+
create_table "identities", force: :cascade do |t|
272+
t.bigint "user_id", null: false
273+
t.string "provider", null: false
274+
t.string "uid"
275+
t.datetime "created_at", null: false
276+
t.datetime "updated_at", null: false
277+
t.index ["provider", "uid"], name: "index_identities_on_provider_and_uid", unique: true
278+
t.index ["user_id"], name: "index_identities_on_user_id"
279+
end
280+
271281
create_table "learning_path_topic_items", force: :cascade do |t|
272282
t.text "comment"
273283
t.datetime "created_at", null: false
@@ -687,6 +697,7 @@
687697
add_foreign_key "event_materials", "materials"
688698
add_foreign_key "events", "spaces"
689699
add_foreign_key "events", "users"
700+
add_foreign_key "identities", "users"
690701
add_foreign_key "learning_path_topic_links", "learning_paths"
691702
add_foreign_key "learning_path_topics", "spaces"
692703
add_foreign_key "learning_paths", "content_providers"

test/integration/omniauth_test.rb

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -225,9 +225,9 @@ class OmniauthTest < ActionDispatch::IntegrationTest
225225
# check user created
226226
new_user = User.find_by_username(expected_username)
227227
assert !new_user.nil?, "new username[#{expected_username}] not created successfully"
228-
assert_equal 'oidc2', new_user.provider
229-
assert !new_user.uid.nil?
230-
assert_equal new_uid, new_user.uid
228+
identity = new_user.identities.find_by(provider: 'oidc2')
229+
refute_nil identity
230+
assert_equal new_uid, identity.uid
231231
assert_equal new_email, new_user.email
232232
end
233233

@@ -399,6 +399,28 @@ class OmniauthTest < ActionDispatch::IntegrationTest
399399
assert_response :not_found
400400
end
401401

402+
test 'logged-in users can link additional identities' do
403+
user = users(:regular_user)
404+
post '/users/sign_in', params: { 'user[login]' => user.username, 'user[password]' => 'hello' }
405+
406+
OmniAuth.config.mock_auth[:oidc2] = OmniAuth::AuthHash.new(
407+
{
408+
provider: 'oidc2',
409+
uid: 'oidc2-linked-uid',
410+
info: {
411+
email: user.email,
412+
nickname: user.username
413+
}
414+
})
415+
416+
post user_oidc2_omniauth_authorize_url(link_identity: '1')
417+
follow_redirect! # OmniAuth redirect
418+
follow_redirect! # CallbacksController identities redirect
419+
420+
assert_equal user_identities_path(user), path
421+
assert user.reload.identities.where(provider: 'oidc2', uid: 'oidc2-linked-uid').any?
422+
end
423+
402424
test 'authentication redirects users back to origin space on subdomain' do
403425
space = spaces(:astro)
404426
space.update!(host: 'space.example.com')

test/models/user_test.rb

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,13 +92,29 @@ class UserTest < ActiveSupport::TestCase
9292
end
9393

9494
test "should save with nil password if using omniauth" do
95-
user = User.new(@user_params.merge(password: nil, provider: 'elixir_aai', uid: 'abcdefg'))
95+
user = User.new(@user_params.merge(password: nil))
96+
user.identities.build(provider: 'elixir_aai', uid: 'abcdefg')
9697
refute user.password_required?
9798
assert user.using_omniauth?
9899
assert user.save
99100
assert user.reload.encrypted_password.blank?
100101
end
101102

103+
test 'should create identity from omniauth details when creating new user' do
104+
auth = OpenStruct.new(
105+
provider: 'oidc',
106+
uid: 'abc123',
107+
info: OpenStruct.new(email: 'new-aaf-user@example.com', nickname: 'new_aaf_user')
108+
)
109+
110+
user = User.from_omniauth(auth)
111+
assert user.save
112+
113+
identity = user.identities.find_by(provider: 'oidc')
114+
refute_nil identity
115+
assert_equal 'abc123', identity.uid
116+
end
117+
102118
test "should not save with password under 8 characters" do
103119
user = User.new(@user_params.merge(password: '1234567'))
104120
assert_not user.save, 'Allowed a user to have a password under 8 characters'

0 commit comments

Comments
 (0)