Devise find by username or email - ruby-on-rails

after setup devise
i would like to get the user by username or email with in a api session
I passed login to parameters but rails spits out a error
User.find_for_database_authentication(:login => params[:username][:email])
the overwrite of find_for_database_authentication on model is already done.
def self.find_for_database_authentication(warden_conditions)
Someone has any hint to spare?
Devise
class Api::SessionsController < Devise::SessionsController
respond_to :json
def create
resource = User.find_for_database_authentication(:login => params[:username][:email])
if resource.valid_password?(params[:login][:password])
sign_in(:user, resource)
render :json=> {:auth_token=>resource.authentication_token, :email=>resource.email}, :status => :ok
return
end
invalid_login_attempt
end
end

I did this by overriding find_for_database_authentication like this:
class User
def self.find_for_database_authentication(auth_hash)
self.where("username = :query OR email = :query", query: auth_hash[:email]).first
end
end
Nothing more was needed.

Try this: User.find_by(username: 'username') or User.find_by(email: 'your#email.com')

Related

Devise confirm account and set password

In my app I am using Devise and Active Admin. Users are created in admin area without password, then they receive a mail with a link to a confirmation page where they can enter the password for the new account.
This is confirmation controller:
class ConfirmationsController < Devise::ConfirmationsController
def show
#original_token = params[:confirmation_token]
digested_token = Devise.token_generator.digest(self, :confirmation_token,params[:confirmation_token])
self.resource = resource_class.find_by_confirmation_token(digested_token) if params[:confirmation_token].present?
super if resource.nil? or resource.confirmed?
render :layout => "internal"
end
def confirm
digested_token = Devise.token_generator.digest(self, :confirmation_token, params[resource_name][:confirmation_token])
self.resource = resource_class.find_by_confirmation_token(digested_token) if params[resource_name][:confirmation_token].present?
if resource.update_attributes(params[resource_name].except(:confirmation_token).permit(:email, :password, :password_confirmation)) && resource.password_match?
self.resource = resource_class.confirm_by_token(params[resource_name][:confirmation_token])
set_flash_message :notice, :confirmed
sign_in_and_redirect(resource_name, resource)
else
#original_token = params[resource_name][:confirmation_token]
render :action => "show", :layout => "internal"
end
end
end
Relevant routes:
devise_for :users, :path_prefix => 'd', :controllers => {:confirmations => 'confirmations'}
devise_scope :user do
patch "/confirm" => "confirmations#confirm"
end
When the users clicks on the activation link in the background it sets the account as confirmed in the database, but instead of being redirected to confirmations/show to set the password, I get on this line render :layout => "internal"
Render and/or redirect were called multiple times in this action.
Please note that you may only call render OR redirect, and at most
once per action. Also note that neither redirect nor render terminate
execution of the action, so if you want to exit an action after
redirecting, you need to do something like "redirect_to(...) and
return".
Why is this happening?
In your ConfirmationsController:
class ConfirmationsController < Devise::ConfirmationsController
def show
#original_token = params[:confirmation_token]
digested_token = Devise.token_generator.digest(self, :confirmation_token,params[:confirmation_token])
self.resource = resource_class.find_by_confirmation_token(digested_token) if params[:confirmation_token].present?
# here you call super if resource is nil or confirmed
# super calls the show action of Devise::ConfirmationController
# it results with the first render (but doesn't call any return)
super if resource.nil? or resource.confirmed?
# after super is finished you continue and render layout internal
# (again, since it has happened in the parent controller already)
render :layout => "internal"
end
...
you can check what exactly is being done in Devise::ConfirmationsController on their github page: https://github.com/heartcombo/devise/blob/master/app/controllers/devise/confirmations_controller.rb
if you want to render render the confirmations/show page then just remove the
render layout: 'internal'
line.

DRY concepts in rails controllers

I'm working on my first rails api server.
I've got a controller for my User model that looks as such:
class UsersController < ApplicationController
def index
if current_user.admin?
#users = User.all
render json: #users
else
render json: { message: 'You do not have the appropriate permissions to access this resource' }, status: 401
end
end
def show
if User.exists?(#id)
#id = params[:id]
if current_user.id.to_s == #id || current_user.admin?
#user = User.find(#id)
render json: #user
else
render json: { message: 'You do not have the appropriate permissions to access this resource' }, status: 401
end
else
render json: { message: 'Requested resource not found' }, status: 404
end
end
end
What I want and currently have for these two controller methods is:
/users fetch all users only if the authenticated user making the request is of role admin
/users/:id fetch a user by id only if the authenticated user making the request has a matching id or is of role admin
The current implementation breaks the DRY philosophy. The reasoning is that the logic for handling whether or not the requesting user has the permissions to access the requested resource(s) is repeated across both controller methods. Furthermore, any model's controller method for show will repeat the logic for checking whether or not the requested resource exists. I also feel like this kind of implementation makes for fat controllers, where I'd rather them be skinny.
What I want to know from the community and from those that have solved this problem before; what is the best way to go about this in order to conform to the DRY philosophy and to keep controllers skinny.
Good to know: I'm using devise and devise-token-auth for authentication.
You need to use some kind of Authorization gem like cancancan. It is exactly what you need. Also it's else not elsif. elsif is followed by condition.
You can use github.com/varvet/pundit instead, for authorization.
It matches with the controller, instead of putting the authorization in the controller, you can use this to move out the authorization to another class.
I have used this across multiple Rails/Rails-API projects and didn't encounter a problem so far.
Instead of writing the code above. You can do this instead.
Also, prioritize early returns over nested ifs for readability.
In your controller.
class UsersController < ApplicationController
def index
authorize User # This will call the policy that matches this controller since this is UsersController it will call `UserPolicy`
#users = User.all
render :json => #users
end
def show
#user = User.find_by :id => params[:id] # Instead of using exists which query the data from db then finding it again, you can use find_by which will return nil if no records found.
if #user.blank?
return render :json => {:message => 'User not found.'}, :status => 404
end
authorize #user # This will call the policy that matches this controller since this is UsersController it will call `UserPolicy`
render :json => #user
end
end
In your Policy
class UserPolicy < ApplicationPolicy
def index?
#user.admin? # The policy is called in controller then this will check if the user is admin if not it will raise Pundit::NotAuthorizedError
end
def show?
#user.admin? || #record == #user # The policy is called in controller then this will check if the user is admin or the user is the same as the record he is accessing if not it will raise Pundit::NotAuthorizedError
end
end
In your ApplicationController
class ApplicationController < ActionController::API
include Pundit
rescue_from Pundit::NotAuthorizedError, :with => :show_forbidden
private
def show_forbidden exception
return render :json => {
:message => 'You are not authorized to perform this action.'
}, :status => 403
end
end

Password Reset Link doesn't work using Devise in rails

I have overridden the Devise' password controller in my rails application. But it's not working properly. On click of 'reset password link', sometimes call to password#create goes into the gem or sometimes it goes into the overridden controller. My controller looks like:
class Users::PasswordsController < Devise::PasswordsController
layout 'lunchbox/lunchbox', only: [:lunchbox_forgot, :lunchbox_recover]
def create
self.resource = resource_class.send_reset_password_instructions(resource_params)
if successfully_sent?(resource)
respond_with({}, :location => after_sending_reset_password_instructions_path_for(resource_name))
else
flash[:error] = "Email not found"
redirect_path = params[:user][:lunchbox] ? lunchbox_forgot_password_path : new_user_password_path
redirect_to redirect_path
end
end
end

How to implement a "Remember Me" function in Rails 3?

What are the best practices to implement a "Remember Me" function in Rails 3 application ?
I store session information (session id + user id) in the database when user logs in, and I don't want to use any plugins at this moment.
Any pointers or code samples will be much appreciated.
You can just set the expiration on a signed cookie to accomplish this. (Signed cookies are tamper-proof just like the Rails-provided session cookie.)
class SessionsController < ApplicationController
def create
...
user = User.authenticate(params[:email_address], params[:password])
if params[:remember_me]
cookies.signed[:user_id] = { value: user.id, expires: 2.weeks.from_now }
else
# expires at the end of the browser session
cookies.signed[:user_id] = user.id
end
end
def destroy
cookies.delete :user_id
end
end
class ApplicationController < ActionController::Base
...
def current_user
User.find(cookies.signed[:user_id])
end
end
Railscasts has an episode on achieving this as well as well as a great HOWTO on implementing those features via BDD with RSpec and Capybara.
I store session information (session id + user id) in the database when user logs in
I believe that's one approach and the casts above does the same with cookies by issuing each User account a unique authentication token.
Have been reading the Rails tutorial book and it has an implementation for Remember Me
You can check for some hints (The implementation may be different from yours)
http://ruby.railstutorial.org/book/ruby-on-rails-tutorial#sec:remember_me
This is how I implemented remember_me (the below snippet is from my example Rails app on authentication):
class SessionsController < ApplicationController
skip_before_filter :login_required, :only => [:new, :create]
def new
end
def create
#current_user = User.authenticate(params[:email], params[:password])
if #current_user
#current_user.track_on_login(request)
if params[:remember_me]
cookies[:remember_token] = { :value => #current_user.remember_token, :expires => 24.weeks.from_now }
else
cookies[:remember_token] = #current_user.remember_token
end
redirect_to dashboard_url, :notice => "Logged in successfully."
else
flash.now[:alert] = "Invalid login or password."
render 'new'
end
end
def destroy
current_user.track_on_logout
current_user.reset_remember_token_and_save # can't rely on the 'save_current_user_if_dirty' after_filter here
cookies.delete(:remember_token)
reset_session
redirect_to root_url, :notice => "You have been logged out."
end
end
Just an example without salt:
class ApplicationController < ActionController::Base
protected
def signin!(user_id)
return unless user_id
#current_user = User.find(user_id)
self.session_user_id = #current_user.id
self.permanent_user_id = #current_user.id if session[:accept_remember_me]
end
def signout!
self.session_user_id = nil
self.permanent_user_id = nil
session[:accept_remember_me] = nil
#current_user = nil
end
def remember_me
session[:accept_remember_me] = true
end
private
def permanent_user_id
cookies.signed[:permanent_user_id]
end
def permanent_user_id= value
cookies.permanent.signed[:permanent_user_id] = value
end
def session_user_id
session[:user_id]
end
def session_user_id= value
session[:user_id] = value
end
end

Failure to Redirect on user Sign-in

I have a rails app . I have created a sessionscontroller and want to redirect to users page '/users' once the user signs in. But the redirect doesnt seem to be happening.
class SessionsController < ApplicationController
def create
user = User.find_or_create_by_fbid(params[:user][:fbid]) #...Success
user.update_attributes(params[:user]) #....Sucess
sign_in(user) # ....This occurs successfully
redirect_to users_path # .... Redirect doesnt occur on the browser side
end
end
The sign_in method is defined inside the Application Controller
class ApplicationController < ActionController::Base
def sign_in(user)
session[:fbid] = user.fbid
#current_user = user
end
end
Server Logs below . The redirect actually seems to be happening on the server side . But I do not see any change on the client side. The browser doesnt change page.
The UsersController
class UsersController < ApplicationController
def index
#users = User.all
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #users }
end
end
end
Original Ajax Post -
$.post("/sessions",{user:{name:profile.name, email:profile.email,fbid:profile.id}});
The redirect occurs successfully if I use a javascript redirect statement inside the $post() as a callback function .
$.post("/sessions",{user:{name:profile.name, email:profile.email,fbid:profile.id}},function( data ) {
window.location="/users";
}
);
You need to handle the response's redirection in an ajax query. Normally the browser handles, but it won't with ajax.
From this SO question (edited slightly for your case)
var params = {user:{name:profile.name, email:profile.email,fbid:profile.id}};
$.ajax({
type: "POST",
url: "/sessions",
data: params,
dataType: "json",
success: function(data, textStatus) {
if (data.redirect) {
// data.redirect contains the string URL to redirect to
window.location.href = data.redirect;
}
else {
// data.form contains the HTML for the replacement form
$("#login_form").replaceWith(data.form);
}
}
});
I'm using a real basic authentication based of Ryan Bates' screencast. If you can't figure it out from my code, I recommend watching Authentication from Scratch
applications_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery
helper_method :current_user
private
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
end
sessions_controller.eb
class SessionsController < ApplicationController
def new
end
def create
user = User.authenticate(params[:email], params[:password])
if user
session[:user_id] = user.id
redirect_to root_url, :notice => "Logged in!"
else
flash.now.alert = "Invalid email or password"
render "new"
end
end
def destroy
session[:user_id] = nil
redirect_to root_url, :notice => "Logged out!"
end
end
Ajax Post -
$.post("/sessions",{user:{name:profile.name, email:profile.email,fbid:profile.id}});

Resources