canvas-lms/app/models/communication_channel.rb

384 lines
12 KiB
Ruby
Raw Normal View History

2011-02-01 09:57:29 +08:00
#
# Copyright (C) 2011 Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
class CommunicationChannel < ActiveRecord::Base
extend ActiveSupport::Memoizable
2011-02-01 09:57:29 +08:00
# You should start thinking about communication channels
# as independent of pseudonyms
include Workflow
attr_accessible :user, :path, :path_type, :build_pseudonym_on_confirm, :pseudonym
belongs_to :pseudonym
has_many :pseudonyms
belongs_to :user
has_many :notification_policies, :dependent => :destroy
has_many :messages
belongs_to :access_token
user searching module refs #CNVS-2326 Added a UserSearch module that provides and interface for searching across the name, the email, and the sis id (and the database id), and added a trigram index on the columns that will be searched against. also made a slight refactor to the AR initializer in order to expose some of the behavior I wanted more granularly and added some specs to cover it fixed a small permissions bug in course.rb and pulled some scoping out of the courses controller down into the user search module TEST PLAN: This is currently not accessible from the site itself, this is just the ground work for the eventual user search api endpoint. The code is not called by anything currently in production either so there is no need for regression testing. The one thing to check would be the creation of new users (and pseudonyms and communication channels). There have been new indexes added to columns on those tables and there is some documentation indicating that writing to these indexes can be time consuming if the data set is quite large. It would be worth making sure that there have not been any unacceptable performance regressions in the creation of any of those record types in a database that has a full load of data (comparable to the production environment) Change-Id: I8fb13a6ec714f27efc8012c5ed2bed4d963c24e6 Reviewed-on: https://gerrit.instructure.com/16459 Tested-by: Jenkins <jenkins@instructure.com> QA-Review: Clare Hetherington <clare@instructure.com> Reviewed-by: Brian Palmer <brianp@instructure.com>
2012-12-29 03:28:09 +08:00
2011-02-01 09:57:29 +08:00
before_save :consider_retiring, :assert_path_type, :set_confirmation_code
before_save :consider_building_pseudonym
validates_presence_of :path, :path_type, :user, :workflow_state
validate :uniqueness_of_path
validate :not_otp_communication_channel, :if => lambda { |cc| cc.path_type == TYPE_SMS && cc.retired? && !cc.new_record? }
validates_presence_of :access_token_id, if: lambda { |cc| cc.path_type == TYPE_PUSH }
refactor user creation/invitations closes #5833 fixes #5573, #5572, #5753 * communication channels are now only unique within a single user * UserList changes * Always resolve pseudonym#unique_ids * Support looking up by SMS CCs * Option to either require e-mails match an existing CC, or e-mails that don't match a Pseudonym will always be returned unattached (relying on better merging behavior to not have a gazillion accounts created) * Method to return users, creating new ones (*without* a Pseudonym) if necessary. (can't create with a pseudonym, since Pseudonym#unique_id is still unique, I can't have multiple outstanding users with the same unique_id) * EnrollmentsFromUserList is mostly gutted, now using UserList's functionality directy. * Use UserList for adding account admins, removing the now unused Account#add_admin => User#find_by_email/User#assert_by_email codepath * Update UsersController#create to not worry about duplicate communication channels * Remove AccountsController#add_user, and just use UsersController#create * Change SIS::UserImporter to send out a merge opportunity e-mail if a conflicting CC is found (but still create the CC) * In /profile, don't worry about conflicting CCs (the CC confirmation process will now allow merging) * Remove CommunicationChannelsController#try_merge and #merge * For the non-simple case of CoursesController#enrollment_invitation redirect to /register (CommunicationsChannelController#confirm) * Remove CoursesController#transfer_enrollment * Move PseudonymsController#registration_confirmation to CommunicationChannelsController#confirm (have to be able to register an account without a Pseudonym yet) * Fold the old direct confirm functionality in, if there are no available merge opportunities * Allow merging the new account with the currently logged in user * Allow changing the Pseudonym#unique_id when registering a new account (since there might be conflicts) * Display a list of merge opportunities based on conflicting communication channels * Provide link(s) to log in as the other user, redirecting back to the registration page after login is complete (to complete the merge as the current user) * Remove several assert_* methods that are no longer needed * Update PseudonymSessionsController a bit to deal with the new way of dealing with conflicting CCs (especially CCs from LDAP), and to redirect back to the registration/confirmation page when attempting to do a merge * Expose the open_registration setting; use it to control if inviting users to a course is able to create new users Change-Id: If2f38818a71af656854d3bf8431ddbf5dcb84691 Reviewed-on: https://gerrit.instructure.com/6149 Tested-by: Hudson <hudson@instructure.com> Reviewed-by: Jacob Fugal <jacob@instructure.com>
2011-10-13 04:30:48 +08:00
2011-02-01 09:57:29 +08:00
acts_as_list :scope => :user_id
user searching module refs #CNVS-2326 Added a UserSearch module that provides and interface for searching across the name, the email, and the sis id (and the database id), and added a trigram index on the columns that will be searched against. also made a slight refactor to the AR initializer in order to expose some of the behavior I wanted more granularly and added some specs to cover it fixed a small permissions bug in course.rb and pulled some scoping out of the courses controller down into the user search module TEST PLAN: This is currently not accessible from the site itself, this is just the ground work for the eventual user search api endpoint. The code is not called by anything currently in production either so there is no need for regression testing. The one thing to check would be the creation of new users (and pseudonyms and communication channels). There have been new indexes added to columns on those tables and there is some documentation indicating that writing to these indexes can be time consuming if the data set is quite large. It would be worth making sure that there have not been any unacceptable performance regressions in the creation of any of those record types in a database that has a full load of data (comparable to the production environment) Change-Id: I8fb13a6ec714f27efc8012c5ed2bed4d963c24e6 Reviewed-on: https://gerrit.instructure.com/16459 Tested-by: Jenkins <jenkins@instructure.com> QA-Review: Clare Hetherington <clare@instructure.com> Reviewed-by: Brian Palmer <brianp@instructure.com>
2012-12-29 03:28:09 +08:00
2011-02-01 09:57:29 +08:00
has_a_broadcast_policy
user searching module refs #CNVS-2326 Added a UserSearch module that provides and interface for searching across the name, the email, and the sis id (and the database id), and added a trigram index on the columns that will be searched against. also made a slight refactor to the AR initializer in order to expose some of the behavior I wanted more granularly and added some specs to cover it fixed a small permissions bug in course.rb and pulled some scoping out of the courses controller down into the user search module TEST PLAN: This is currently not accessible from the site itself, this is just the ground work for the eventual user search api endpoint. The code is not called by anything currently in production either so there is no need for regression testing. The one thing to check would be the creation of new users (and pseudonyms and communication channels). There have been new indexes added to columns on those tables and there is some documentation indicating that writing to these indexes can be time consuming if the data set is quite large. It would be worth making sure that there have not been any unacceptable performance regressions in the creation of any of those record types in a database that has a full load of data (comparable to the production environment) Change-Id: I8fb13a6ec714f27efc8012c5ed2bed4d963c24e6 Reviewed-on: https://gerrit.instructure.com/16459 Tested-by: Jenkins <jenkins@instructure.com> QA-Review: Clare Hetherington <clare@instructure.com> Reviewed-by: Brian Palmer <brianp@instructure.com>
2012-12-29 03:28:09 +08:00
2011-02-01 09:57:29 +08:00
attr_reader :request_password
attr_reader :send_confirmation
refactor user creation/invitations closes #5833 fixes #5573, #5572, #5753 * communication channels are now only unique within a single user * UserList changes * Always resolve pseudonym#unique_ids * Support looking up by SMS CCs * Option to either require e-mails match an existing CC, or e-mails that don't match a Pseudonym will always be returned unattached (relying on better merging behavior to not have a gazillion accounts created) * Method to return users, creating new ones (*without* a Pseudonym) if necessary. (can't create with a pseudonym, since Pseudonym#unique_id is still unique, I can't have multiple outstanding users with the same unique_id) * EnrollmentsFromUserList is mostly gutted, now using UserList's functionality directy. * Use UserList for adding account admins, removing the now unused Account#add_admin => User#find_by_email/User#assert_by_email codepath * Update UsersController#create to not worry about duplicate communication channels * Remove AccountsController#add_user, and just use UsersController#create * Change SIS::UserImporter to send out a merge opportunity e-mail if a conflicting CC is found (but still create the CC) * In /profile, don't worry about conflicting CCs (the CC confirmation process will now allow merging) * Remove CommunicationChannelsController#try_merge and #merge * For the non-simple case of CoursesController#enrollment_invitation redirect to /register (CommunicationsChannelController#confirm) * Remove CoursesController#transfer_enrollment * Move PseudonymsController#registration_confirmation to CommunicationChannelsController#confirm (have to be able to register an account without a Pseudonym yet) * Fold the old direct confirm functionality in, if there are no available merge opportunities * Allow merging the new account with the currently logged in user * Allow changing the Pseudonym#unique_id when registering a new account (since there might be conflicts) * Display a list of merge opportunities based on conflicting communication channels * Provide link(s) to log in as the other user, redirecting back to the registration page after login is complete (to complete the merge as the current user) * Remove several assert_* methods that are no longer needed * Update PseudonymSessionsController a bit to deal with the new way of dealing with conflicting CCs (especially CCs from LDAP), and to redirect back to the registration/confirmation page when attempting to do a merge * Expose the open_registration setting; use it to control if inviting users to a course is able to create new users Change-Id: If2f38818a71af656854d3bf8431ddbf5dcb84691 Reviewed-on: https://gerrit.instructure.com/6149 Tested-by: Hudson <hudson@instructure.com> Reviewed-by: Jacob Fugal <jacob@instructure.com>
2011-10-13 04:30:48 +08:00
# Constants for the different supported communication channels
TYPE_EMAIL = 'email'
TYPE_SMS = 'sms'
TYPE_CHAT = 'chat'
TYPE_TWITTER = 'twitter'
TYPE_FACEBOOK = 'facebook'
TYPE_PUSH = 'push'
RETIRE_THRESHOLD = 5
multi-factor authentication closes #9532 test plan: * enable optional MFA, and check the following: * normal log in should not be affected * you can enroll in MFA from your profile page * you can re-enroll in MFA from your profile page * you can disable MFA from your profile page * MFA can be reset by an admin on your user page * when enrolled, you are asked for verification code after username/password when logging in * you can't access any other part of the site directly until until entering your verification code * enable required MFA, and check the following * when not enrolled in MFA, and you log in, you are forced to enroll * you cannot disable MFA from your profile page * you can re-enroll in MFA from your profile page * an admin (other than himself) can reset MFA from the user page * for enrolling in MFA * use Google Authenticator and scan the QR code; you should have 30-seconds or so of extra leeway to enter your code * having no SMS communication channels on your profile, the enrollment page should just have a form to add a new phone * having one or more SMS communication channels on your profile, the enrollment page should list them, or allow you to create a new one (and switch back) * having more than one SMS communication channel on your profile, the enrollment page should remember which one you have selected after you click "send" * an unconfirmed SMS channel should go to confirmed when it's used to enroll in MFA * you should not be able to go directly to /login/otp to enroll if you used "Remember me" token to log in * MFA login flow * if configured with SMS, it should send you an SMS after you put in your username/password; you should have about 5 minutes of leeway to put it in * if you don't check "remember computer" checkbox, you should have to enter a verification code each time you log in * if you do check it, you shouldn't have to enter your code anymore (for three days). it also shouldn't SMS you a verification code each time you log in * setting MFA to required for admins should make it required for admins, optional for other users * with MFA enabled, directly go to /login/otp after entering username/password but before entering a verification code; it should send you back to the main login page * if you enrolled via SMS, you should not be able to remove that SMS from your profile * there should not be a reset MFA link on a user page if they haven't enrolled * test a login or required enrollment sequence with CAS and/or SAML Change-Id: I692de7405bf7ca023183e717930ee940ccf0d5e6 Reviewed-on: https://gerrit.instructure.com/12700 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Brian Palmer <brianp@instructure.com>
2012-08-03 05:17:50 +08:00
def self.sms_carriers
@sms_carriers ||= Canvas::ICU.collate_by((Setting.from_config('sms', false) ||
multi-factor authentication closes #9532 test plan: * enable optional MFA, and check the following: * normal log in should not be affected * you can enroll in MFA from your profile page * you can re-enroll in MFA from your profile page * you can disable MFA from your profile page * MFA can be reset by an admin on your user page * when enrolled, you are asked for verification code after username/password when logging in * you can't access any other part of the site directly until until entering your verification code * enable required MFA, and check the following * when not enrolled in MFA, and you log in, you are forced to enroll * you cannot disable MFA from your profile page * you can re-enroll in MFA from your profile page * an admin (other than himself) can reset MFA from the user page * for enrolling in MFA * use Google Authenticator and scan the QR code; you should have 30-seconds or so of extra leeway to enter your code * having no SMS communication channels on your profile, the enrollment page should just have a form to add a new phone * having one or more SMS communication channels on your profile, the enrollment page should list them, or allow you to create a new one (and switch back) * having more than one SMS communication channel on your profile, the enrollment page should remember which one you have selected after you click "send" * an unconfirmed SMS channel should go to confirmed when it's used to enroll in MFA * you should not be able to go directly to /login/otp to enroll if you used "Remember me" token to log in * MFA login flow * if configured with SMS, it should send you an SMS after you put in your username/password; you should have about 5 minutes of leeway to put it in * if you don't check "remember computer" checkbox, you should have to enter a verification code each time you log in * if you do check it, you shouldn't have to enter your code anymore (for three days). it also shouldn't SMS you a verification code each time you log in * setting MFA to required for admins should make it required for admins, optional for other users * with MFA enabled, directly go to /login/otp after entering username/password but before entering a verification code; it should send you back to the main login page * if you enrolled via SMS, you should not be able to remove that SMS from your profile * there should not be a reset MFA link on a user page if they haven't enrolled * test a login or required enrollment sequence with CAS and/or SAML Change-Id: I692de7405bf7ca023183e717930ee940ccf0d5e6 Reviewed-on: https://gerrit.instructure.com/12700 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Brian Palmer <brianp@instructure.com>
2012-08-03 05:17:50 +08:00
{ 'AT&T' => 'txt.att.net',
'Alltel' => 'message.alltel.com',
'Boost' => 'myboostmobile.com',
'C Spire' => 'cspire1.com',
multi-factor authentication closes #9532 test plan: * enable optional MFA, and check the following: * normal log in should not be affected * you can enroll in MFA from your profile page * you can re-enroll in MFA from your profile page * you can disable MFA from your profile page * MFA can be reset by an admin on your user page * when enrolled, you are asked for verification code after username/password when logging in * you can't access any other part of the site directly until until entering your verification code * enable required MFA, and check the following * when not enrolled in MFA, and you log in, you are forced to enroll * you cannot disable MFA from your profile page * you can re-enroll in MFA from your profile page * an admin (other than himself) can reset MFA from the user page * for enrolling in MFA * use Google Authenticator and scan the QR code; you should have 30-seconds or so of extra leeway to enter your code * having no SMS communication channels on your profile, the enrollment page should just have a form to add a new phone * having one or more SMS communication channels on your profile, the enrollment page should list them, or allow you to create a new one (and switch back) * having more than one SMS communication channel on your profile, the enrollment page should remember which one you have selected after you click "send" * an unconfirmed SMS channel should go to confirmed when it's used to enroll in MFA * you should not be able to go directly to /login/otp to enroll if you used "Remember me" token to log in * MFA login flow * if configured with SMS, it should send you an SMS after you put in your username/password; you should have about 5 minutes of leeway to put it in * if you don't check "remember computer" checkbox, you should have to enter a verification code each time you log in * if you do check it, you shouldn't have to enter your code anymore (for three days). it also shouldn't SMS you a verification code each time you log in * setting MFA to required for admins should make it required for admins, optional for other users * with MFA enabled, directly go to /login/otp after entering username/password but before entering a verification code; it should send you back to the main login page * if you enrolled via SMS, you should not be able to remove that SMS from your profile * there should not be a reset MFA link on a user page if they haven't enrolled * test a login or required enrollment sequence with CAS and/or SAML Change-Id: I692de7405bf7ca023183e717930ee940ccf0d5e6 Reviewed-on: https://gerrit.instructure.com/12700 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Brian Palmer <brianp@instructure.com>
2012-08-03 05:17:50 +08:00
'Cingular' => 'cingularme.com',
'CellularOne' => 'mobile.celloneusa.com',
'Cricket' => 'sms.mycricket.com',
'Nextel' => 'messaging.nextel.com',
'Sprint PCS' => 'messaging.sprintpcs.com',
'T-Mobile' => 'tmomail.net',
'Verizon' => 'vtext.com',
'Virgin Mobile' => 'vmobl.com' }), &:first)
multi-factor authentication closes #9532 test plan: * enable optional MFA, and check the following: * normal log in should not be affected * you can enroll in MFA from your profile page * you can re-enroll in MFA from your profile page * you can disable MFA from your profile page * MFA can be reset by an admin on your user page * when enrolled, you are asked for verification code after username/password when logging in * you can't access any other part of the site directly until until entering your verification code * enable required MFA, and check the following * when not enrolled in MFA, and you log in, you are forced to enroll * you cannot disable MFA from your profile page * you can re-enroll in MFA from your profile page * an admin (other than himself) can reset MFA from the user page * for enrolling in MFA * use Google Authenticator and scan the QR code; you should have 30-seconds or so of extra leeway to enter your code * having no SMS communication channels on your profile, the enrollment page should just have a form to add a new phone * having one or more SMS communication channels on your profile, the enrollment page should list them, or allow you to create a new one (and switch back) * having more than one SMS communication channel on your profile, the enrollment page should remember which one you have selected after you click "send" * an unconfirmed SMS channel should go to confirmed when it's used to enroll in MFA * you should not be able to go directly to /login/otp to enroll if you used "Remember me" token to log in * MFA login flow * if configured with SMS, it should send you an SMS after you put in your username/password; you should have about 5 minutes of leeway to put it in * if you don't check "remember computer" checkbox, you should have to enter a verification code each time you log in * if you do check it, you shouldn't have to enter your code anymore (for three days). it also shouldn't SMS you a verification code each time you log in * setting MFA to required for admins should make it required for admins, optional for other users * with MFA enabled, directly go to /login/otp after entering username/password but before entering a verification code; it should send you back to the main login page * if you enrolled via SMS, you should not be able to remove that SMS from your profile * there should not be a reset MFA link on a user page if they haven't enrolled * test a login or required enrollment sequence with CAS and/or SAML Change-Id: I692de7405bf7ca023183e717930ee940ccf0d5e6 Reviewed-on: https://gerrit.instructure.com/12700 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Brian Palmer <brianp@instructure.com>
2012-08-03 05:17:50 +08:00
end
2011-02-01 09:57:29 +08:00
def pseudonym
user.pseudonyms.where(:unique_id => path).first if user
2011-02-01 09:57:29 +08:00
end
set_broadcast_policy do |p|
p.dispatch :forgot_password
p.to { self }
p.whenever { |record|
@request_password
}
user searching module refs #CNVS-2326 Added a UserSearch module that provides and interface for searching across the name, the email, and the sis id (and the database id), and added a trigram index on the columns that will be searched against. also made a slight refactor to the AR initializer in order to expose some of the behavior I wanted more granularly and added some specs to cover it fixed a small permissions bug in course.rb and pulled some scoping out of the courses controller down into the user search module TEST PLAN: This is currently not accessible from the site itself, this is just the ground work for the eventual user search api endpoint. The code is not called by anything currently in production either so there is no need for regression testing. The one thing to check would be the creation of new users (and pseudonyms and communication channels). There have been new indexes added to columns on those tables and there is some documentation indicating that writing to these indexes can be time consuming if the data set is quite large. It would be worth making sure that there have not been any unacceptable performance regressions in the creation of any of those record types in a database that has a full load of data (comparable to the production environment) Change-Id: I8fb13a6ec714f27efc8012c5ed2bed4d963c24e6 Reviewed-on: https://gerrit.instructure.com/16459 Tested-by: Jenkins <jenkins@instructure.com> QA-Review: Clare Hetherington <clare@instructure.com> Reviewed-by: Brian Palmer <brianp@instructure.com>
2012-12-29 03:28:09 +08:00
2011-02-01 09:57:29 +08:00
p.dispatch :confirm_registration
p.to { self }
p.whenever { |record|
@send_confirmation and
(record.workflow_state == 'active' ||
(record.workflow_state == 'unconfirmed' and (self.user.pre_registered? || self.user.creation_pending?))) and
self.path_type == TYPE_EMAIL
2011-02-01 09:57:29 +08:00
}
p.dispatch :confirm_email_communication_channel
p.to { self }
p.whenever { |record|
@send_confirmation and
record.workflow_state == 'unconfirmed' and self.user.registered? and
self.path_type == TYPE_EMAIL
2011-02-01 09:57:29 +08:00
}
p.context { @root_account }
user searching module refs #CNVS-2326 Added a UserSearch module that provides and interface for searching across the name, the email, and the sis id (and the database id), and added a trigram index on the columns that will be searched against. also made a slight refactor to the AR initializer in order to expose some of the behavior I wanted more granularly and added some specs to cover it fixed a small permissions bug in course.rb and pulled some scoping out of the courses controller down into the user search module TEST PLAN: This is currently not accessible from the site itself, this is just the ground work for the eventual user search api endpoint. The code is not called by anything currently in production either so there is no need for regression testing. The one thing to check would be the creation of new users (and pseudonyms and communication channels). There have been new indexes added to columns on those tables and there is some documentation indicating that writing to these indexes can be time consuming if the data set is quite large. It would be worth making sure that there have not been any unacceptable performance regressions in the creation of any of those record types in a database that has a full load of data (comparable to the production environment) Change-Id: I8fb13a6ec714f27efc8012c5ed2bed4d963c24e6 Reviewed-on: https://gerrit.instructure.com/16459 Tested-by: Jenkins <jenkins@instructure.com> QA-Review: Clare Hetherington <clare@instructure.com> Reviewed-by: Brian Palmer <brianp@instructure.com>
2012-12-29 03:28:09 +08:00
2011-02-01 09:57:29 +08:00
p.dispatch :merge_email_communication_channel
p.to { self }
p.whenever {|record|
@send_merge_notification and
self.path_type == TYPE_EMAIL
2011-02-01 09:57:29 +08:00
}
user searching module refs #CNVS-2326 Added a UserSearch module that provides and interface for searching across the name, the email, and the sis id (and the database id), and added a trigram index on the columns that will be searched against. also made a slight refactor to the AR initializer in order to expose some of the behavior I wanted more granularly and added some specs to cover it fixed a small permissions bug in course.rb and pulled some scoping out of the courses controller down into the user search module TEST PLAN: This is currently not accessible from the site itself, this is just the ground work for the eventual user search api endpoint. The code is not called by anything currently in production either so there is no need for regression testing. The one thing to check would be the creation of new users (and pseudonyms and communication channels). There have been new indexes added to columns on those tables and there is some documentation indicating that writing to these indexes can be time consuming if the data set is quite large. It would be worth making sure that there have not been any unacceptable performance regressions in the creation of any of those record types in a database that has a full load of data (comparable to the production environment) Change-Id: I8fb13a6ec714f27efc8012c5ed2bed4d963c24e6 Reviewed-on: https://gerrit.instructure.com/16459 Tested-by: Jenkins <jenkins@instructure.com> QA-Review: Clare Hetherington <clare@instructure.com> Reviewed-by: Brian Palmer <brianp@instructure.com>
2012-12-29 03:28:09 +08:00
2011-02-01 09:57:29 +08:00
p.dispatch :confirm_sms_communication_channel
p.to { self }
p.whenever { |record|
@send_confirmation and
record.workflow_state == 'unconfirmed' and
self.path_type == TYPE_SMS and
2011-02-01 09:57:29 +08:00
!self.user.creation_pending?
}
p.context { @root_account }
2011-02-01 09:57:29 +08:00
end
def uniqueness_of_path
return if path.nil?
return if retired?
return unless user_id
conditions = ["LOWER(path)=LOWER(?) AND user_id=? AND path_type=? AND workflow_state IN('unconfirmed', 'active')", path, user_id, path_type]
unless new_record?
conditions.first << " AND id<>?"
conditions << id
end
if self.class.where(conditions).exists?
self.errors.add(:path, :taken, :value => path)
end
end
multi-factor authentication closes #9532 test plan: * enable optional MFA, and check the following: * normal log in should not be affected * you can enroll in MFA from your profile page * you can re-enroll in MFA from your profile page * you can disable MFA from your profile page * MFA can be reset by an admin on your user page * when enrolled, you are asked for verification code after username/password when logging in * you can't access any other part of the site directly until until entering your verification code * enable required MFA, and check the following * when not enrolled in MFA, and you log in, you are forced to enroll * you cannot disable MFA from your profile page * you can re-enroll in MFA from your profile page * an admin (other than himself) can reset MFA from the user page * for enrolling in MFA * use Google Authenticator and scan the QR code; you should have 30-seconds or so of extra leeway to enter your code * having no SMS communication channels on your profile, the enrollment page should just have a form to add a new phone * having one or more SMS communication channels on your profile, the enrollment page should list them, or allow you to create a new one (and switch back) * having more than one SMS communication channel on your profile, the enrollment page should remember which one you have selected after you click "send" * an unconfirmed SMS channel should go to confirmed when it's used to enroll in MFA * you should not be able to go directly to /login/otp to enroll if you used "Remember me" token to log in * MFA login flow * if configured with SMS, it should send you an SMS after you put in your username/password; you should have about 5 minutes of leeway to put it in * if you don't check "remember computer" checkbox, you should have to enter a verification code each time you log in * if you do check it, you shouldn't have to enter your code anymore (for three days). it also shouldn't SMS you a verification code each time you log in * setting MFA to required for admins should make it required for admins, optional for other users * with MFA enabled, directly go to /login/otp after entering username/password but before entering a verification code; it should send you back to the main login page * if you enrolled via SMS, you should not be able to remove that SMS from your profile * there should not be a reset MFA link on a user page if they haven't enrolled * test a login or required enrollment sequence with CAS and/or SAML Change-Id: I692de7405bf7ca023183e717930ee940ccf0d5e6 Reviewed-on: https://gerrit.instructure.com/12700 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Brian Palmer <brianp@instructure.com>
2012-08-03 05:17:50 +08:00
def not_otp_communication_channel
self.errors.add(:workflow_state, "Can't remove a user's SMS that is used for one time passwords") if self.id == self.user.otp_communication_channel_id
end
def context
pseudonym.try(:account)
end
# Public: Determine if this channel is the product of an SIS import.
#
# Returns a boolean.
def imported?
id.present? &&
Pseudonym.where(:sis_communication_channel_id => self).exists?
end
# Return the 'path' for simple communication channel types like email and sms. For
# Facebook and Twitter, return the user's configured user_name for the service.
2011-02-01 09:57:29 +08:00
def path_description
if self.path_type == TYPE_FACEBOOK
res = self.user.user_services.for_service(TYPE_FACEBOOK).first.service_user_name rescue nil
res ||= t :default_facebook_account, 'Facebook Account'
2011-02-01 09:57:29 +08:00
res
elsif self.path_type == TYPE_TWITTER
res = self.user.user_services.for_service(TYPE_TWITTER).first.service_user_name rescue nil
res ||= t :default_twitter_handle, 'Twitter Handle'
res
elsif self.path_type == TYPE_PUSH
access_token.purpose ? "#{access_token.purpose} (#{access_token.developer_key.name})" : access_token.developer_key.name
2011-02-01 09:57:29 +08:00
else
self.path
end
end
def forgot_password!
@request_password = true
set_confirmation_code(true)
self.save!
@request_password = false
end
def send_confirmation!(root_account)
2011-02-01 09:57:29 +08:00
@send_confirmation = true
@root_account = root_account
2011-02-01 09:57:29 +08:00
self.save!
@root_account = nil
2011-02-01 09:57:29 +08:00
@send_confirmation = false
end
def send_merge_notification!
@send_merge_notification = true
self.save!
@send_merge_notification = false
end
multi-factor authentication closes #9532 test plan: * enable optional MFA, and check the following: * normal log in should not be affected * you can enroll in MFA from your profile page * you can re-enroll in MFA from your profile page * you can disable MFA from your profile page * MFA can be reset by an admin on your user page * when enrolled, you are asked for verification code after username/password when logging in * you can't access any other part of the site directly until until entering your verification code * enable required MFA, and check the following * when not enrolled in MFA, and you log in, you are forced to enroll * you cannot disable MFA from your profile page * you can re-enroll in MFA from your profile page * an admin (other than himself) can reset MFA from the user page * for enrolling in MFA * use Google Authenticator and scan the QR code; you should have 30-seconds or so of extra leeway to enter your code * having no SMS communication channels on your profile, the enrollment page should just have a form to add a new phone * having one or more SMS communication channels on your profile, the enrollment page should list them, or allow you to create a new one (and switch back) * having more than one SMS communication channel on your profile, the enrollment page should remember which one you have selected after you click "send" * an unconfirmed SMS channel should go to confirmed when it's used to enroll in MFA * you should not be able to go directly to /login/otp to enroll if you used "Remember me" token to log in * MFA login flow * if configured with SMS, it should send you an SMS after you put in your username/password; you should have about 5 minutes of leeway to put it in * if you don't check "remember computer" checkbox, you should have to enter a verification code each time you log in * if you do check it, you shouldn't have to enter your code anymore (for three days). it also shouldn't SMS you a verification code each time you log in * setting MFA to required for admins should make it required for admins, optional for other users * with MFA enabled, directly go to /login/otp after entering username/password but before entering a verification code; it should send you back to the main login page * if you enrolled via SMS, you should not be able to remove that SMS from your profile * there should not be a reset MFA link on a user page if they haven't enrolled * test a login or required enrollment sequence with CAS and/or SAML Change-Id: I692de7405bf7ca023183e717930ee940ccf0d5e6 Reviewed-on: https://gerrit.instructure.com/12700 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Brian Palmer <brianp@instructure.com>
2012-08-03 05:17:50 +08:00
def send_otp!(code)
m = self.messages.new
m.to = self.path
m.body = t :body, "Your Canvas verification code is %{verification_code}", :verification_code => code
Mailer.message(m).deliver rescue nil # omg! just ignore delivery failures
multi-factor authentication closes #9532 test plan: * enable optional MFA, and check the following: * normal log in should not be affected * you can enroll in MFA from your profile page * you can re-enroll in MFA from your profile page * you can disable MFA from your profile page * MFA can be reset by an admin on your user page * when enrolled, you are asked for verification code after username/password when logging in * you can't access any other part of the site directly until until entering your verification code * enable required MFA, and check the following * when not enrolled in MFA, and you log in, you are forced to enroll * you cannot disable MFA from your profile page * you can re-enroll in MFA from your profile page * an admin (other than himself) can reset MFA from the user page * for enrolling in MFA * use Google Authenticator and scan the QR code; you should have 30-seconds or so of extra leeway to enter your code * having no SMS communication channels on your profile, the enrollment page should just have a form to add a new phone * having one or more SMS communication channels on your profile, the enrollment page should list them, or allow you to create a new one (and switch back) * having more than one SMS communication channel on your profile, the enrollment page should remember which one you have selected after you click "send" * an unconfirmed SMS channel should go to confirmed when it's used to enroll in MFA * you should not be able to go directly to /login/otp to enroll if you used "Remember me" token to log in * MFA login flow * if configured with SMS, it should send you an SMS after you put in your username/password; you should have about 5 minutes of leeway to put it in * if you don't check "remember computer" checkbox, you should have to enter a verification code each time you log in * if you do check it, you shouldn't have to enter your code anymore (for three days). it also shouldn't SMS you a verification code each time you log in * setting MFA to required for admins should make it required for admins, optional for other users * with MFA enabled, directly go to /login/otp after entering username/password but before entering a verification code; it should send you back to the main login page * if you enrolled via SMS, you should not be able to remove that SMS from your profile * there should not be a reset MFA link on a user page if they haven't enrolled * test a login or required enrollment sequence with CAS and/or SAML Change-Id: I692de7405bf7ca023183e717930ee940ccf0d5e6 Reviewed-on: https://gerrit.instructure.com/12700 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Brian Palmer <brianp@instructure.com>
2012-08-03 05:17:50 +08:00
end
2011-02-01 09:57:29 +08:00
# If you are creating a new communication_channel, do nothing, this just
# works. If you are resetting the confirmation_code, call @cc.
# set_confirmation_code(true), or just save the record to leave the old
# confirmation code in place.
def set_confirmation_code(reset=false)
self.confirmation_code = nil if reset
if self.path_type == TYPE_EMAIL or self.path_type.nil?
2011-02-01 09:57:29 +08:00
self.confirmation_code ||= AutoHandle.generate(nil, 25)
else
self.confirmation_code ||= AutoHandle.generate
end
true
2011-02-01 09:57:29 +08:00
end
scope :for, lambda { |context|
2011-02-01 09:57:29 +08:00
case context
when User
where(:user_id => context)
2011-02-01 09:57:29 +08:00
when Notification
includes(:notification_policies).where(:notification_policies => { :notification_id => context })
2011-02-01 09:57:29 +08:00
else
scoped
2011-02-01 09:57:29 +08:00
end
}
scope :by_path, lambda { |path|
if %{mysql mysql2}.include?(connection_pool.spec.config[:adapter])
where(:path => path)
else
where("LOWER(communication_channels.path)=LOWER(?)", path)
end
}
scope :email, where(:path_type => TYPE_EMAIL)
scope :sms, where(:path_type => TYPE_SMS)
multi-factor authentication closes #9532 test plan: * enable optional MFA, and check the following: * normal log in should not be affected * you can enroll in MFA from your profile page * you can re-enroll in MFA from your profile page * you can disable MFA from your profile page * MFA can be reset by an admin on your user page * when enrolled, you are asked for verification code after username/password when logging in * you can't access any other part of the site directly until until entering your verification code * enable required MFA, and check the following * when not enrolled in MFA, and you log in, you are forced to enroll * you cannot disable MFA from your profile page * you can re-enroll in MFA from your profile page * an admin (other than himself) can reset MFA from the user page * for enrolling in MFA * use Google Authenticator and scan the QR code; you should have 30-seconds or so of extra leeway to enter your code * having no SMS communication channels on your profile, the enrollment page should just have a form to add a new phone * having one or more SMS communication channels on your profile, the enrollment page should list them, or allow you to create a new one (and switch back) * having more than one SMS communication channel on your profile, the enrollment page should remember which one you have selected after you click "send" * an unconfirmed SMS channel should go to confirmed when it's used to enroll in MFA * you should not be able to go directly to /login/otp to enroll if you used "Remember me" token to log in * MFA login flow * if configured with SMS, it should send you an SMS after you put in your username/password; you should have about 5 minutes of leeway to put it in * if you don't check "remember computer" checkbox, you should have to enter a verification code each time you log in * if you do check it, you shouldn't have to enter your code anymore (for three days). it also shouldn't SMS you a verification code each time you log in * setting MFA to required for admins should make it required for admins, optional for other users * with MFA enabled, directly go to /login/otp after entering username/password but before entering a verification code; it should send you back to the main login page * if you enrolled via SMS, you should not be able to remove that SMS from your profile * there should not be a reset MFA link on a user page if they haven't enrolled * test a login or required enrollment sequence with CAS and/or SAML Change-Id: I692de7405bf7ca023183e717930ee940ccf0d5e6 Reviewed-on: https://gerrit.instructure.com/12700 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Brian Palmer <brianp@instructure.com>
2012-08-03 05:17:50 +08:00
scope :active, where(:workflow_state => 'active')
scope :unretired, where("communication_channels.workflow_state<>'retired'")
scope :for_notification_frequency, lambda { |notification, frequency|
includes(:notification_policies).where(:notification_policies => { :notification_id => notification, :frequency => frequency })
2011-02-01 09:57:29 +08:00
}
# Get the list of communication channels that overrides an association's default order clause.
# This returns an unretired and properly ordered already fetch array of CommunicationChannel objects ready for usage.
def self.all_ordered_for_display(user)
# Add communication channel for users that already had Twitter
# integrated before we started offering it as a cc
twitter_service = user.user_services.for_service(CommunicationChannel::TYPE_TWITTER).first
twitter_service.assert_communication_channel if twitter_service
rank_order = [TYPE_EMAIL, TYPE_SMS, TYPE_PUSH]
# Add facebook and twitter (in that order) if the user's account is setup for them.
rank_order << TYPE_FACEBOOK unless user.user_services.for_service(CommunicationChannel::TYPE_FACEBOOK).empty?
rank_order << TYPE_TWITTER if twitter_service
self.unretired.where('communication_channels.path_type IN (?)', rank_order).
order("#{self.rank_sql(rank_order, 'communication_channels.path_type')} ASC, communication_channels.position asc").
all
end
scope :include_policies, includes(:notification_policies)
scope :in_state, lambda { |state| where(:workflow_state => state.to_s) }
scope :of_type, lambda {|type| where(:path_type => type) }
2011-02-01 09:57:29 +08:00
def can_notify?
self.notification_policies.any? { |np| np.frequency == 'never' } ? false : true
end
def move_to_user(user, migrate=true)
return unless user
if self.pseudonym && self.pseudonym.unique_id == self.path
self.pseudonym.move_to_user(user, migrate)
else
old_user_id = self.user_id
self.user_id = user.id
self.save!
if old_user_id
Pseudonym.where(:user_id => old_user_id, :unique_id => self.path).update_all(:user_id => user)
User.where(:id => [old_user_id, user]).update_all(:updated_at => Time.now.utc)
2011-02-01 09:57:29 +08:00
end
end
end
def consider_building_pseudonym
if self.build_pseudonym_on_confirm && self.active?
self.build_pseudonym_on_confirm = false
pseudonym = self.user.pseudonyms.build(:unique_id => self.path, :account => Account.default)
existing_pseudonym = self.user.pseudonyms.active.select{|p| p.account_id == Account.default.id }.first
if existing_pseudonym
pseudonym.password_salt = existing_pseudonym.password_salt
pseudonym.crypted_password = existing_pseudonym.crypted_password
end
pseudonym.save!
end
true
end
def consider_retiring
self.retire if self.bounce_count >= RETIRE_THRESHOLD
true
2011-02-01 09:57:29 +08:00
end
alias_method :destroy!, :destroy
def destroy
self.workflow_state = 'retired'
self.save
end
workflow do
state :unconfirmed do
event :confirm, :transitions_to => :active do
self.set_confirmation_code(true)
end
event :retire, :transitions_to => :retired
end
state :active do
event :retire, :transitions_to => :retired
end
state :retired do
event :re_activate, :transitions_to => :active do
self.bounce_count = 0
end
end
end
# This is setup as a default in the database, but this overcomes misspellings.
def assert_path_type
pt = self.path_type
self.path_type = TYPE_EMAIL unless pt == TYPE_EMAIL or pt == TYPE_SMS or pt == TYPE_CHAT or pt == TYPE_FACEBOOK or pt == TYPE_TWITTER or pt == TYPE_PUSH
true
2011-02-01 09:57:29 +08:00
end
protected :assert_path_type
def self.serialization_excludes; [:confirmation_code]; end
def self.associated_shards(path)
[Shard.default]
end
def merge_candidates(break_on_first_found = false)
shards = self.class.associated_shards(self.path) if Enrollment.cross_shard_invitations?
shards ||= [self.shard]
scope = CommunicationChannel.active.by_path(self.path).of_type(self.path_type)
merge_candidates = {}
Shard.with_each_shard(shards) do
scope.where("user_id<>?", self.user_id).includes(:user).map(&:user).select do |u|
result = merge_candidates.fetch(u.global_id) do
merge_candidates[u.global_id] = (u.all_active_pseudonyms.length != 0)
end
return [u] if result && break_on_first_found
result
end
end.uniq
end
def has_merge_candidates?
!merge_candidates(true).empty?
end
def self.create_push(access_token, device_token)
(scope(:find, :shard) || Shard.current).activate do
connection.transaction do
cc = new
cc.path_type = CommunicationChannel::TYPE_PUSH
cc.path = device_token
cc.access_token = access_token
cc.workflow_state = 'active'
# save first, so we can put the global id in it
cc.save!
response = DeveloperKey.sns.client.create_platform_endpoint(
platform_application_arn: access_token.developer_key.sns_arn,
token: device_token,
custom_user_data: cc.global_id.to_s
)
cc.internal_path = response.data[:endpoint_arn]
cc.save!
cc
end
end
end
2011-02-01 09:57:29 +08:00
end