Permalink
Fetching contributors…
Cannot retrieve contributors at this time
118 lines (103 sloc) 4.04 KB
require 'securerandom'
require 'active_model'
require 'active_support'
require 'base32'
# :nodoc: namespace
module Authpwn
# Included by the model class that represents users.
#
# Parts of the codebase assume the model will be named User.
module UserModel
extend ActiveSupport::Concern
included do
# Externally-visible user ID.
#
# This is decoupled from "id" column to avoid leaking information about
# the application's usage.
validates :exuid, presence: true, length: 1..32, uniqueness: true
# Credentials used to authenticate the user.
has_many :credentials, dependent: :destroy, inverse_of: :user,
autosave: true
validates_associated :credentials
# Automatically assign exuid.
before_validation :set_default_exuid, on: :create
end
# Class methods on models that include Authpwn::UserModel.
module ClassMethods
# Scope using the value returned by User#to_param.
#
# @param [String] param value returned by User#to_param
# @return [ActiveRecord::Relation]
def with_param(param)
where(exuid: param)
end
# Authenticates a user given the information on a signup form.
#
# The easiest method of accepting other login information is to override
# this method, locate the user's email, and supply it in a call to super.
#
# @param [Session] signin the information entered in the sign-in form
# @return [User, Symbol] the authenticated user, or a symbol indicating the
# reason why the authentication failed
def authenticate_signin(signin)
Credentials::Password.authenticate_email signin.email, signin.password
end
# Looks up the User tat may be related to an OmniAuth sign-in.
#
# This method is called when there is no Credential matching the OmniAuth
# information, but before {User#create_from_omniauth}. It is an opportunity
# to identify an existing user who uses a new sign-in method.
#
# The default implementation finds an user whose e-mail matches the 'email'
# value in the OmniAuth hash.
#
# @param [Hash] omniauth_hash the hash provided by OmniAuth
# @return [User] the user who should be signed in, or nil if no such user
# exists
def related_to_omniauth(omniauth_hash)
info_hash = omniauth_hash['info']
return nil unless email = info_hash && info_hash['email']
credential = Credentials::Email.with email
credential and credential.user
end
# Change this to customize on-demand user creation on OmniAuth signup.
#
# This method is called when there is no existing user matching the
# OmniAuth information, and is responsible for creating a user. It is an
# opportunity to collect the OmniAuth information to populate the user's
# account.
#
# The default implementation creates a user with the e-mail matching the
# 'email' key in the OmniAuth hash. If no e-mail key is present, no User is
# created.
#
# @param [Hash] omniauth_hash the hash provided by OmniAuth
# @return [User] a saved User, or nil if the OmniAuth sign-in information
# should not be used to create a user
def create_from_omniauth(omniauth_hash)
info_hash = omniauth_hash['info']
return nil unless email = info_hash && info_hash['email']
user = User.new
user.credentials << Credentials::Email.new(email: email, verified: true)
user.save!
user
end
end # module Authpwn::UserModel::ClassMethods
# Checks if a credential is acceptable for authenticating a user.
#
# Returns nil if the credential is acceptable, or a String containing a
# user-visible reason why the credential is not acceptable.
def auth_bounce_reason(crdential)
nil
end
# Use e-mails instead of exposing ActiveRecord IDs.
def to_param
exuid
end
# :nodoc: sets exuid to a (hopefully) unique value before validations occur.
def set_default_exuid
self.exuid ||=
Base32.encode(SecureRandom.random_bytes(16)).downcase.sub(/=*$/, '')
end
end # namespace Authpwn::UserModel
end # namespace Authpwn