Customizing Devise gem (Rails) - ruby-on-rails

I am trying to add a new field "registration code" along with the email/password in sign up form. (i have the registration code field in a separate database, only a valid registration code/email pair will have to work with the sign up)
I could not able to find any controller for actions done by devise gem.
How do i customize devise to achieve this?
Thanks in advance.

It seems like your question basically has nothing to do with Devise itself (besides the views). To validate your registration code/email pairs, you surely need to add this as validation.
The easy way to validate registration code could be:
class User
validate :validate_registration_code
private
def validate_registration_code
reg_code = RegistrationCode.find_by_code(registration_code)
unless reg_code.email == record.email
errors.add(:registration_code, "Invalid registration code for #{record.email}")
end
end
end
You also might want to write simple custom validator:
class RegistrationCodeValidator < ActiveModel::Validator
def validate(record)
# actual reg code validation
# might look like:
reg_code = RegistrationCode.find_by_code(record.registration_code)
unless reg_code.email == record.email
record.errors[:registration_code] << "Invalid registration code for #{record.email}"
end
end
end
# in your User model
class User
# include registration code validator
include RegistrationCodeValidator
validates_with MyValidator
end

Devise keeps it's controllers behind the scenes inside the gem. If you want to add an action or modify one, you have to subclass it and do a little work in routes to get the action to your controller.
However you shouldn't need to do that to add a field. See goshakkk's answer

Related

Devise reconfirmation hook

This question is very similar to Rails Devise: after_confirmation except I'm looking for more specifically a reconfirmation hook
My use case : I need to synchronise a new user email on some third party service (Intercom to be precise).
I have a first implementation using an API where I have put the logic I need there (all tidied up in a service)
However, I often end up doing a lot of maintenance using the console, and the most obvious things that come to mind is to perform
user.email = 'newemail#example.com'
user.confirm # or skip_reconfirmation
user.save
Using this, I do not fire my resynchronisation logic automatically. Is there a way to force some reconfirmation callback ? overriding after_confirmation does not seem to work
Not really an answer, but in the meantime I have monkeypatched my user class with the following (where xxx represents some custome methods added on the user class to sync to third party services the new email)
class User
include User::Console if defined?('Rails::Console')
end
module User::Console
extend ActiveSupport::Concern
included do
## Override Devise confirmable to add warnings
def confirmation_warning
puts "WARNING !!".red
puts "calling `#confirm` or `#skip_reconfirmation!` on the user does not sync to xxx !\n"\
'Please either use [Name of custom confirmation service], '\
'or use `user.sync_to_xxx` to propagate changes'.red
end
def confirm
confirmation_warning
super
end
def skip_reconfirmation!
confirmation_warning
super
end
end
end
I have the exact same use case, where I need to sync a new user email on a third party service.
The way I solved it is a before_update filter on the User model, and using email_changed?:
class User < ApplicationRecord
before_update :update_email_in_third_party_service
private
def update_email_in_third_party_service
return unless self.valid? && self.email_changed?
# We passed our check, update email in the third party service (preferably in a background job)
end
end

How can I automatically create an adress parameter upon sign up with Devise (Ruby On Rails)

I'm currently trying to automatically create an user_address (which will be a randomly generated hash, which is for now hardcoded) string upon sign-up with the Ruby on Rails devise Gem. I have added the user_address to the list of allowed parameters I am currently trying to add this logic to the registrations_controller.rb by adding the following method :
def create
super do
current_user.user_address = '1F1tAaz5x1HUXrCNLbtMDqcw6o5GNn4xqX'
end
end
I suppose this is because current_user must not be defined before the actual POST (create user) has been processed but I am not sure about the way to do this.
Any help would be greatly appreciated, thanks in advance
If i understand you correctly (I think I do) you could move away from trying to do this in the create action in the controller and instead use a model callback on the User model.. that way its automatically created when a user registers.
example:
class User < ApplicationRecord
before_create :assign_user_address
validates_uniqueness_of :user_address
def assign_user_address
begin
self.user_address = SecureRandom.hex
other_user = User.find_by(user_address: self.user_address)
end while other_user
end
end
the before_create will generate the user_address and assign it to the user that is registering, while the validates_uniqueness_of ensures that you will never have a duplicate user address all in one fell swoop (although with a random string the chances of duplicating are slim.. but better to be safe than sorry.. I find this method super easy and keeps your controller clean and un cluttered.
Let me know if this wasn't what you were looking for..

Rails devise user function

How do I execute a particular function after a user has signed up.
(I wanted to add to one of my associations, I already have it coded in a non-devise rails but now I need it here)
Device has provided helper action 'after_sign_in_path_for' you can override it within your application controller.
def after_sign_in_path_for(resource_or_scope)
.... #write your customize code or call any method
end
For sign up it would look like:
def after_sign_up_path_for(resource_or_scope)
if resource_or_scope.is_a? User # and perhaps other conditions
#... do something, go somewhere
else
super
end
end
Ofc. Assuming that your Devise user model is called User.
You can use the after_create callback in your User model.
The following guide has tons of examples: http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html

Devise - How do I forbid certain users from signing in?

I am using Devise for authentication in my application.
How do I forbid certain users from signing in - kind of disable a user?
Do it like this:
Create a column called is_active for the User model.
Then add the code below to the User model:
class User < ActiveRecord::Base
#this method is called by devise to check for "active" state of the model
def active_for_authentication?
#remember to call the super
#then put our own check to determine "active" state using
#our own "is_active" column
super and self.is_active?
end
end
UPDATE
As Matt Huggins notes, the method is now called active_for_authentication? (Documentation)
Add a column to the User model: allowed_to_log_in.
Then add this to /app/models/user.rb:
def active_for_authentication?
super and self.allowed_to_log_in?
end
If you want to inform the user with a custom message you can add this as well:
def inactive_message
"You are not allowed to log in."
end
I think that is quite important because the standard message from Devise says:
"Your account is not activated yet."
That is confusing for users and the real reason is that you have "banned" them from logging in.
You want to do authorization, not authentication. Devise only does authetication, though.
I.e. devise only tells you that a user is who he says he is.
You need something else to forbid him from using the site.
Authorization is a popular topic and there's a whole list of gems that can help you with it:
http://ruby-toolbox.com/categories/rails_authorization.html
Take your pick.
Sounds like you may be interested in cancan
https://github.com/ryanb/cancan
http://railscasts.com/episodes/192-authorization-with-cancan

Modify confirmation with Devise

I would like to set a boolean flag upon user confirmation via Devise. Essentially I want to set the user as 'active'. However, Devise simply confirms the account and logs them in.
How can I create a callback of some sorts to update my user record to set that 'active' column to true upon confirmation?
Any help very appreciated!
Presuming that your authentication model is called User, you can do this:
class User < ActiveRecord::Base
def active?
super and (not self.confirmed_at.nil?)
end
end
With this, Devise will not login the user but will wait until the user confirms (the confirmed_at field will be non-NULL if a user has confirmed)
For your particular question, you're better off implementing your active? attribute as confirmed_at being nil, as suggested by Zabba.
But here is how to do what you're asking, since it may be helpful to people trying to set other values on the user after confirmation.
class Users::ConfirmationsController < Devise::ConfirmationsController
def show
# let Devise actually confirm the user
super
# if they're confirmed, it will also log them in
if current_user then
# and you can do whatever you want with their record
current_user.active = true
end
end
end
This is basically a comment on Turadg's Answer below. If you follow that suggestion (which I did) you will have a small problem when users attempt to use an invalid confirmation_token. You will get a "Missing template users/confirmations/new". What the Devise::ConfirmationsController is doing here is sending you to devise/confirmations/new to inform you the token is invalid and allow you to send another one.
Since I had already customized the Devise views, what I ended up doing to get around this minor issue is moving the devise/confirmations/new.html.haml file into the now expected location under user/confirmations/new.html.haml.

Resources