discourse/lib/auth/result.rb
David Taylor 485fc4636a
FEATURE: Add fallback to suggested value when auth_overrides_username (#16612)
If the identity provider does not provide a precise username value, then we should use our UserNameSuggester to generate one and use it for the override. This makes the override consistent with initial account creation.
2022-05-04 11:22:09 +01:00

219 lines
5.3 KiB
Ruby

# frozen_string_literal: true
class Auth::Result
ATTRIBUTES = [
:user,
:name,
:username,
:email,
:email_valid,
:extra_data,
:awaiting_activation,
:awaiting_approval,
:authenticated,
:authenticator_name,
:requires_invite,
:not_allowed_from_ip_address,
:admin_not_allowed_from_ip_address,
:skip_email_validation,
:destination_url,
:omniauth_disallow_totp,
:failed,
:failed_reason,
:failed_code,
:associated_groups,
:overrides_email,
:overrides_username,
:overrides_name,
]
attr_accessor *ATTRIBUTES
# These are stored in the session during
# account creation. The user cannot read or modify them
SESSION_ATTRIBUTES = [
:email,
:username,
:email_valid,
:name,
:authenticator_name,
:extra_data,
:skip_email_validation,
:associated_groups,
:overrides_email,
:overrides_username,
:overrides_name,
]
def [](key)
key = key.to_sym
public_send(key) if ATTRIBUTES.include?(key)
end
def initialize
@failed = false
end
def email
@email&.downcase
end
def email_valid=(val)
if !val.in? [true, false, nil]
raise ArgumentError, "email_valid should be boolean or nil"
end
@email_valid = !!val
end
def failed?
!!@failed
end
def session_data
SESSION_ATTRIBUTES.map { |att| [att, public_send(att)] }.to_h
end
def self.from_session_data(data, user:)
result = new
data = data.with_indifferent_access
SESSION_ATTRIBUTES.each { |att| result.public_send("#{att}=", data[att]) }
result.user = user
result
end
def apply_user_attributes!
change_made = false
if (SiteSetting.auth_overrides_username? || overrides_username) && (resolved_username = resolve_username).present?
change_made = UsernameChanger.override(user, resolved_username)
end
if (SiteSetting.auth_overrides_email || overrides_email || user&.email&.ends_with?(".invalid")) &&
email_valid &&
email.present? &&
user.email != Email.downcase(email)
user.email = email
change_made = true
end
if (SiteSetting.auth_overrides_name || overrides_name) && name.present? && user.name != name
user.name = name
change_made = true
end
change_made
end
def apply_associated_attributes!
if authenticator&.provides_groups? && !associated_groups.nil?
associated_group_ids = []
associated_groups.uniq.each do |associated_group|
begin
associated_group = AssociatedGroup.find_or_create_by(
name: associated_group[:name],
provider_id: associated_group[:id],
provider_name: extra_data[:provider]
)
rescue ActiveRecord::RecordNotUnique
retry
end
associated_group_ids.push(associated_group.id)
end
user.update(associated_group_ids: associated_group_ids)
AssociatedGroup.where(id: associated_group_ids).update_all("last_used = CURRENT_TIMESTAMP")
end
end
def can_edit_name
!(SiteSetting.auth_overrides_name || overrides_name)
end
def can_edit_username
!(SiteSetting.auth_overrides_username || overrides_username)
end
def to_client_hash
if requires_invite
return { requires_invite: true }
end
if user&.suspended?
return {
suspended: true,
suspended_message: user.suspended_message
}
end
if omniauth_disallow_totp
return {
omniauth_disallow_totp: !!omniauth_disallow_totp,
email: email
}
end
if user
result = {
authenticated: !!authenticated,
awaiting_activation: !!awaiting_activation,
awaiting_approval: !!awaiting_approval,
not_allowed_from_ip_address: !!not_allowed_from_ip_address,
admin_not_allowed_from_ip_address: !!admin_not_allowed_from_ip_address
}
result[:destination_url] = destination_url if authenticated && destination_url.present?
return result
end
result = {
email: email,
username: resolve_username,
auth_provider: authenticator_name,
email_valid: !!email_valid,
can_edit_username: can_edit_username,
can_edit_name: can_edit_name
}
result[:destination_url] = destination_url if destination_url.present?
if SiteSetting.enable_names?
result[:name] = name.presence
result[:name] ||= User.suggest_name(username || email) if can_edit_name
end
result
end
private
def staged_user
return @staged_user if defined?(@staged_user)
if email.present? && email_valid
@staged_user = User.where(staged: true).find_by_email(email)
end
end
def username_suggester_attributes
attributes = [username]
attributes << name if SiteSetting.use_name_for_username_suggestions
attributes << email if SiteSetting.use_email_for_username_and_name_suggestions
attributes
end
def authenticator
@authenticator ||= Discourse.enabled_authenticators.find { |a| a.name == authenticator_name }
end
def resolve_username
if staged_user
if !username.present? || UserNameSuggester.fix_username(username) == staged_user.username
return staged_user.username
end
end
UserNameSuggester.suggest(*username_suggester_attributes, current_username: user&.username)
end
end