mirror of
https://gh.wpcy.net/https://github.com/discourse/discourse.git
synced 2026-05-01 04:07:14 +08:00
When `use_email_for_username_and_name_suggestions` is disabled, the username suggester was falling back to generic usernames like "user1", "user2", etc. These suggestions are rarely helpful as they create indistinct usernames that users often accept without modification, resulting in forums populated with "user763", "user764", etc. This change adds an `allow_generic_fallback` option to `UserNameSuggester.suggest()`. When set to false, it returns nil instead of falling back to generic "userN" usernames. The OAuth authentication flow now uses this option, leaving the username field blank so users must choose their own meaningful username. Ref - https://meta.discourse.org/t/391542
212 lines
5.3 KiB
Ruby
212 lines
5.3 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class Auth::Result
|
|
ATTRIBUTES = %i[
|
|
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 = %i[
|
|
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)
|
|
raise ArgumentError, "email_valid should be boolean or nil" if !val.in? [true, false, nil]
|
|
@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
|
|
|
|
# refreshes automatic group membership (despite the name, it doesn't change TLs)
|
|
Group.user_trust_level_change!(user.id, user.trust_level) if user
|
|
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
|
|
return { requires_invite: true } if requires_invite
|
|
|
|
return { suspended: true, suspended_message: user.suspended_message } if user&.suspended?
|
|
|
|
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)
|
|
@staged_user = User.where(staged: true).find_by_email(email) if email.present? && email_valid
|
|
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,
|
|
allow_generic_fallback: false,
|
|
)
|
|
end
|
|
end
|