mirror of
https://github.com/discourse/discourse.git
synced 2024-11-30 23:55:19 +08:00
df3886d6e5
This commit introduces a new site setting "google_oauth2_hd_groups". If enabled, group information will be fetched from Google during authentication, and stored in the Discourse database. These 'associated groups' can be connected to a Discourse group via the "Membership" tab of the group preferences UI. The majority of the implementation is generic, so we will be able to add support to more authentication methods in the near future. https://meta.discourse.org/t/managing-group-membership-via-authentication/175950
209 lines
4.8 KiB
Ruby
209 lines
4.8 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,
|
|
:omit_username, # Used by plugins to prevent username edits
|
|
:skip_email_validation,
|
|
:destination_url,
|
|
:omniauth_disallow_totp,
|
|
:failed,
|
|
:failed_reason,
|
|
:failed_code,
|
|
:associated_groups
|
|
]
|
|
|
|
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,
|
|
:omit_username,
|
|
:name,
|
|
:authenticator_name,
|
|
:extra_data,
|
|
:skip_email_validation,
|
|
:associated_groups
|
|
]
|
|
|
|
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.symbolize_keys
|
|
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? && username.present?
|
|
change_made = UsernameChanger.override(user, username)
|
|
end
|
|
|
|
if SiteSetting.auth_overrides_email && email_valid && email.present? && user.email != Email.downcase(email)
|
|
user.email = email
|
|
change_made = true
|
|
end
|
|
|
|
if SiteSetting.auth_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
|
|
end
|
|
|
|
def can_edit_username
|
|
!(SiteSetting.auth_overrides_username || omit_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
|
|
username || name || email
|
|
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)
|
|
end
|
|
end
|