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