1
0
Fork 0
forked from OpenNeo/impress
impress/app/models/auth_user.rb
Emi Matchu 3eeb5d1065 Actually create user from NeoPass authentication! <3 <3
Whew, exciting! Still done nothing against the live NeoPass server, but
we've got this fully working with the development server, it seems!
Wowie!!

This is all still hidden behind secret flags, so it's fine to deploy
live. (And it's not actually a problem if someone gets past to the
endpoints behind it, because we haven't actually set up real
credentials for our NeoPass client yet, so authentication will fail!)

Okay time to lie down lol.
2024-03-14 19:11:06 -07:00

85 lines
No EOL
2.9 KiB
Ruby

class AuthUser < AuthRecord
self.table_name = 'users'
devise :database_authenticatable, :encryptable, :registerable, :validatable,
:rememberable, :trackable, :recoverable, :omniauthable,
omniauth_providers: [:neopass]
validates :name, presence: true, uniqueness: {case_sensitive: false},
length: {maximum: 20}
has_one :user, foreign_key: :remote_id, inverse_of: :auth_user
# It's important to keep AuthUser and User in sync. When we create an AuthUser
# (e.g. through the registration process), we create a matching User, too. And
# when the AuthUser's name changes, we update User to match.
#
# TODO: Should we sync deletions too? We don't do deletions anywhere in app
# right now, so I'll hold off to avoid leaving dead code around.
after_create :create_user!
after_update :sync_name_with_user!, if: :saved_change_to_name?
def create_user!
User.create!(name: name, auth_server_id: 1, remote_id: id)
end
def sync_name_with_user!
user.name = name
user.save!
end
def uses_omniauth?
provider? && uid?
end
def email_required?
!uses_omniauth?
end
def password_required?
!uses_omniauth?
end
def self.from_omniauth(auth)
raise MissingAuthInfoError, "Username missing" if auth.uid.blank?
raise MissingAuthInfoError, "Email missing" if auth.info.email.blank?
transaction do
find_or_create_by!(provider: auth.provider, uid: auth.uid) do |user|
# Use the Neopets username if possible, or a unique username if not.
dti_username = build_unique_username_like(auth.uid)
user.name = dti_username
# Copy the email address from their Neopets account to their DTI
# account, unless they already have a DTI account with this email, in
# which case, ignore it. (It's primarily for their own convenience with
# password recovery!)
email_exists = AuthUser.where(email: auth.info.email).exists?
user.email = auth.info.email unless email_exists
end
end
end
def self.build_unique_username_like(name)
name_query = sanitize_sql_like(name) + "%"
similar_names = where("name LIKE ?", name_query).pluck(:name)
# Use the given name itself, if we can.
return name unless similar_names.include?(name)
# If not, try appending "-neopass".
return "#{name}-neopass" unless similar_names.include?("#{name}-neopass")
# After that, try appending "-neopass-1", "-neopass-2", etc, until a
# unique name arises. (We don't expect this to happen basically ever, but
# it's nice to have a guarantee!)
max = similar_names.size + 1
candidates = (1..max).map { |n| "#{name}-neopass-#{n}"}
numerical_name = candidates.find { |name| !similar_names.include?(name) }
return numerical_name unless numerical_name.nil?
raise "Failed to build unique username (shouldn't be possible?)"
end
class MissingAuthInfoError < ArgumentError;end
end