2014-02-24 21:30:49 -06:00
require_dependency 'single_sign_on'
2014-04-18 23:00:40 -05:00
2014-02-24 21:30:49 -06:00
class DiscourseSingleSignOn < SingleSignOn
2014-06-02 02:32:39 -05:00
2014-02-24 21:30:49 -06:00
def self . sso_url
SiteSetting . sso_url
end
def self . sso_secret
SiteSetting . sso_secret
end
2017-07-27 20:20:09 -05:00
def self . generate_sso ( return_path = " / " )
2014-02-24 21:30:49 -06:00
sso = new
sso . nonce = SecureRandom . hex
2014-02-25 16:58:30 -06:00
sso . register_nonce ( return_path )
2014-11-26 00:25:54 -06:00
sso . return_sso_url = Discourse . base_url + " /session/sso_login "
2016-04-07 20:20:01 -05:00
sso
end
2017-07-27 20:20:09 -05:00
def self . generate_url ( return_path = " / " )
2016-04-07 20:20:01 -05:00
generate_sso ( return_path ) . to_url
2014-02-24 21:30:49 -06:00
end
2014-02-25 16:58:30 -06:00
def register_nonce ( return_path )
2014-02-24 21:30:49 -06:00
if nonce
2014-02-25 16:58:30 -06:00
$redis . setex ( nonce_key , NONCE_EXPIRY_TIME , return_path )
2014-02-24 21:30:49 -06:00
end
end
def nonce_valid?
nonce && $redis . get ( nonce_key ) . present?
end
2014-02-25 16:58:30 -06:00
def return_path
$redis . get ( nonce_key ) || " / "
end
2014-02-24 21:30:49 -06:00
def expire_nonce!
if nonce
$redis . del nonce_key
end
end
def nonce_key
" SSO_NONCE_ #{ nonce } "
end
2017-07-27 20:20:09 -05:00
def lookup_or_create_user ( ip_address = nil )
2014-05-06 08:41:59 -05:00
sso_record = SingleSignOnRecord . find_by ( external_id : external_id )
2014-04-15 00:53:48 -05:00
2016-09-01 21:04:22 -05:00
if sso_record && ( user = sso_record . user )
2014-02-24 21:30:49 -06:00
sso_record . last_payload = unsigned_payload
else
2015-02-23 14:58:45 -06:00
user = match_email_or_create_user ( ip_address )
2014-02-27 18:48:46 -06:00
sso_record = user . single_sign_on_record
end
2014-04-15 00:53:48 -05:00
2016-06-21 04:28:58 -05:00
# ensure it's not staged anymore
user . staged = false
2014-02-27 18:48:46 -06:00
# if the user isn't new or it's attached to the SSO record we might be overriding username or email
unless user . new_record?
change_external_attributes_and_override ( sso_record , user )
2014-02-24 21:30:49 -06:00
end
2015-05-19 11:16:02 -05:00
if sso_record && ( user = sso_record . user ) && ! user . active && ! require_activation
2014-02-25 17:28:03 -06:00
user . active = true
2014-06-02 02:32:39 -05:00
user . save!
2015-03-20 12:03:24 -05:00
user . enqueue_welcome_message ( 'welcome_user' ) unless suppress_welcome_message
2017-06-14 12:20:18 -05:00
user . set_automatic_groups
2014-02-25 17:28:03 -06:00
end
2014-04-15 00:53:48 -05:00
2017-07-27 20:20:09 -05:00
custom_fields . each do | k , v |
2014-04-21 22:52:13 -05:00
user . custom_fields [ k ] = v
end
2015-02-23 14:58:45 -06:00
user . ip_address = ip_address
2016-05-17 02:31:34 -05:00
2014-11-26 19:39:00 -06:00
user . admin = admin unless admin . nil?
user . moderator = moderator unless moderator . nil?
2017-01-31 18:42:27 -06:00
user . title = title unless title . nil?
2014-02-27 18:48:46 -06:00
# optionally save the user and sso_record if they have changed
2016-08-28 20:28:19 -05:00
user . user_avatar . save! if user . user_avatar
2014-02-27 18:48:46 -06:00
user . save!
2016-05-17 02:31:34 -05:00
2016-08-01 00:29:28 -05:00
if bio && ( user . user_profile . bio_raw . blank? || SiteSetting . sso_overrides_bio )
user . user_profile . bio_raw = bio
user . user_profile . save!
end
2016-05-17 02:31:34 -05:00
unless admin . nil? && moderator . nil?
Group . refresh_automatic_groups! ( :admins , :moderators , :staff )
end
2014-02-27 18:48:46 -06:00
sso_record . save!
2014-02-25 17:28:03 -06:00
2016-11-10 23:57:31 -06:00
if sso_record . user
apply_group_rules ( sso_record . user )
end
2014-02-24 21:30:49 -06:00
sso_record && sso_record . user
end
2014-04-15 00:53:48 -05:00
2014-02-27 18:48:46 -06:00
private
2014-04-15 00:53:48 -05:00
2018-04-09 22:17:23 -05:00
def synchronize_groups ( user )
names = ( groups || " " ) . split ( " , " ) . map ( & :downcase )
ids = Group . where ( 'LOWER(NAME) in (?) AND NOT automatic' , names ) . pluck ( :id )
group_users = GroupUser
. where ( 'group_id IN (SELECT id FROM groups WHERE NOT automatic)' )
. where ( user_id : user . id )
2018-04-10 00:30:18 -05:00
delete_group_users = group_users
if ids . length > 0
delete_group_users = group_users . where ( 'group_id NOT IN (?)' , ids )
end
delete_group_users . destroy_all
2018-04-09 22:17:23 -05:00
ids -= group_users . where ( 'group_id IN (?)' , ids ) . pluck ( :group_id )
ids . each do | group_id |
GroupUser . create ( group_id : group_id , user_id : user . id )
end
end
2016-11-10 23:57:31 -06:00
def apply_group_rules ( user )
2018-04-09 22:17:23 -05:00
if SiteSetting . sso_overrides_groups
synchronize_groups ( user )
return
end
2016-11-10 23:57:31 -06:00
if add_groups
2017-08-02 10:30:23 -05:00
split = add_groups . split ( " , " ) . map ( & :downcase )
2016-11-10 23:57:31 -06:00
if split . length > 0
2017-08-11 17:09:22 -05:00
Group . where ( 'LOWER(name) in (?) AND NOT automatic' , split ) . pluck ( :id ) . each do | id |
2016-11-10 23:57:31 -06:00
unless GroupUser . where ( group_id : id , user_id : user . id ) . exists?
GroupUser . create ( group_id : id , user_id : user . id )
end
end
end
end
if remove_groups
2017-08-11 17:09:22 -05:00
split = remove_groups . split ( " , " ) . map ( & :downcase )
2016-11-10 23:57:31 -06:00
if split . length > 0
GroupUser
2017-07-27 20:20:09 -05:00
. where ( user_id : user . id )
2017-08-11 17:09:22 -05:00
. where ( 'group_id IN (SELECT id FROM groups WHERE LOWER(name) in (?))' , split )
2017-07-27 20:20:09 -05:00
. destroy_all
2016-11-10 23:57:31 -06:00
end
end
end
2015-02-23 14:58:45 -06:00
def match_email_or_create_user ( ip_address )
2018-04-12 03:18:49 -05:00
# Use a mutex here to counter SSO requests that are sent at the same time w
# the same email payload
DistributedMutex . synchronize ( " discourse_single_sign_on_ #{ email } " ) do
unless user = User . find_by_email ( email )
try_name = name . presence
try_username = username . presence
user_params = {
primary_email : UserEmail . new ( email : email , primary : true ) ,
name : try_name || User . suggest_name ( try_username || email ) ,
username : UserNameSuggester . suggest ( try_username || try_name || email ) ,
ip_address : ip_address
}
user = User . create! ( user_params )
if SiteSetting . verbose_sso_logging
Rails . logger . warn ( " Verbose SSO log: New User (user_id: #{ user . id } ) Params: #{ user_params } User Params: #{ user . attributes } User Errors: #{ user . errors . full_messages } Email: #{ user . primary_email . attributes } Email Error: #{ user . primary_email . errors . full_messages } " )
end
2017-11-07 05:38:36 -06:00
end
2014-02-24 21:30:49 -06:00
2018-04-12 03:18:49 -05:00
if user
if sso_record = user . single_sign_on_record
sso_record . last_payload = unsigned_payload
sso_record . external_id = external_id
else
if avatar_url . present?
Jobs . enqueue ( :download_avatar_from_url ,
url : avatar_url ,
user_id : user . id ,
override_gravatar : SiteSetting . sso_overrides_avatar
)
end
user . create_single_sign_on_record! (
last_payload : unsigned_payload ,
external_id : external_id ,
external_username : username ,
external_email : email ,
external_name : name ,
external_avatar_url : avatar_url
2017-11-07 04:38:38 -06:00
)
end
2014-02-27 18:48:46 -06:00
end
2014-04-15 00:53:48 -05:00
2018-04-12 03:18:49 -05:00
user
end
2014-02-27 18:48:46 -06:00
end
2014-04-15 00:53:48 -05:00
2014-02-27 18:48:46 -06:00
def change_external_attributes_and_override ( sso_record , user )
2017-11-08 08:55:15 -06:00
if SiteSetting . sso_overrides_email && user . email != Email . downcase ( email )
2014-02-27 18:48:46 -06:00
user . email = email
2017-05-16 15:18:18 -05:00
user . active = false if require_activation
2014-02-27 18:48:46 -06:00
end
2014-04-15 00:53:48 -05:00
2018-03-13 17:29:11 -05:00
if SiteSetting . sso_overrides_username? && username . present?
2018-03-09 15:06:55 -06:00
if user . username . downcase == username . downcase
user . username = username # there may be a change of case
2018-03-13 17:29:11 -05:00
elsif user . username != username
2018-03-09 15:06:55 -06:00
user . username = UserNameSuggester . suggest ( username || name || email , user . username )
end
2014-02-27 18:48:46 -06:00
end
2014-04-15 00:53:48 -05:00
2015-08-23 19:24:09 -05:00
if SiteSetting . sso_overrides_name && user . name != name && name . present?
2015-05-06 22:52:26 -05:00
user . name = name || User . suggest_name ( username . blank? ? email : username )
2014-02-27 18:48:46 -06:00
end
2014-04-15 00:53:48 -05:00
2016-09-15 18:44:45 -05:00
avatar_missing = user . uploaded_avatar_id . nil? || ! Upload . exists? ( user . uploaded_avatar_id )
2015-01-28 09:47:59 -06:00
2016-09-15 18:44:45 -05:00
if ( avatar_missing || avatar_force_update || SiteSetting . sso_overrides_avatar ) && avatar_url . present?
2016-10-24 12:55:30 -05:00
avatar_changed = sso_record . external_avatar_url != avatar_url
2016-09-15 18:44:45 -05:00
2016-10-24 12:55:30 -05:00
if avatar_force_update || avatar_changed || avatar_missing
2017-01-18 23:22:24 -06:00
Jobs . enqueue ( :download_avatar_from_url , url : avatar_url , user_id : user . id , override_gravatar : SiteSetting . sso_overrides_avatar )
2016-10-24 12:55:30 -05:00
end
2014-08-19 02:49:14 -05:00
end
2014-02-27 18:48:46 -06:00
# change external attributes for sso record
sso_record . external_username = username
sso_record . external_email = email
sso_record . external_name = name
2014-08-19 02:49:14 -05:00
sso_record . external_avatar_url = avatar_url
2014-02-27 18:48:46 -06:00
end
2014-04-15 00:53:48 -05:00
end