Devise: change password - devise

I've been stuck on this for over 24hrs trying to follow other solutions posted here, but I can't get this to work. I'm new to Rails and need help!
I want to get my /users/edit page working so that I can simply change a users password. Originally, I wanted to do it without current_password but I don't mind leaving it in there as long as I can get the password changing and updating.
Here's what I did:
I followed the example in the Devise Wiki and inserted it into my Users controller which I specified to inherit from Devise::RegistrationsController
class UsersController < Devise::RegistrationsController
...
end
I changed my routes:
devise_for :users, :controllers => { :registrations => 'users' } do
match '/users' => 'users#index'
end
And here's my model:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
attr_accessor :password, :password_confirmation, :current_password
attr_accessible :email, :password, :password_confirmation, :current_password, :remember_me, :full_name, :coach, :bio
validates :full_name, presence: true
end
I was assuming the UsersController I created would override the Registrations controller and that I would be able to change/update password. It works to the extent the redirect to root_path happens (which is only meant to happen after updating without current password) but the new password is not saved (I checked the logs and there was no SQL to show it was saved)...
Any ideas?

Try doing something similar to:
Devise Forgot Password for logged in user
This lets you have a separate view for changing the password.
The key is that I couldn't ever get it working in devise, so I wrote my own solution in the users controller and post to that instead of using the methods provided by devise.
add this to your user form where you want to be able to edit the password:
<%= form_for(#user, :url => url_for(:action => :do_reset_password) , :html => { :method => :post }) do |f| %>
<%= f.hidden_field :reset_password_token %>
<div><%= f.label :password, "New password" %><br />
<%= f.password_field :password %></div>
<div><%= f.label :password_confirmation, "Confirm new password" %><br />
<%= f.password_field :password_confirmation %></div>
<div><%= f.submit "Change my password" %></div>
<% end %>
users controller:
def do_reset_password
id = params[:id]
# there may be a better way of doing this, devise should be able to give us these messages
if params[:user][:password] != params[:user][:password_confirmation]
flash[:alert] = "Passwords must match."
redirect_to :back
return
end
if #user.reset_password!(params[:user][:password],params[:user][:password_confirmation])
#user.save
respond_to do |format|
format.html { redirect_to '/home', notice: 'Your password has been changed.' }
end
else
flash[:alert] = "Invalid password, must be at least 6 charactors."
redirect_to :back
end
end
config/routes.rb
resource :users do
post 'do_reset_password'
end

Related

Updating a model from another controller

I would like to update the "User" model which has a column named "verification_code_confirmation" from the "Verifications" Controller
I had tried writing this code
class VerificationController < ApplicationController
def index
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
redirect_to #user, :flash => { :success => "Successfully verified." }
else
render :action => 'edit'
end
end
and
index.html.erb for verifications view.
<%= form_for(#user) do |f| %>
<div><%= f.label :verification_code_confirmation %><br />
<%= f.number_field :verification_code_confirmation %></div>
<% end %>
User model
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me, :verification_code_confirmation, :phone
end
end
I have updated to
if #user.update_attributes(params[:user])
How do i do it? I have looked around in the internet to update my column through the verification controller but i couldn't.

Unpermitted parameters. Devise + Oauth. Rails 4

Rails 4, Devise 3.0.3, Oauth-facebook
I've added two additional parameters in my User model - :name, :uid and trying to save it in my users table from my form (route /users/sign_up). But as result i receive record in table, which contains only default values for fields :name and :uid instead of values, which i put in my text_fields.
In console i've got the following message:
Unpermitted parameters: name, uid
WARNING: Can't mass-assign protected attributes for User: password_confirmation
app/models/user.rb:31:in `new_with_session'
Here is my user.rb model. I've tried to remove these fiels from attr_accessible but it gives no result.
class User < ActiveRecord::Base
attr_accessible :oauth_expires_at, :oauth_token, :oauth_secret, :email, :password, :uid, :provider, :name
default_scope -> {order('created_at ASC')}
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:omniauthable, :omniauth_providers => [:facebook]
has_many :microposts, :primary_key => "uid", dependent: :destroy
# validates :uid, presence: true
def self.find_for_facebook_oauth(auth, signed_in_resource=nil)
user = User.where(:provider => auth.provider, :uid => auth.uid).first
unless user
user = User.create(name:auth.extra.raw_info.name,
provider:auth.provider,
uid:auth.uid,
email:auth.info.email,
password:Devise.friendly_token[0,20]
)
end
user
end
def self.new_with_session(params, session)
super.tap do |user|
if data = session["devise.facebook_data"] && session["devise.facebook_data"]["extra"]["user_hash"]
user.email = data["email"]
end
end
end
end
Here is my users/omniauth_callbacks_controller.rb (without facebook method). I've tried to apply different advises related to before_filter, but it still does not work
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
before_filter :configure_permitted_parameters
def create
super
end
private
def configure_permitted_parameters
params.require(:user).permit(:name, :uid, :provider)
end
end
Here is my view-form ()
Sign up
<%= link_to "Sign in with Facebook", user_omniauth_authorize_path(:facebook) %>
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<div><%= f.label :name %> <br />
<%= f.text_field :name, :autofocus => true %></div>
<div><%= f.label :email %><br />
<%= f.email_field :email %></div>
<div><%= f.label :password %><br />
<%= f.password_field :password %></div>
<div><%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %></div>
<div><%= f.label :uid %><br />
<%= f.text_field :uid %></div>
<div><%= f.submit "Sign up" %></div>
<% end %>
<%= render "devise/shared/links" %>
Could you help me, i don't understand what am i doing wrong. How to configure whitelist for my strong params to receive proper values (which user put in view-form) ?
All my source code is available here: https://github.com/DavydenkovM/d23m
Thanks in advance!
UPDATE
I've remove attr_accessible fields and update my controller. But the problem with unpermitted params name and uid at the same point. Now my controller looks like:
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
before_filter :configure_permitted_parameters, if: :devise_controller?
def facebook
#user = User.find_for_facebook_oauth(request.env["omniauth.auth"], current_user)
if #user.persisted?
sign_in_and_redirect #user, :event => :authentication #this will throw if #user is not activated
flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Facebook"
set_flash_message(:notice, :success, :kind => "Facebook") # if is_navigational_format?
else
redirect_to root_url if user_signed_in?
session["devise.facebook_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
def create
super
end
#def update
# person = current_account.user.find(params[:id])
# person.update_attributes!(person_params)s
# redirect_to person
#end
private
def configure_permitted_parameters
devise_parameter_sanitizer.for(:users) do |u|
u.permit(:name, :email, :password, :password_confirmation, :uid, :provider)
end
end
end
UPDATE 2.
I'm not clearly understand what is resource in devise_parameter_sanitizer.for(?) and where i need to assign this ?
Please try the following.
def self.find_for_facebook_oauth(auth, signed_in_resource=nil)
user = User.where(:provider => auth.provider, :uid => auth.uid).first
unless user
user = User.create(name:auth.extra.raw_info.name,
provider:auth.provider,
uid:auth.uid,
email:auth.info.email,
password:Devise.friendly_token[0,20]
).permit!(:name, :uid, :provider)
end
user
end
Or
def facebook
#user = User.find_for_facebook_oauth(request.env["omniauth.auth"].permit!(:name, :uid, :provider), current_user)
if #user.persisted?
sign_in_and_redirect #user, :event => :authentication #this will throw if #user is not activated
flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Facebook"
set_flash_message(:notice, :success, :kind => "Facebook") # if is_navigational_format?
else
redirect_to root_url if user_signed_in?
session["devise.facebook_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
Then try this
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:name, :email, :password, :password_confirmation, :uid, :provider) }
end
attr_accessible is not available in Rails 4 because Strong Parameters is now used by default. It appears you are already using strong parameters, so you should simply remove the line from your user model with attr_accessible in it.

Routing issue with Devise confirmable - Email only signup, getting error "Couldn't find User with id=confirmation"

I think I have a routing issue with Devise that I can't figure out. I am trying to implement this method for having users sign up using Devise for authentication. The example uses haml, and I have attempted to translate that back to erb, hopefully with some success.
Rails 3.2.8 in development on Mac OS X, using PostgresQL
Using Devise and simple_form
app/views/devise/registrations/new.html.erb:
<%= simple_form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %>
<%# simple_form_for([:backend, #user]) do |f| %>
<%= f.error_notification %>
<div class="inputs">
<%= f.input :email, :required => true, :autofocus => true, :label => "Email" %>
</div>
<div class="actions">
<%= f.button :submit, "Add User" %>
</div>
<% end %>
app/views/devise/confirmations/show.html.erb:
<%= simple_form_for(resource, :as => resource_name, :url => confirmation_path(resource_name)) do |f| %>
<div class="inputs">
<%= f.input :password, :required => true, :label => "Password" %>
<%= f.input :password_confirmation, :required => true, :label => "Confirm Password" %>
<%= f.hidden_field :confirmation_token %>
</div>
<div class="actions">
<%= f.button :submit, "Confirm" %>
</div>
<% end %>
app/modesl/user.rb:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable,
:timeoutable
# Setup accessible (or protected) attributes for your model
attr_accessible :email,
:password,
:password_confirmation,
:remember_me,
:username
def password_required?
super if confirmed?
end
def password_match?
self.errors[:password] << "can't be blank" if password.blank?
self.errors[:password_confirmation] << "can't be blank" if password_confirmation.blank?
self.errors[:password_confirmation] << "does not match password" if password != password_confirmation
password == password_confirmation && !password.blank?
end
end
app/controllers/confirmations_controller.rb:
class ConfirmationsController < Devise::ConfirmationsController
def show
self.resource = resource_class.find_by_confirmation_token(params[:confirmation_token])
super if resource.confirmed?
end
def confirm
self.resource = resource_class.find_by_confirmation_token(params[resource_name][:confirmation_token])
if resource.update_attributes(params[resource_name].except(:confirmation_token)) && 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
render :action => "show"
end
end
end
app/config/routes.rb:
devise_for :users, :controllers => {:confirmations => 'confirmations'}
devise_scope :user do
put "/confirm" => "confirmations#confirm"
end
resources :users
When I input my email address at the users/sign_up page, everything works fine. I receive the confirmation email, click the confirmation link and am directed to:
/users/confirmation?confirmation_token=[CONFIRMATION TOKEN HERE]
Then, when I fill in my password and submit, I am directed to this page:
users/confirmation
with the following error:
ActiveRecord::RecordNotFound in UsersController#update
Couldn't find User with id=confirmation
I'm pretty sure it's a routing issue. Any ideas? Thanks so much!
Cyle, thanks for getting my brain working this evening. The answer was staring me right in the face the whole time. In routes, I changed
match "/confirm" => "confirmations#confirm"
to
match "/confirmation" => "confirmations#confirm"
Now I can start researching the next error it throws. Thanks again!

Calling two methods from one controller in nested model form

Through other posts on SO I've learned that my sign-up process using a nested model form is flawed in that I create a new User, then redirect to create its Profile. Here is the process:
user = User.new
user.email = ...
user.password = ...
user.profile = Profile.new
user.profile.first_name = ...
...
user.profile.save
user.save
It seems as if one solution is to initiate the profile method from within the UsersController create(?) action, so that I POST to both models(?) then redirect to a page with a form to fill out the rest of the profile.
But I'm not entirely sure how to do that, as I am new to programming/Rails. So can anyone give me guidance on how to introduce the Profile method within the UsersController? I gave it a go but don't think it's correct. Code for both Users/ProfilesController below:
User:
def new
#user = User.new
#user.profile = Profile.new
end
def index
#user = User.all
end
def create
#user = User.new(params[:user])
if #user.profile.save
redirect_to profile_new_path, :notice => 'User successfully added.'
else
render :action => 'new'
end
end
Profile:
def new
#user.profile = Profile.new
end
def create
#profile = Profile.new(params[:profile])
if #profile.save
redirect_to profile_path, :notice => 'User successfully added.'
else
render :action => 'new'
end
end
Routes.rb:
match '/signup' => 'profiles#new', :as => "signup"
get "signup" => "profiles#new", :as => "signup"
root :to => 'users#new'
resources :users
resources :profiles
My nested model form (the relevant parts):
<%= form_for(:user, :url => { :action => :create }, :html => {:id => 'homepage'}) do |f| %>
<%= f.text_field :email, :size=> 13, :id => "user[email]" %>
<%= f.fields_for :profile do |f| %>
<% end%>
<% end %>
If anyone could help me I'd greatly appreciate it.
You should have something like this in your models:
class User < ActiveRecord::Base
has_one :profile
accepts_nested_attributes_for :profile
end
class Profile < ActiveRecord::Base
belongs_to :user
end
...of course all backed up with proper migrations. Then while building up a form you can use fields_for helper. Here is slightly modified example from docs:
<%= form_for #user do |user_form| %>
Email: <%= user_form.text_field :email %>
<%= user_form.fields_for :profile do |profile_fields| %>
First Name: <%= profile_fields.text_field :first_name %>
<% end %>
<% end %>
And update your user and his profile in the controller in one go, thanks to accepts_nested_attributes_for :profile declaration in your model.

Rails 3: role_mask won't save - WARNING: Can't mass-assign protected attributes: roles

I'm using the code from Railscasts 189 to implement roles with Devise so I can use Cancan. However the roles are not being saved to the role_mask field. Relevant code from my user model:
attr_accessible :email, :password, :password_confirmation, :remember_me,
:name, :about, :awards, :url, :roles_mask
ROLES = %w[admin support worker monitor visitor]
named_scope :with_role, lambda { |role| {:conditions => "roles_mask & #{2**ROLES.index(role.to_s)} > 0"} }
def roles=(roles)
self.roles_mask = (roles & ROLES).map { |r| 2**ROLES.index(r) }.sum
end
def roles
ROLES.reject { |r| ((roles_mask || 0) & 2**ROLES.index(r)).zero? }
end
def role_symbols
roles.map(&:to_sym)
end
In the views for user "new" and "edit" I have the following to show checkboxes for the roles:
<p>
<%= f.label :roles %><br />
<% for role in User::ROLES %>
<%= check_box_tag "user[roles][]", role, #user.roles.include?(role) %>
<%=h role.humanize %><br />
<% end %>
<%= hidden_field_tag "user[roles][]", "" %>
</p>
When I check the check boxes for a couple of the roles and hit "submit", I get the following flash:
1 error prohibited this user from being saved:
And I get the following error from the server:
WARNING: Can't mass-assign protected attributes: roles
I've checked the DB and nothing gets saved in the roles_mask field. There are lots of questions in Stackoverflow about "can't mass-assign protected attributes" but they don't seem germane to this situation.
Any suggestions?
Try adding :roles to the attr_accessible list like this:
attr_accessible :email, :password, :password_confirmation, :remember_me,
:name, :about, :awards, :url, :roles_mask, :roles
Understand completely about the mass-assignment issues before doing this.