I have two classes of accounts in my application. I'm trying to update the attributes of one of them (Investors). They are created using one form via devise. As a result, when an investor tries to update their account information, the form requires that they provide and confirm their password. I would like for them to be able to edit (First name, last name, etc) without having to input their password unless they want to change their password with those fields.
Here is my update method in the Investor controller
def update
session[:investor_params] ||= {}
session[:investor_params].deep_merge!(params[:investor]) if params[:investor].present?
#investor.attributes = session[:investor_params]
params[:investor_params].delete(:password) if params[:investor_params][:password].blank?
params[:investor_params].delete(:password_confirmation) if params[:investor_params][:password_confirmation].blank?
respond_to do |format|
if #investor.update_attributes(params[:investor_params])
session[:investor_params] = nil
sign_in(#investor.account, :bypass => true)
format.html { redirect_to(projects_url, :notice => 'Investor was successfully updated.') }
format.xml { render :xml => #investor, :status => :created, :location => #investor }
else
format.html { render :action => "edit", :layout => "investor" }
format.xml { render :xml => #investor.errors, :status => :unprocessable_entity }
end
end
end
Here is my registrations controller
class RegistrationsController < Devise::RegistrationsController
layout "index"
protected
def after_sign_up_path_for(resource)
new_user_url(:account => resource)
end
end
Here's my routes.rb
devise_for :accounts, :controllers => {
:registrations => 'registrations',
:passwords => 'passwords',
:sessions => 'sessions' }
devise_scope :account do
root :to => 'registrations#new'
And this is a portion of my investor model that references attributes for the account model.
class Investor < ActiveRecord::Base
has_one :account, :as => :profile
accepts_nested_attributes_for :account
Here is the portion of the account model that is referenced
class Account < 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
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me, :invited_by, :invited_by_id
I tried the recommendations on the devise github repo but I wasn't able to get it working for me.
If you have any suggestions or if I am missing anything, please let me know!
Are you sending a :current_password anywhere in your params? (even if it is blank?) Normally devise wants you to use #update_with_password when requiring a password, so I'm not sure why you are getting this with :update_attributes.
Related
I am migrating a Rails 2 app to Rails 3 and had to use a new authentication solution since restful_authentication is no longer supported. I am trying out Devise and am having problems with it.
I have a login page, which then directs you to the app. You can also use the app without logging in but some options are not available until you login.
Once logging in Devise says that I have logged in via a flash message, but any current_user will evaluate to nil, current_user.blank? evaluates to true and any user_signed_in? will evaluate to false. Oddly signed_in? evaluates to true. Checking my session data shows that warden.user.user.id contains the correct user id and a csrf token exists. This is true both in the view and in the controller.
Here is routes.rb
MyApp::Application.routes.draw do
devise_for :users
match "/:controller(/:action(/:id))"
match "/:controller(/:action(/:id))(.:format)"
match "/:controller(/:action(.:format))"
devise_scope :user do
get 'signup', :to => 'devise/registrations#new'
get 'login', :to => 'devise/sessions#new'
get 'signout', :to => 'devise/sessions#destroy'
end
root :to => 'main#design'
end
User.rb
require 'digest/sha1'
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :confirmable, :validatable,
:encryptable, :encryptor => :restful_authentication_sha1
attr_accessible :email, :password, :password_confirmation, :remember_me, :profile_company, :profile_state, :profile_city, :profile_postcode, :profile_country, :profile_phone, :profile_address
validates_length_of :name, :maximum => 100
validates_presence_of :email
validates_length_of :email, :within => 6..100
validates_uniqueness_of :email
def profile_complete?
if !(self.profile_address.blank? || self.profile_city.blank? || self.profile_state.blank? || self.profile_phone.blank? || self.name.blank? || self.state != 'active')
true
else
false
end
end
end
I've seen a lot of similar questions on SO, but none of the answers seem to fit my scenario. Any help would be greatly appreciated, thanks!
EDIT: warden.authenticate(:scope => :user) returns what i would expect current_user to return.
I believe that I have solved this issue, the restful_authentication plugin added a UsersHelper file that I believe was somehow interfering with how Devise was handling the User class.
In my RoR application I'm using devise and the client requires a bit of customisation - basically he requires that the administrator be able to change passwords of other users and that the password change page be different than the page to edit profile details. I've set up custom actions to handle this namely my own change_password action in users controller.
Users Controller Actions
def change_password
#user = User.find(params[:id])
end
def update_password # I post to this
#user = User.find(params[:id])
if #user.update_attributes!(params[:user])
redirect_to users_path, :notice => "User updated."
else
redirect_to users_path, :alert => "Unable to update user."
end
end
Heres the routes.rb entries
devise_for :users, :skip => [:registrations]
as :user do
get 'users/edit' => 'devise/registrations#edit', :as => 'edit_user_registration'
put 'users' => 'devise/registrations#update', :as => 'user_registration'
end
resources :users
...
match "/users/:id/change_password" =>"users#change_password", :as=>:change_password_user, :via=>:get
match "/users/:id/update_password" => "users#update_password", :as=>:update_password_user, :via=>:post
And this is my users model
class User < ActiveRecord::Base
rolify
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable, :registerable,
devise :database_authenticatable, #:registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :role_ids, :as => :admin
attr_protected :username, :name, :email, :password, :password_confirmation, :remember_me
validates_uniqueness_of :username
validates_presence_of :username, :email
validates_uniqueness_of :email
end
however I keep getting this mass attributes assignment error
Can't mass-assign protected attributes: password, password_confirmation
the weird thing is that I've set all these attributes to accessible_protected. I can edit other users details but can't edit their passwords. Whats going on here?
There are many ways you can fix this problem. I'll try to explain a few.
I think the key to your problem is that you are mixing up the MassAssignmentSecurity roles. You've defined a Whitelist for the admin role and a Blacklist for the default role. The error says that you tried to assign something that was on the Blacklist for the default role.
Since you are defining different roles, I assume you probably want to fix it this way:
Change your admin Whitelist
attr_accessible :role_ids, :password, :password_confirmation, as: :admin
Then assign as the admin:
if #user.update_attributes!(params[:user], as: :admin)
(If your controller action includes fields other than the password fields, this may cause new violations.)
A different option is to stick to the default role. You can bypass security a couple ways.
The first option which I don't recommend is to not pass the password and password confirmation as part of the User params, and send them separately in your view. You can then manually set those fields like so:
#user.assign_attributes(params[:user])
#user.password = params[:password]
#user.password_confirmation = params[:password_confirmation]
if #user.save!
However, it's even easier to do the following to just skip protection:
#user.assign_attributes(params[:user], without_protection: true)
if #user.save!
For more information, this guide is fairly good:
http://guides.rubyonrails.org/security.html#mass-assignment
I hope that helps.
I have Two models Users and Profiles ( Users is from devise ). A user can have many profiles , so i created a migration to add user_id to Profiles.
class Profile < ActiveRecord::Base
belongs_to :user
attr_accessible :description, :name, :product, :image, :price , :user_id
mount_uploader :image, ImageUploader
validates_presence_of :name, :product,:image, :price
end
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
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me
# attr_accessible :title, :body
has_many :profiles
validates_associated :profiles
end
In my profiles controller , i have a new method to create a new profile. I want that when user logs in, he is able to see only his profiles not all.
def new
#profile = current_user.profiles.build
##profile = #user.profile.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #profile }
end
end
Ideally, my user_id column should be updated with build, but my user_id does not get updated and that is why profiles are not displayed . i am using postgresql with pg gem and when i manually add the user_id for that profile , i am able to see the profiles.
Please help to solve this, i am able to figure this out since a long time.Let me know if you want more information to solve this.
Maybe try something like this in your controller:
if current_user.profiles.count == 0
profile = Profile.create
current_user.profiles << profile
end
NoMethodError in AuthenticationsController#create
undefined method `user' for # /Authentication:0x00000105c7b1f8\
I'm pulling my hair out.. I downloaded the sample app from railscasts and it works.. mine has what looks like one last bug.
I am attempting to follow the railscasts to add this in to my app that is/was otherwise working -
http://railscasts.com/episodes/236-omniauth-part-2
It seems that it's mostly getting there - in the authorizations table there is a record..
I'm not getting as far as the step where the page asks for an email because it doesn't have one.
I'm sure this is super simple - here are some snippets:
controller:
class AuthenticationsController < ApplicationController
def create
omniauth = request.env["omniauth.auth"]
authentication = Authentication.find_by_provider_and_uid(omniauth['provider'], omniauth['uid'])
if authentication
flash[:notice] = "Signed in successfully."
sign_in_and_redirect(:user, authentication.user)
elsif current_user
current_user.authentications.create!(:provider => omniauth['provider'], :uid => omniauth['uid'])
flash[:notice] = "Authentication successful."
redirect_to authentications_url
else
#user = User.new
#user.apply_omniauth(omniauth)
if #user.save
flash[:notice] = "Signed in successfully."
sign_in_and_redirect(:user, user)
else
session[:omniauth] = omniauth.except('extra')
redirect_to new_user_registration_url
end
end
end
end
devise user model:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable, :lockable and :timeoutable
has_many :authentications
devise :database_authenticatable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
#attr_accessible :email, :password, :password_confirmation, :remember_me
def apply_omniauth(omniauth)
self.email = omniauth['user_info']['email'] if email.blank?
authentications.build(:provider => omniauth['provider'], :uid => omniauth['uid'])
end
def password_required?
(authentications.empty? || !password.blank?) && super
end
end
some of the routes.rb:
resources :authentications
match '/auth/:provider/callback' => 'authentications#create'
get "search/show"
#devise_for :users
devise_for :users, :controllers => {:registrations => 'registrations'}
There should be #user in sign_in_and_redirect(:user, user). Not user.
I am getting this error:
2011-04-09T18:13:17+00:00 app[web.1]: NameError (uninitialized constant Group::Membership):
2011-04-09T18:13:17+00:00 app[web.1]: app/controllers/groups_controller.rb:25:in `show_members'
When I try and use the Show_members method in the groups controller. What have I done wrong?
user.rb
class User < ActiveRecord::Base
require 'paperclip'
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable, :lockable and :timeoutable
devise :database_authenticatable, :registerable, :confirmable,
:recoverable, :rememberable, :trackable, :validatable
has_and_belongs_to_many :roles
has_many :articles
has_many :ratings
has_many :rated_articles, :through => :ratings, :source => :articles
has_many :memberships, :dependent => :destroy
has_many :groups, :through => :memberships
...
membership.rb
class Membership < ActiveRecord::Base
attr_accessible :user_id, :group_id
belongs_to :user
belongs_to :group
validates_uniqueness_of :user_id, :message => "You can only join one group!"
end
group.rb
class Group < ActiveRecord::Base
require 'paperclip'
attr_accessible :name, :group_admin, :about, :location, :created_at, :avatar
validates_uniqueness_of :name, :group_admin
validates_length_of :name, :in => 4..40
has_many :memberships, :dependent => :destroy
has_many :users, :through => :memberships
end
group_controller
def show_members # members page
#group = Group.find(params[:id])
#members = #group.users <= THIS LINE IS THE ERROR
#group_admin = User.find(#group.group_admin)
respond_to do |format|
format.html
format.xml { render :xml => #group }
end
end
Update
def remove_user
#membership = Membership.find(params[:user_id, :group_id])
#membership.destroy
authorize! :remove_user, #membership
respond_to do |format|
format.html { redirect_to(:back, :notice => 'User was successfully Removed.') }
format.xml { head :ok }
end
I have this action in the groups_controller linked to in the show_members.html.erb
could the error be coming from this?
Oh my god. I just checked the git logs. Apparently, git decided not to add the memberships model file to the repo for some reason despite me doing git commit -a -m"blah". I thought the -a flag added everything?
I don't see an error in the code you pasted, but judging from the error code you get, you must be having a constant value somewhere that cannot be accessed. Do you have any Group.Membership call somewhere in your code ? (maybe a module?) Maybe you meant to write group.membership or something. I really can't spot anything else :/
EDIT :
Btw, i see something that should be causing a problem as well :
#group_admin = User.find(#group.group_admin)
group.admin is a name as a i see in your validations and you're looking up for an id. It should be find_by_group_admin in this one.