create pseudonyms in the target account when merging temporary users

refs #6177, #6199

test plan: (only relevant with a plugin that changes the behavior of
  Pseudonym#works_for_account?; otherwise behavior should be unchanged)
  enable open registration, invite a user that already exists, but
  doesn't have a pseudonym in the account you're inviting them to.  accept
  as the existing user.  they should now have a pseudonym in the target
  account, copied from another account (preferentially the default account)

Change-Id: Ia86888524a5132387bb120df3262d7205d8e04d1
Reviewed-on: https://gerrit.instructure.com/6983
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: JT Olds <jt@instructure.com>
This commit is contained in:
Cody Cutrer 2011-11-18 15:20:09 -07:00
parent 310ebfccd4
commit 1546835901
4 changed files with 172 additions and 0 deletions

View File

@ -89,6 +89,8 @@ class CommunicationChannelsController < ApplicationController
merge_users << @current_user if @current_user && !@user.registered? && !merge_users.include?(@current_user)
User.send(:preload_associations, merge_users, { :pseudonyms => :account })
merge_users.reject! { |u| u != @current_user && u.pseudonyms.all? { |p| p.deleted? } }
# remove users that don't have a pseudonym for this account, or one can't be created
merge_users = merge_users.select { |u| u.pseudonym_for_account(@root_account, @domain_root_account) }
@merge_opportunities = []
merge_users.each do |user|
account_to_pseudonyms_hash = {}
@ -109,6 +111,9 @@ class CommunicationChannelsController < ApplicationController
cc.confirm
@enrollment.accept if @enrollment
@user.move_to_user(@current_user) if @user != @current_user
# create a new pseudonym if necessary and possible
pseudonym = @current_user.pseudonym_for_account(@root_account, @domain_root_account)
pseudonym.save! if pseudonym && pseudonym.new_record?
elsif @current_user && @current_user != @user && @enrollment && @user.registered?
if params[:transfer_enrollment].present?
cc.active? || cc.confirm

View File

@ -2125,4 +2125,35 @@ class User < ActiveRecord::Base
end
h
end
# account = the account that you want a pseudonym for
# preferred_template_account = true-ish to create a pseudonym for the specified account if one doesn't exist (and it's possible)
# pass in an actual account if you have a preference for which account the new pseudonym gets copied from
# this may not be able to find a suitable pseudonym to copy, so would still return nil
# if a pseudonym is created, it is *not* saved, and *not* added to the pseudonyms collection
def pseudonym_for_account(account, preferred_template_account = nil)
pseudonym = self.pseudonyms.detect { |p| p.active? && p.works_for_account?(account) }
if !pseudonym && preferred_template_account
# list of copyable pseudonyms
active_pseudonyms = self.pseudonyms.select { |p| p.active? && !p.password_auto_generated? && !p.account.delegated_authentication? }
templates = []
# re-arrange in the order we prefer
templates.concat active_pseudonyms.select { |p| p.account_id == preferred_template_account.id } if preferred_template_account.is_a?(Account)
templates.concat active_pseudonyms.select { |p| p.account_id == Account.site_admin.id }
templates.concat active_pseudonyms.select { |p| p.account_id == Account.default.id }
templates.concat active_pseudonyms
templates.uniq!
template = templates.detect { |template| !account.pseudonyms.find_by_unique_id(template.unique_id) }
if template
# creating this not attached to the user's pseudonyms is intentional
pseudonym = account.pseudonyms.build
pseudonym.user = self
pseudonym.unique_id = template.unique_id
pseudonym.password_salt = template.password_salt
pseudonym.crypted_password = template.crypted_password
end
end
pseudonym
end
end

View File

@ -378,6 +378,48 @@ describe CommunicationChannelsController do
response.should render_template('confirm')
assigns[:merge_opportunities].should == [[@user1, [@user1.pseudonym]]]
end
it "should not show users that can't have a pseudonym created for the correct account" do
Pseudonym.any_instance.stubs(:works_for_account?).returns(false)
@account1 = Account.create!
@account1.account_authorization_configs.create!(:auth_type => 'cas')
user_with_pseudonym(:active_all => 1, :account => @account1, :username => 'jt@instructure.com')
@account2 = Account.create!
course(:active_all => 1, :account => @account2)
user
@user.update_attribute(:workflow_state, 'creation_pending')
@cc = @user.communication_channels.create!(:path => 'jt@instructure.com')
@enrollment = @course.enroll_user(@user)
get 'confirm', :nonce => @cc.confirmation_code, :enrollment => @enrollment.uuid
response.should render_template('confirm')
assigns[:merge_opportunities].should == []
end
it "should create a pseudonym in the target account by copying an existing pseudonym when merging" do
Pseudonym.any_instance.stubs(:works_for_account?).returns(false)
user_with_pseudonym(:active_all => 1, :username => 'jt@instructure.com')
@old_user = @user
@account2 = Account.create!
course(:active_all => 1, :account => @account2)
user
@user.update_attribute(:workflow_state, 'creation_pending')
@cc = @user.communication_channels.create!(:path => 'jt@instructure.com')
@enrollment = @course.enroll_user(@user)
user_session(@old_user, @old_user.pseudonym)
get 'confirm', :nonce => @cc.confirmation_code, :enrollment => @enrollment.uuid, :confirm => 1
response.should redirect_to(course_url(@course))
@old_user.reload
@user.reload
@user.should be_deleted
@enrollment.reload
@enrollment.user.should == @old_user
@old_user.pseudonyms.length.should == 2
@old_user.pseudonyms.detect { |p| p.account == @account2 }.unique_id.should == 'jt@instructure.com'
end
end
describe "invitations" do

View File

@ -972,4 +972,98 @@ describe User do
@user1.cached_current_enrollments.should == [@enrollment]
end
end
describe "pseudonym_for_account" do
before do
@account2 = Account.create!
@account3 = Account.create!
Pseudonym.any_instance.stubs(:works_for_account?).returns(false)
Pseudonym.any_instance.stubs(:works_for_account?).with(Account.default).returns(true)
end
it "should return an active pseudonym" do
user_with_pseudonym(:active_all => 1)
@user.pseudonym_for_account(Account.default).should == @pseudonym
end
it "should return a trusted pseudonym" do
user_with_pseudonym(:active_all => 1, :account => @account2)
@user.pseudonym_for_account(Account.default).should == @pseudonym
end
it "should return nil if none work" do
user_with_pseudonym(:active_all => 1)
@user.pseudonym_for_account(@account2).should == nil
end
it "should create a copy of an existing pseudonym" do
@account1 = Account.create!
@account2 = Account.create!
@account3 = Account.create!
# from unrelated account
user_with_pseudonym(:active_all => 1, :account => @account2, :username => 'unrelated@example.com', :password => 'abcdef')
new_pseudonym = @user.pseudonym_for_account(@account1, true)
new_pseudonym.should_not be_nil
new_pseudonym.should be_new_record
new_pseudonym.unique_id.should == 'unrelated@example.com'
# from default account
@user.pseudonyms.create!(:unique_id => 'default@example.com', :password => 'abcdef', :password_confirmation => 'abcdef')
@user.pseudonyms.create!(:account => @account3, :unique_id => 'preferred@example.com', :password => 'abcdef', :password_confirmation => 'abcdef')
new_pseudonym = @user.pseudonym_for_account(@account1, true)
new_pseudonym.should_not be_nil
new_pseudonym.should be_new_record
new_pseudonym.unique_id.should == 'default@example.com'
# from site admin account
@user.pseudonyms.create!(:account => Account.site_admin, :unique_id => 'siteadmin@example.com', :password => 'abcdef', :password_confirmation => 'abcdef')
new_pseudonym = @user.pseudonym_for_account(@account1, true)
new_pseudonym.should_not be_nil
new_pseudonym.should be_new_record
new_pseudonym.unique_id.should == 'siteadmin@example.com'
# from preferred account
new_pseudonym = @user.pseudonym_for_account(@account1, @account3)
new_pseudonym.should_not be_nil
new_pseudonym.should be_new_record
new_pseudonym.unique_id.should == 'preferred@example.com'
# from unrelated account, if other options are not viable
@account1.pseudonyms.create!(:unique_id => 'preferred@example.com', :password => 'abcdef', :password_confirmation => 'abcdef')
@user.pseudonyms.detect { |p| p.account == Account.site_admin }.update_attribute(:password_auto_generated, true)
Account.default.account_authorization_configs.create!(:auth_type => 'cas')
new_pseudonym = @user.pseudonym_for_account(@account1, @account3)
new_pseudonym.should_not be_nil
new_pseudonym.should be_new_record
new_pseudonym.unique_id.should == 'unrelated@example.com'
new_pseudonym.save!
new_pseudonym.valid_password?('abcdef').should be_true
end
it "should not create a new one when there are no viable candidates" do
@account1 = Account.create!
# no pseudonyms
user
@user.pseudonym_for_account(@account1, true).should be_nil
# auto-generated password
@account2 = Account.create!
@user.pseudonyms.create!(:account => @account2, :unique_id => 'bracken@instructure.com')
@user.pseudonym_for_account(@account1, true).should be_nil
# delegated auth
@account3 = Account.create!
@account3.account_authorization_configs.create!(:auth_type => 'cas')
@account3.should be_delegated_authentication
@user.pseudonyms.create!(:account => @account3, :unique_id => 'jacob@instructure.com', :password => 'abcdef', :password_confirmation => 'abcdef')
@user.pseudonym_for_account(@account1, true).should be_nil
# conflict
@user2 = User.create! { |u| u.workflow_state = 'registered' }
@user2.pseudonyms.create!(:account => @account1, :unique_id => 'jt@instructure.com', :password => 'abcdef', :password_confirmation => 'abcdef')
@user.pseudonyms.create!(:unique_id => 'jt@instructure.com', :password => 'ghijkl', :password_confirmation => 'ghijkl')
@user.pseudonym_for_account(@account1, true).should be_nil
end
end
end