Разработать пригласительный токен с моделью пользователей
У меня есть модель User, ограниченная site_id. Когда devise пытается найти приглашенного пользователя по токену, site_id не включается
class User < ApplicationRecord
belongs_to :site
default_scope { where(site_id: Site.current_id) }
devise :invitable, :confirmable, :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable
validates_uniqueness_of :email, scope: :site
validates_format_of :email, with: Devise::email_regexp
end
Когда я использую ссылку приглашения и devise пытается найти пользователя по токену приглашения, site_id имеет значение NULL
SELECT `users`.* FROM `users` WHERE `users`.`site_id` IS NULL AND `users`.`invitation_token` = 'acomplicatedtoken' ORDER BY `users`.`id` ASC LIMIT 1
Как включить devise для распознавания пользовательской области?
2 ответа
Мой подход заключается в переопределении invitations_controller.rb. Я взглянул на файл invitations_controller.rb по умолчанию, встроенный в devise_invitable ( https://github.com/scambra/devise_invitable/blob/master/app/controllers/devise/invitations_controller.rb), и просто настроил before_actions.
routes.rb
devise_for :users, controllers: { invitations: 'users/invitations' }
/app/controllers/users/invitations_controller.rb
class Users::InvitationsController < Devise::InvitationsController
skip_before_action :resource_from_invitation_token, only: [:edit]
before_action :scoped_resource_from_invitation_token, only: [:edit]
private
def scoped_resource_from_invitation_token
unless params[:invitation_token] && self.resource = User.find_by_invitation_token(params[:invitation_token], true)
set_flash_message(:alert, :invitation_token_invalid) if is_flashing_format?
redirect_to after_sign_out_path_for(resource_name)
end
end
end
# self.resource = resource_class.find_by_invitation_token
# is now
# self.resource = User.find_by_invitation_token
Более подробно:
class Users::InvitationsController < Devise::InvitationsController
skip_before_action :has_invitations_left?, :only => [:create]
skip_before_action :resource_from_invitation_token, :only => [:edit, :destroy]
before_action :scoped_has_invitations_left?, :only => [:create]
before_action :scoped_resource_from_invitation_token, :only => [:edit, :destroy]
if respond_to? :helper_method
helper_method :after_sign_in_path_for
end
# GET /resource/invitation/new
def new
self.resource = User.new
render :new
end
# POST /resource/invitation
def create
self.resource = invite_resource
resource_invited = resource.errors.empty?
yield resource if block_given?
if resource_invited
if is_flashing_format? && self.resource.invitation_sent_at
set_flash_message :notice, :send_instructions, :email => self.resource.email
end
if self.method(:after_invite_path_for).arity == 1
respond_with resource, :location => after_invite_path_for(current_inviter)
else
respond_with resource, :location => after_invite_path_for(current_inviter, resource)
end
else
respond_with_navigational(resource) { render :new }
end
end
# GET /resource/invitation/accept?invitation_token=abcdef
def edit
set_minimum_password_length
resource.invitation_token = params[:invitation_token]
render :edit
end
# PUT /resource/invitation
def update
raw_invitation_token = update_resource_params[:invitation_token]
self.resource = accept_resource
invitation_accepted = resource.errors.empty?
yield resource if block_given?
if invitation_accepted
if Devise.allow_insecure_sign_in_after_accept
flash_message = resource.active_for_authentication? ? :updated : :updated_not_active
set_flash_message :notice, flash_message if is_flashing_format?
sign_in(resource_name, resource)
respond_with resource, :location => after_accept_path_for(resource)
else
set_flash_message :notice, :updated_not_active if is_flashing_format?
respond_with resource, :location => new_session_path(resource_name)
end
else
resource.invitation_token = raw_invitation_token
respond_with_navigational(resource){ render :edit }
end
end
# GET /resource/invitation/remove?invitation_token=abcdef
def destroy
resource.destroy
set_flash_message :notice, :invitation_removed if is_flashing_format?
redirect_to after_sign_out_path_for(resource_name)
end
protected
def invite_resource(&block)
User.invite!(invite_params, current_inviter, &block)
end
def accept_resource
User.accept_invitation!(update_resource_params)
end
def current_inviter
authenticate_inviter!
end
def scoped_has_invitations_left?
unless current_inviter.nil? || current_inviter.has_invitations_left?
self.resource = User.new
set_flash_message :alert, :no_invitations_remaining if is_flashing_format?
respond_with_navigational(resource) { render :new }
end
end
def scoped_resource_from_invitation_token
unless params[:invitation_token] && self.resource = User.find_by_invitation_token(params[:invitation_token], true)
set_flash_message(:alert, :invitation_token_invalid) if is_flashing_format?
redirect_to after_sign_out_path_for(resource_name)
end
end
def invite_params
devise_parameter_sanitizer.sanitize(:invite)
end
def update_resource_params
devise_parameter_sanitizer.sanitize(:accept_invitation)
end
def translation_scope
'devise.invitations'
end
end