Devise log in with one more condition to check - ruby-on-rails

I´m using devise gem in a rails 4 app and I have in my user table a column called valid that by default is false, when the user registers in the site it should send me a email with the information about them and approve it, and put that valid column in true. So then in the log in action it will check that valid is true and let them login to the site.
My question is how modify that login action that takes care of the valid column in users table.

You should look at adding :confirmable to your User model, it may take care of most of what you are looking to do.
Otherwise, if you want to modify whether someone can login, look at the wiki on how to customize account validation.
From the wiki:
def active_for_authentication?
# Uncomment the below debug statement to view the properties of the returned
# self model values.
# logger.debug self.to_yaml
super && account_active?
end

Related

Rails devise reset password fails if user record contains invalid data

I have an existing application in production where we have added new mandatory fields to the user record. As a result, the forgot password function is failing. I believe the failure occurs in the following method in recoverable.rb
# Update password saving the record and clearing token. Returns true if
# the passwords are valid and the record was saved, false otherwise.
def reset_password(new_password, new_password_confirmation)
if new_password.present?
self.password = new_password
self.password_confirmation = new_password_confirmation
save
else
errors.add(:password, :blank)
false
end
end
The new attributes cannot be generated mechanically. Only the user himself will know the correct values. Does anyone know of a way to get around this problem?
The only idea that I've come up with so far is as follows:
use a rake script to populate the new fields with values that I know would never occur in real life but would be accepted by the model validation rule
when the user logs in, detect if the user record contains invalid data and force them to do an update. In the user edit form, I would use some javascript to change the bogus values to blank.
You could use a custom validation context to make sure the validations on these added fields are only run when a user edits their profile, but skipped when (for example) Devise saves the user record.
In the User model, add an on:-clause to these validations
validates_presence_of :recently_added_mandatory_field, on: :user_updates_profile
And in the appropriate controller action, add this validation context when calling #valid? or #save:
# Existing code uses `#user.valid?` -> change to:
if #user.valid?(:user_updates_profile)
…
# Existing code uses `#user.save` -> change to:
if #user.save(context: :user_updates_profile)
…
See here for a full description of validation contexts: https://api.rubyonrails.org/classes/ActiveModel/Validations/ClassMethods.html#method-i-validates

how to implement second level authentication in rails?

I have a devise authentication already setup in my application.Now i want to implement second level authentication in rails. i want to hard code the email and password in coding in controllers. i want to compare this email and password with the email and password provided by the user. rather than saving in database and picking for second level authentication.,Because these 5 to 6 links are going to be used by only one person in the company at any point of time. i want to implement this for a 5 to 6 actions in my application in controller. how can i implement this? i checked a gem gem 'devise-authy' but it seems it sends a password to mobile for otp authentication. i dont want otp authentication. i want a second level authentication with the same email as devise email but the password should be different from the devise original password.(like in devise table i want to take one more column second_level_password.) is there any gem for this kind of requirement or is this manually coded. if it is manually coded then how can i do that?
You can use before_action in rails controller.
Add following line of code in your controller.
before_action :second_level_access
private
def second_level_access
#You can use your logic to get password
unless current_user.second_level_password == params[:second_level_password]
# signout to user because user is already logged in with devise.
redirect_to session_path
end
end
EDIT
You can use
attr_accessor_with_default :second_level_logged_in, false
add following line to application controller:
before_action :logged_in_with_second_level, if: 'current_user and !second_level_logged_in'
def logged_in_with_second_level
redirect_to 'second_level_logged_in_form_page'
end
in create action of second_level_logged_in
add code current_user.second_level_logged_in=true

How do I change user status along with Devise actions

I'm using Rails 4.1 with Devise 3.2.4
I'm using Devise's email confirmation to setup user accounts, but I also need do things after email confirmation such as setup billing information before an account can be considered active.
How do I manage a user's status along with Devise's mechanism?
I.e. when a user first signs up, I want their status to be "new". When they confirm their email, I want their status changed to "email_confirmed", and when they submit payment information, I want it to change to "active". I also want to account for when they change their email address -- their status needs to return to "new".
Devise uses "confirmation_token" field in users table. You can use it to set all of you conditions. You can add a boolean field in users table lets say 'active'(default false). When a new user signs up "confirmation_token" field contains your token so you could check for its not nil condition but we don't need user to be active now so 'active' field will remain false. Now when a user clicks on the link in email, devise changes the "confirmation_token" field to nil so again you could check it but we still don't want the user to be active so your 'active' field will remain false.
Now if you look at devise github repo when a user clicks on the confirmation token in email, your app is redirected to confirmation controllers show action.
In your case what you can do is redirect the user to your payment information page with a notice saying something like "you need to complete payment information to do transactions" or whatever you may like. You'll have to override devise after_confirmation_path method for that something like:
# The path used after confirmation.
def after_confirmation_path_for(resource_name, resource)
if signed_in?(resource_name)
signed_in_root_path(resource)
else
# your payment information page path
end
end
And when user completes the payment information details successfully you can set his/her active field to true and build on from there.

Setting Pundit role for user from Devise Registrations New View / Controller

I have both Pundit and Devise setup and working correctly in my rails app. However I am unsure about how to let the user decide their role when signing up.
At the moment:
I have a URL param which is passed to the Devise new view.
In the form_for I set a hidden field called role to the value of the param.
This works.
But I am concerned that a malicious user could change this param to say "Admin" and now they are an admin.
How should I handle this? I don't want to put a restriction in the model as that will cause issues when I want to create an admin. Should I override the devise registrations controller to put a check in there?
You don't need to override Devise's RegistrationsController for what you're trying to do.
If you want admins to be able to create users that have an arbitrary role set, you could simply use your own controller. Devise still makes it easy to create a user yourself, so you'll just have to make a controller handling this. Of course, don't forget to protect it using Pundit so only admins can use this functionality.
This approach still works if you use the Confirmable module. As no confirmation e-mail will be sent on user creation, though, you'll either have to call user.confirm! after saving the model to immediately unlock the account, or manually send the confirmation e-mail using user.send_confirmation_instructions.
Edit:
This Pundit policy may or may not work for what you're trying to do. You will have to override the create action of Devise's RegistrationsController here in order to use Pundit's authorize method. For dryness' sake, you should also move the roles list elsewhere, perhaps into the model.
class UserPolicy < Struct.new(:current_user, :target_user)
def create?
registration_roles.include?(target_user.role) if current_user.nil?
end
private
def registration_roles
%w(RED BLU Spectator)
end
end
After a fair amount of googling I have an answer. First stick some validation in your model for the roles Active Record Validations Guide: See 2.6 inclusion: validator option
After this your roles are validated to ensure they are correct, you could of course have a lookup table as well. Then you have two options:
Use a conditional before_save Callback for new records. Then check if the new record has the role your protecting and if so raise an error. To catch later (in an overridden devise controller (see second option).
Override the Devise registrations controller See this SO question. And put some checks in a completely overridden create action. Use the session to store the url param passed to the new action (also needs to be completely overridden). Then if the create action fails and redirects to new you still have access to the role in the session (as the param will be cleared from the URL unless you manipulate it).
So either way you need to override the registrations controller, its just a case of how much.
I suspect there is a way to do this with just Pundit. But I have yet to be able to get it to work.

rails devise hook into on_login

I'm looking to hook into devise after login / after session create. How would I go about doing this?
Basically I want to set a users location every time they login, and to do that I need an after login hook of sorts.
Devise is built on Warden, so you can use Warden's after_authentication hook.
Put this in an initializer:
Warden::Manager.after_authentication do |user,auth,opts|
# do something with user
end
The remote IP address and other request info is stored in auth.request (i.e. auth.request.remote_ip).
See https://github.com/hassox/warden/wiki/callbacks
Devise updates the value of the user.current_sign_in_at timestamp on successful login. So, you could simply add a before_save filter to your User model. In that filter, check to see if the value of this field has changed, and if it has, set the users location.
BTW - I'm not sure what you mean by "location" - if you mean IP address, Devise already stores that for you.
Here's a page from the devise wiki: How To: Redirect to a specific page on successful sign in.
In summary, the recommendation is to add the following method to the application controller:
app/controllers/application_controller.rb
def after_sign_in_path_for(resource)
custom_location_for(resource) || welcome_path
end
In the above code, resource means the object (user, account, etc) that you've implemented devise authentication for. (The object that has the devise_for in your routes.)

Resources