I have a Rails 3.2.x application using Devise.
As per standard Devise feature, a user can delete his account (there is a button to click on the change passsword screen).
I want to prevent a user of deleting his account if he has made previous transactions in the system.
So I did the following in the user model
before_destroy do
# Only delete if no financial transactions exist
return false if Trxhistory.where(:user_id=>self.id).select(:id).count() != 0
end
It works, the user gets deleted if no transactions exist and the user does not get deleted if a transaction exists. BUT: in case the user is not deleted, the screen still shows the deletion success message rather than the error message.
To have such customization. I believe that you will have to overwrite the devise registrations controller.
The good news is that is not hard. all you have to do is generate a new controller on your app that will be the new registration controller for that user. for example.
bundle exec rails generate controller users/registrations
It will have to inherit from Devise:RegistrationsController
class Users::RegistrationsController < Devise::RegistrationsController
end
lastly you will have to reference this controller as the new registration controller at your routes.rb file just like the devise page says
devise_for :users, controllers: { registrations: "users/registrations"}
now you can implement your custom rule for that resource. i.e
class Users::RegistrationsController < Devise::RegistrationsController
def destroy
if resource.didsomething
redirect_to root_path, flash: { error: "You can't delete your account" }
else
super
end
end
end
the method above is overwriting the default destruction method of devise and calling the original one only if the condition is not met.
Related
Users have to confirm their email account via a link sent to them after registration through devise's confirmable.
Right now if a user who has not confirmed their account gets the unhelpful error:
Your email or password is incorrect.
I have tried adding the below to the SessionsController:
def create
user = User.where(email: params[:user][:email]).take
unless user.confirmed?
super
end
flash[:alert] = "Your account has not been confirmed."
end
My routes are
devise_for :users, path: 'users', controllers: {
sessions: "users/sessions"
}
However, signing in as any user (confirmed or not), seems to bypass this entire method. (Even if I put in binding.pry.)
UPDATE:
Devise only ignores the unique create method. If I put in a binding.pry in any other method (new or delete), devise will catch it.
The sessions create doesn't get called if the user isn't confirmed, so this is why you're not able to reach the create method.
Try this
class User < ActiveRecord
protected
def confirmation_required?
false
end
end
This will allow the user to proceed to the create action in sessions_controller and you can test there if the user is actually confirmed.
I am using Devise for my users in my rails app.
In my user model i have column randomID, this column is uquinesse for each user
and i generate it when the user sign-up
In the user model i have also addBY, i use that because the user need the randomID of another user for sign-up
So i will have tree relation between all users
all works fine, but my brobleme now is if the user enter the wrong randomID,
i must check if exist in my DATABASE or not
if the value exist i let the user sign-up
else i display a message error
any ideas
Simply call a function to check for a value existence before your create action.
in your users_controller.rb:
class UsersController < ApplicationController
before_action :check_for_token, only: :create
...
private
def check_for_token
redirect_to root_path unless User.exists?(:randomID => params[:addBY])
end
end
I want to track user sessions in my Rails app. In my Devise Sessions controller, I can track when a session is created... But that only tracks when a user specifically submits the login form. And I'm wanting to track the number of times a user returns to the app (most of the time, they won't need to resubmit the login form).
Where can I put my tracking code to achieve this?
Here's my sessions controller:
class SessionsController < Devise::SessionsController
def create
super do
mixpanel.track("Sign In")
end
end
def destroy
super do
mixpanel.track("Sign Out")
end
end
def new
super
end
end
To clarify:
user#gmail.com visits myapp.com and logs in to the webapp using password/email login (hits sessions#create)
The next day the user returns to myapp.com and does not need to manually login because the app has kept them logged in
The above should register as 2 "sessions" for this user so I am trying to figure out where I can fire the events.
I am not sure that I understand very well what you want but I will try.
In config/routes.rb you have different routes. You should find root to: 'home#index' or something like that. In this case, you can add the tracking code in HomeController#index. When the user loads the web app, it will open this endpoint, so you can track the event there.
In application_controller.rb, make a custom before_action, like the following:
class ApplicationController < ActionController::Base
before_action :check_visit
private
def check_visit
if current_user.nil?
# You've got a visit that wasn't logged in
else
# You've got a visit that was logged in
end
end
end
We can't just put it in home#index or whatever your root is, since the might have a different page bookmarked (for example).
In my Rails project I have different types of users one of which has the user_status :admin, which has full rights to edit content unlike the rest of the users. For obvious reasons I want to add additional security for these types of users, in particular, completely disable password recovery.
What is the correct way of overriding standard Devise password recovery (:recoverable Devise module) methods so that when a user tries to get a reset password link for a user which is an admin user (user_status == "admin") the system gives back the "standard email not found" message?
This is somewhat like the unanswered question: Restrict Devise password recovery to only certain users
Thank you in advance.
The method I chose and that worked for me was overriding the send_reset_password_instructions method of the User model by adding the following to models/user.rb:
def send_reset_password_instructions
return false if self.user_status == 'admin'
super
end
This makes Devise not do anything in case the email belongs to an admin account.
For any future viewers, here's another way to do it. Vitaly's example did work for me, but I was still getting the "Your password email has been sent." notice (I wanted a separate alert to flash), so I went another route.
Extending the Devise::PasswordsController was the easiest solution for me:
class Devise::Extends::PasswordsController < Devise::PasswordsController
def create
if some_condition?
redirect_to :root
flash[:alert] = 'You cannot reset your password, buddy.'
else
super
end
end
Then, in routes.rb:
devise_for :users, controllers: { passwords: 'devise/extends/passwords' }
That will direct your app to the extended controller, then hit the devise controller ("super") if your condition is not met.
Not tested, but I think you can overwrite the reset_password! in the User model as follows:
def reset_password!(new_password, new_password_confirmation)
return false if user_status == 'admin'
super
end
This prevents the password from being reset if the user is an admin.
I don't know if this is the best method to override, there are more devise recoverable methods that are candidate to be overwritten in your User model, ie send_reset_password_instructions. Check the manual for all the interesting methods.
Snippet above from Keller Martin works pretty well!
Some minor issues I faced are the following:
If you got uninitialized constant Devise::Extends (NameError) (probably it's just due to old ruby version?) then you can just use nested modules definition.
If you need to allow some action to run for non authenticated user then you can skip the filter.
Below is updated snippet.
module Devise
module Extends
class PasswordsController < Devise::PasswordsController
skip_before_filter :authenticate_user!, :only => [ :edit ]
def edit
redirect_to "https://www.google.com/"
end
end
end
end
I'm working on a rails 3.2 app that authenticates using devise.
For a goal that is not related to the application (mostly statistics) I need to create a separate users table (let's call it alt_users) that only holds some fields of the users table (name, email, and some other fields) but no password digests and other sensitive infos. Also this records don't have to be modified if a user modifies his account or deletes it.
My idea is that when a user signs up, before devise makes his job some selected fields are inserted in the alt_users table.
What is the correct way to override devise behavior in order to make this happen?
What you can do is to override Devise's RegistrationsController in the following way:
In your routes.rb:
devise_for :users, :controllers => {:registrations => "registrations"}
Then create the file app/controllers/registrations_controller.rb :
# app/controllers/registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
def create
# add your data to alt_users here
super
end
end
On a side note, if you can avoid overriding Devise's controller it would be best.
Try to think of other options like a before_create callback on the User model.