Rails 3 Devise Update Password Without Logging Out - ruby-on-rails-3

I'm Using Devise in my Rails 3.0.9 application for User Authentication. As I wanted to be able to manage Users, I created the following Users Controllers:
class UsersController < ApplicationController
def index
#users = User.all
end
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
flash[:notice] = "Successfully created User."
redirect_to users_path
else
render :action => 'new'
end
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
params[:user].delete(:password) if params[:user][:password].blank?
params[:user].delete(:password_confirmation) if params[:user][:password].blank? and params[:user][:password_confirmation].blank?
if #user.update_attributes(params[:user])
if current_user.update_with_password(params[:user])
sign_in(current_user, :bypass => true)
end
flash[:notice] = "Successfully updated User."
redirect_to users_path
else
render :action => 'edit'
end
end
def destroy
#user = User.find(params[:id])
if #user.destroy
flash[:notice] = "Successfully deleted User."
redirect_to users_path
end
end
end
I this works for showing, creating and deleting Users, but I have run into a problem when updating the passwords.
When I Update the password for the currently logged in account it automatically logs me out.
In the controller I tried to fix this using: (you can see it in the code above)
if current_user.update_with_password(params[:user])
sign_in(current_user, :bypass => true)
end
But that gives me this error ->
undefined method `update_with_password' for nil:NilClass
What I'm really looking for, is the ability to update any accounts password, without logging them out ( as admins have ability to change regular users password ).

It is not necessary to write
This code in the controller
if current_user.update_with_password(params[:user])
sign_in(current_user, :bypass => true)
end
Instead you should go ahead with below one
if #user.update_attributes(params[:user])
sign_in(current_user, :bypass => true)
redirect_to users_path
end
cheers :)

The easiest way to do this is call
sign_in(current_user, :bypass => true)
After the update.
This is what my controller action looks like:
def update_password
if current_user.update_with_password(params[:user])
sign_in(current_user, bypass: true)
flash[:notice] = "Updated Password Successfully"
else
flash[:error] = "There was an error updating your password, please try again."
end
end
I think this is basically what #challenge proposed but I just wanted to make a little cleaner and easier to understand.

Related

How do I emulate logging in for controller tests?

I have a SearchesController that requires a user to be logged in before it will do its thing.
I'd like to write an rspec helper function login to emulate logging in for controller tests. (NB: I will handle integration / requests specs separately.) My attempts so haven't worked: the logged_in? method in ApplicationController returns false.
The question: how do I write the 'login' helper?
Here's the RSpec controller test:
# file: spec/controllers/searches_controller_spec.rb
require 'spec_helper'
require 'controllers_helper'
describe SearchesController do
include ControllersHelper
describe "GET index" do
it 'without login renders login page' do
get :index
response.should redirect_to(login_path)
end
it 'with login finds searches belonging to user' do
me = FactoryGirl.create(:user)
my_searches = FactoryGirl.create_list(:search, 2, :user => me)
not_me = FactoryGirl.create(:user)
not_my_searches = FactoryGirl.create_list(:search, 2, :user => not_me)
login(me) # want to define this in spec/controllers_helper.rb
get :index
assigns(:searches).should =~ my_searches
end
end
end
Here's the Controller:
# file: app/controllers/searches_controller.rb
class SearchesController < ApplicationController
def index
unless logged_in?
redirect_to login_path, :alert => "You must be logged in to access this page."
else
#searches = Search.where(:user_id => current_user.id)
respond_to do |format|
format.html
format.json { render json: #searches }
end
end
end
end
And here's the ApplicationController code. Note that current_user = x has the effect of logging x in, and it's rather simple: it sets #current_user and session[:user_id].
# file: app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery
force_ssl
protected
def current_user
#current_user ||= User.find_by_id(session[:user_id])
end
def current_user=(user)
#current_user = user
session[:user_id] = user && user.id
end
def logged_in?
!!#current_user
end
def require_login
unless logged_in?
redirect_to login_path, :alert => "You must be logged in to access this page."
end
end
helper_method :current_user, :logged_in?, :require_login
end
I may have said this before, but if Stack Overflow gave badges answering one's own questions, I'd have a LOT of badges! :)
Okay, to answer this question you need to look at the documentation for ActionController::TestCase. When you do so, you'll find that it sets up bindings for:
#controller
#request
#response
So for the specific controller given in the OP, writing the login method is trivial:
# file: spec/controllers_helper.rb
module ControllersHelper
def login(user)
#controller.send(:current_user=, user)
end
end
(Did I hear someone say RTFM again? I thought so...)

Show form errors when updating an object from a different controller

I have a form in an account_settings controller that updates a user object through the users_controller. The error messages are not getting passed through. Here's a look at the code.
account_settings/account.html.haml
= form_for #user, :url => { :controller => "users", :action => "update", :id => #user.id } do |f|
- if #user.errors.any?
.alert.alert-error
%h4
Please address the following errors:
%ul
- #user.errors.full_messages.each do |msg|
%li= msg
form stuff...
account_settings_controller
def account
#user = current_user
end
users_controller
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
flash[:notice] = "User was successfully updated."
end
redirect_to :back
end
The form will not update but there are no error messages getting passed back. Any thoughts?
This happens because you are returning a redirecting. The #user object does not persist across the redirect. You should be doing something like:
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
flash[:notice] = "User was successfully updated."
redirect_to :back
else
render :edit
end
end
Here, we redirect when the update is successful, but if not, we render the edit action. Hence we have access to #user, and your errors would be present in #user.errors

Rails 3 error in Safari only - ActiveRecord::RecordNotFound (Couldn't find User with auth_token = ):

In following Railscast #274 to get reset password working in my Rails 3 app, I am experiencing a weird issue in Safari. If I run my app in Heroku I get the following error when I go to my root:
ActiveRecord::RecordNotFound (Couldn't find User with auth_token = ):
app/controllers/application_controller.rb:39:in `lookup_user'
app/controllers/application_controller.rb:32:in `current_user'
app/controllers/application_controller.rb:54:in `logged_in?'
app/controllers/users_controller.rb:8:in `new'
If use Firefox and Chrome (in incognito mode) it works. In Safari, I found that if I get the error, I can make it go away by navigating to /logout. Then the page renders perfectly.
Here's my route for /logout and root:
match "/logout" => "sessions#destroy", :as => "logout"
root :to => "users#new"
Here's my destroy action in sessions_controller:
def destroy
reset_session
cookies.delete(:auth_token)
redirect_to root_path, :notice => "You successfully logged out"
end
My application_controller:
protected
def current_user
#current_user ||= lookup_user
end
def lookup_user
if session[:user_id]
User.find_by_id(session[:user_id])
elsif cookies[:auth_token]
User.find_by_auth_token!(cookies[:auth_token])
end
end
And lastly, here's my new action in users_controller:
def new
#user = User.new
#user.profile = Profile.new
if logged_in?
redirect_to profile_path(current_user)
end
end
What I've tried:
To alter the new action to delete cookies with the following:
def new
#user = User.new
#user.profile = Profile.new
if logged_in?
redirect_to profile_path(current_user)
elsif
cookies.delete(:auth_token)
end
end
The rake task below, as suggested in the Railscast comments:
namespace :user do
desc "Rebuild Auth-Tokens"
task :rebuild_auth_token => :environment do
User.transaction do
User.all.each { |u|
u.generate_token(:auth_token)
u.save!
}
end
end
end
(I ran this with `heroku run rake user:rebuild_auth_token`)
Neither seems to have worked. Can anyone help me figure this out?
Anytime you regenerate the user :auth_code's you will need to delete your cookies for that domain. In a production, you should not regenerate :auth_codes and you will never have this issue, unless users edit their cookies.
In addition I have posted a response on the railscast.com authentication (revised) solution so Ryan can take a look at it.
Good luck!

Rails 3 NoMethod Error - undefined method for profile in Profiles#edit with CanCan

In my Rails 3 app, I'm getting redirected to login during my signup process. The steps to signup are supposed to be:
User creates User and Profile
Upon saving user, user is logged into the app and redirected to Profiles#edit (/signup/join)
Upon saving profile, user is redirect to Profiles#show (/profiles/:id)
I'm getting redirected to /login after step 1, and I'm seeing a 302 error after the redirect. If I comment out my before_filter :authenticate in profiles_controller.rb and redo the steps above I don't get redirected out of /signup/join but I get the following error:
NoMethodError in ProfilesController#edit
undefined method `profile' for nil:NilClass
I'm pointed to the first line of my Profiles#edit action:
def edit
#profile = user.profile
if #profile.higher_ed?
higher_ed = HigherEd.find_or_create_by_name(:name => #profile.higher_ed)
end
if #profile.employer?
employer = Employer.find_or_create_by_name(:name => #profile.employer)
end
render :layout => "join_form"
end
I've been making an attempt to implement CanCan in my app, so I thought that was the cause. However I commented out my entire ability.rb file and the problem persists. I'd obviously like to figure out how to fix this without commenting out the before_filter. So if anyone has an idea I'd greatly appreciate it. Since I'm dealing with CanCan which depends on a current_user, I'll start with the definition of current_user in my application_controller.rb:
protected
# Returns the currently logged in user or nil if there isn't one
def current_user
return unless session[:user_id]
#current_user ||= User.find_by_id(session[:user_id])
#current_user ||= User.find_by_auth_token!(cookies[:auth_token]) if cookies[:auth_token]
end
# Make current_user available in templates as a helper
helper_method :current_user
Here's my users_controller.rb:
class UsersController < ApplicationController
before_filter :authenticate, :only => [:edit, :update, :index]
layout "application"
def new
#user = User.new
#user.profile = Profile.new
if logged_in?
redirect_to current_user.profile
end
end
def create
#user = User.new(params[:user])
if #user.save
session[:user_id] = #user.id
redirect_to join_path, :notice => 'User successfully added.'
UserMailer.registration_confirmation(#user).deliver
else
render :action => 'new'
end
end
My profiles_controller.rb:
class ProfilesController < ApplicationController
#before_filter :authenticate, :only => [:edit, :update]
helper_method :find_or_create_group
layout "application", :except => [:edit, :show]
def new
#profile = Profile.new(params[:profile])
end
def create
#profile = Profile.new(params[:profile])
if #profile.save
redirect_to #user.profile, :notice => 'User successfully added.'
else
render :new
end
if #profile.higher_ed?
HigherEd.find_or_create_by_name(:name => #profile.higher_ed)
end
if #profile.employer?
Employer.find_or_create_by_name(:name => #profile.employer)
end
if #profile.job_title?
JobTitle.find_or_create_by_name(:name => #profile.job_title)
end
if #profile.high_school?
HighSchool.find_or_create_by_name(:name => #profile.high_school)
end
end
def user
#user = current_user
end
def edit
#profile = user.profile
if #profile.higher_ed?
higher_ed = HigherEd.find_or_create_by_name(:name => #profile.higher_ed)
end
if #profile.employer?
employer = Employer.find_or_create_by_name(:name => #profile.employer)
end
render :layout => "join_form"
end
My sessions_controller.rb:
class SessionsController < ApplicationController
def new
end
def create
if user = User.authenticate(params[:email].downcase, params[:password])
session[:user_id] = user.id
cookies.permanent[:auth_token] = user.auth_token
if user.profile.higher_ed?
redirect_to user.profile, :notice => "Logged in successfully"
else
redirect_to join_path, :notice => "Logged in successfully"
end
else
flash.now[:alert] = "Invalid login/password. Try again!"
render :action => 'new'
end
end
def destroy
reset_session
cookies.delete(:auth_token)
redirect_to root_path, :notice => "You successfully logged out"
end
end
My ability.rb for CanCan:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new guest user
if user.role? :admin
can :manage, :all
else
can :manage, :all
end
end
end
My routes.rb:
match "/signup/join" => "profiles#edit", :as => 'join'
#profile = user.profile
Try changing the above line to
#profile = #current_user.profile
or
#profile = current_user.profile
The issue has nothing to do with cancan, rather it has to do with "user" being nil in your controller.
I got it working by reworking my current_user logic. It's now:
def current_user
#current_user ||= lookup_user
end
def lookup_user
if cookies[:auth_token]
User.find_by_auth_token!(cookies[:auth_token])
elsif session[:user_id]
User.find_by_id(session[:user_id])
end
end
That seems to have done the trick.

How can I add a custom column in authlogic?

I've created a simple user system with authlogic using Rails 3. I want to add an "account_type" column to the database. How can I populate this column in the database when the user signs up? I have very very little experience with Rails.
Update: So I know somewhere in "#user = User.new(params[:user])" I need to add :account_type = "user". What's the right syntax for this though?
Here's what I have in my user controller:
class UserController < ApplicationController
before_filter :require_no_user, :only => [:new, :create]
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
flash[:notice] = "Registration successful!"
redirect_to root_url
else
render :action => :new
end
end
end
Thanks in advance!