I am using devise to manage user authentication in my rails app. Devise is really great for that.
However I have a special requirement for my application: A user must be whitelisted before he can register as a User.
So there is a admin which creates a list of allowed emails. A user registers with a email and if the email is in the whitelist table he will be registered. If however, the mail is not in the whitelist, the registration should be aborted with a message like "You are not yet invited".
Do you have an idea how that could be solved with devise?
Thanks in advance.
I would just use model validation. I'm assuming your User class has the devise method
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable #etc
before_validation :whitelisted
def whitelisted
unless celebrityemail.include? email
errors.add :email, "#{email} is not on our invitation list"
end
end
end
What you can do is create your own registrations controller and extend the device one like:
class MyRegistrationController < Devise::RegistrationsController
def create
# do your checks
super
end
end
see: https://github.com/plataformatec/devise/blob/master/app/controllers/devise/registrations_controller.rb
And: https://github.com/plataformatec/devise/wiki/How-to:-Customize-routes-to-user-registration-pages
Good luck!
I did create my own controller as suggested:
class Users::RegistrationsController < Devise::RegistrationsController
def create
email = params[:user][:email]
if Admin::Whitelist.find_by_email(email) != nil
super
else
build_resource
set_flash_message :error, "You are not permitted to sign up yet. If you have already payed your registration fee, try again later."
render_with_scope :new
end
end
end
I placed it in app/users/registrations_controller.rb. Then I had to copy the devise registration views into app/views/users/registrations because the default views were not used.
It is working now, thanks for your help
Related
In my application I have users and sites (think of it like users and organizations). There's a lookup table the sits between them called SiteUser. In SiteUser:
belongs_to :site
belongs_to :user
before_validation :set_user_id, if: ->() { email != nil }
def set_user_id
existing_user = User.find_by(email: email)
self.user = if existing_user.present?
UserMailer.notify_existing_user(site, existing_user).deliver_now unless Rails.env.test?
existing_user
else
User.invite!(email: email)
end
end
I need the subject of the email that gets generated to include the site name. "Example Company has invite you to join their site!"
And in the email body I also need the site title.
I am confused on how to get the site parameter over to devise invitable. As you can see in the code above, if the user who is being invited to the site already exists in our system, I use my own mailer in which I pass in the site and the existing_user so I have access to it in my mailer view.
class UserMailer < ApplicationMailer
def notify_existing_user(site, user)
#site = site
#user = user
mail to: #user.email, subject: "You've been given access to #{#site.title} in the Dashboard."
end
end
I cannot figure out how to accomplish this similarly with the devise invitable mailer, which is used when the user doesn't already exist in the system.
Any help you could provide would be incredibly appreciated!
The way I went about accomplishing this was to add a temporary/virtual attribute on User called invite_site_name. Because the problem was that site wasn't an attribute of User, so I could not send site in to User.invite!.
class User < ActiveRecord::Base
attr_accessor :invite_site_name
and then in my CustomDeviseMailer I had access to it:
class CustomDeviseMailer < Devise::Mailer
helper :application # gives access to all helpers defined within `application_helper`.
include Devise::Controllers::UrlHelpers # Optional. eg. `confirmation_url`
default template_path: 'devise/mailer' # to make sure that your mailer uses the devise views
def invitation_instructions(record, token, opts={})
opts[:subject] = "#{record.invite_site_name} has invited you to their Dashboard! Instructions inside!"
super
end
end
You could always override devise's subject_for method. Found this on the gems issues, that also suggests another way:
https://github.com/scambra/devise_invitable/issues/660#issuecomment-277242853
Hope this helps!
I am using Devise for authentication, it's working fine. My users table contains a role attribute. I want to not allow login for users whose role field is empty or nil.
How can I do that?
One way to conditionally allow user login is by overriding Devise's #active_for_authentication? method:
class User < ActiveRecord::Base
def active_for_authentication?
super && role.present?
end
end
Hope this helps!
You can validate this on the ApplicationController, like:
before_action :validate_user_role
...
def validate_user_role
flash[:alert] == "User don't have a Role"
redirect_to login_path if current_user.role.blank?
end
...
In my Rails app I use Devise for authentication. Now I want to extend the login screen with an extra field where the user needs to fill in a value only he knows, like the date when he started at the company or something.
How can I add this extra check, besides the regular email and password fields, to the Devise authentication check process?
I read something about the active_for_authentication? which you can extend in the User model:
def active_for_authentication?
super && special_condition_is_valid?
end
Is this the correct way to do this?
Edit: In the end I have overwritten the SessionsController:
class SessionsController < Devise::SessionsController
protected
def after_sign_in_path_for(resource)
if resource.is_a?(User) && !resource.correct_token?(params[:user][:security_token])
sign_out resource
flash[:error] = I18n.t('.devise.failure.invalid_token')
root_path
else
super
end
end
end
This was inspired on the solution here: Rails + Devise - Is there a way to BAN a user so they can't login or reset their password?
The only issue I have left is that when an invalid token is submitted, both the success and error message are visible...
I would simple generate the views:
bundle exec rails generate devise:views
This will copy the devise views into your app/views folder.
After that you can modify the app/views/devise/sessions/new.html.erb file and extend it with your form fields.
After that you can overwrite the devise controller.
With the Confirmable module enabled, Devise will not allow an unconfirmed user to sign in after a predefined period of time has elapsed. Instead the user is redirected back to the sign in page with the flash message "You have to confirm your account before continuing".
This is an undesirable interaction model, as a flash notice does not provide adequate space to properly explain to the user why access has been denied, what "confirm your account" means, provide a link to resend the confirmation, and instructions on how to check your spam folder and so on.
Is there a way I can change this behaviour to redirect to a specific URL instead?
Sorry at first I thought you meant after Sign Up not Sign In. So the down below works for how to direct users after Sign Up and what you need to do for Sign In is to create a custom Devise::FailureApp
See the wiki page: https://github.com/plataformatec/devise/wiki/How-To:-Redirect-to-a-specific-page-when-the-user-can-not-be-authenticated
Then within your custom FailureApp overwrite redirect_url method from https://github.com/plataformatec/devise/blob/master/lib/devise/failure_app.rb:
def redirect_url
if warden_message == :unconfirmed
custom_redirect_path
else
super
end
end
For custom redirect after Sign Up:
There is a controller method after_inactive_sign_up_path_for within the RegistrationsController that you can overwrite to accomplish this.
First in your Routes you will need to specify to use your custom controller:
config/routes.rb:
devise_for :users, :controllers => { :registrations => "users/registrations" }
Second you create your custom controller that inherits from the normal controller in order to overwrite the method:
app/controllers/users/registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
protected
def after_inactive_sign_up_path_for(resource)
signed_up_path
end
end
In this case for my App my Devise model is User so you may want to change that namespace if your model is named differently. I wanted my users to be redirected to the signed_up_path, but you can change that to your desired path.
I just did this, but took a different approach.
in app/controllers/sessions_controller.rb:
class SessionsController < Devise::SessionsController
before_filter :check_user_confirmation, only: :create
#
# other code here not relevant to the example
#
private
def check_user_confirmation
user = User.find_by_email(params[:email])
redirect_to new_confirmation_path(:user) unless user && user.confirmed?
end
end
This worked for me and seemed minimally invasive. In my app new sessions always have to go through sessions#create and users always sign in with their email address, so this may be a simpler case than yours.
You can of course redirect_to any location you desire in the check_user_confirmation method. new_confirmation_path was the logical choice for me because it provides users with the resources to get confirmed.
This is my solution you need to add :unconfirmed message on devise locales below the sessions.
in app/controllers/sessions_controller.rb
def check_user_confirmation
user = User.where(email: params[:user][:email]).take
unless user && user.confirmed?
set_flash_message! :alert, :unconfirmed
expire_data_after_sign_in!
respond_with user, location: after_inactive_sign_up_path_for(user)
end
end
protected
def after_inactive_sign_up_path_for(resource)
new_user_session_path
end
How can I send a welcoming email to the user when they sign up? I'm using the Devise gem for authentication. SMTP is already set up. I just need to understand how to extend devise to send emails.
NOTE - this is not confirmation email!
UPD Solution:
class User < ActiveRecord::Base
after_create :send_welcome_email
private
def send_welcome_email
UserMailer.deliver_welcome_email(self)
end
end
Add a callback (after_create ) in the model or observer to send the email using normal mailer methods.
FYI, in Rails 3 it's:
class User < ActiveRecord::Base
after_create :send_welcome_email
private
def send_welcome_email
UserMailer.welcome_email(self).deliver
end
end