I'm using Rails (3.2.3) and Devise, and allowing administrators to create new users – and edit user accounts.
At this point, administrators can create accounts successfully. However, they can't edit them.
When you try to edit a user's account, a mass-assignment error is raised:
Can't mass-assign protected attributes: email, name
Even though, in the User model, these attributes are set to accessible:
attr_accessible :name, :email, :password, :password_confirmation, :remember_me
What's interesting, is, if I change the above line to attr_protected, you CAN edit user information but you CANNOT create users anymore. Very weird.
Here's the relevant code I'm working with... any help is appreciated.
User model:
class User < ActiveRecord::Base
rolify
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
attr_accessible :role_ids, :as => :admin
attr_accessible :name, :email, :password, :password_confirmation, :remember_me
end
Users controller:
class UsersController < ApplicationController
before_filter :authenticate_user!
def update
authorize! :update, #user, :message => 'Not authorized as an administrator.'
#user = User.find(params[:id])
if params[:user][:password].blank?
params[:user].delete(:password)
params[:user].delete(:password_confirmation)
end
if #user.update_attributes(params[:user], :as => :admin)
redirect_to users_path, :notice => "User updated."
else
redirect_to users_path, :alert => "Unable to update user."
end
end
def new
#user = User.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #user }
end
end
def edit
#user = User.find(params[:id])
end
def create
#user = User.new(params[:user])
if params[:user][:password].blank?
params[:user].delete(:password)
params[:user].delete(:password_confirmation)
end
respond_to do |format|
if #user.save
format.html { redirect_to users_path, notice: 'User was successfully created.' }
else
format.html { render action: "new" }
end
end
end
end
Change to
class User < ActiveRecord::Base
rolify
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
attr_accessible :role_ids, :name, :email, :password, :password_confirmation, :remember_me, :as => :admin
attr_accessible :name, :email, :password, :password_confirmation, :remember_me
end
Now administrators can create accounts successfully & they CAN edit them as well. Hope it helps
Related
Problem: I want to make password & password_confirmation fields validates presence:true for create action and no validation for update action
guest.rb:
class Guest < ActiveRecord::Base
devise :database_authenticatable, :recoverable, :rememberable, :trackable
validates :email, presence: true
end
My guests_controller.rb:
class GuestsController < ApplicationController
before_action :set_guest, only: [:show, :edit, :update]
def index
#guests = Guest.all
end
def show
#guest = Guest.find(params[:id])
end
def new
#guest = Guest.new
end
def edit
#guest = Guest.find(params[:id])
end
def create
respond_to do |format|
format.html do
#guest = Guest.new(guest_params)
if #guest.save
redirect_to guests_path, notice: 'Client was successfully created.'
else
render :new
end
end
end
end
def update
#guest = Guest.find(params[:id])
if #guest.update_attributes(guest_params)
sign_in(#guest, :bypass => true) if #guest == current_guest
redirect_to guests_path, notice: 'Client was successfully updated.'
else
render :edit
end
end
If I put validates :password, presence: true, it effects everything, whereas I need it only for create
From the Active Record Validations Guide:
The :on option lets you specify when the validation should happen. The default behavior for all the built-in validation helpers is to be run on save (both when you're creating a new record and when you're updating it). If you want to change it, you can use on: :create to run the validation only when a new record is created or on: :update to run the validation only when a record is updated.
So in your case you would use:
validates :email, presence: true, on: :create
I suggest you take a moment to sit down and read through the entire guide and the API documentation for validates.
I am following a tutorial to setup a prelaunch page using Devise to manage collecting user emails. when I enter an email address i get error message "1 error prohibited this user from being saved:Email can't be blank"
My users model:
class User < ActiveRecord::Base
rolify
# Include default devise modules. Others available are:
# :token_authenticatable, :encryptable, :confirmable, :lockable :timeoutable and :omniauthable
devise :database_authenticatable, :registerable, :confirmable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :name, :email, :password, :password_confirmation, :remember_me
after_create :add_user_to_mailchimp
before_destroy :remove_user_from_mailchimp
# override Devise method
# no password is required when the account is created; validate password when the user sets one
validates_confirmation_of :password
def password_required?
if !persisted?
!(password != "")
else
!password.nil? || !password_confirmation.nil?
end
end
# override Devise method
def confirmation_required?
false
end
# override Devise method
def active_for_authentication?
confirmed? || confirmation_period_valid?
end
def send_reset_password_instructions
if self.confirmed?
super
else
errors.add :base, "You must receive an invitation before you set your password."
end
end
# new function to set the password
def attempt_set_password(params)
p = {}
p[:password] = params[:password]
p[:password_confirmation] = params[:password_confirmation]
update_attributes(p)
end
# new function to determine whether a password has been set
def has_no_password?
self.encrypted_password.blank?
end
# new function to provide access to protected method pending_any_confirmation
def only_if_unconfirmed
pending_any_confirmation {yield}
end
my users controller:
before_filter :authenticate_user!
def index
authorize! :index, #user, :message => 'Not authorized as an administrator.'
#users = User.all
end
def show
#user = User.find(params[:id])
end
def update
authorize! :update, #user, :message => 'Not authorized as an administrator.'
#user = User.find(params[:id])
if #user.update_attributes(params[:user], :as => :admin)
redirect_to users_path, :notice => "User updated."
else
redirect_to users_path, :alert => "Unable to update user."
end
end
def destroy
authorize! :destroy, #user, :message => 'Not authorized as an administrator.'
user = User.find(params[:id])
unless user == current_user
user.destroy
redirect_to users_path, :notice => "User deleted."
else
redirect_to users_path, :notice => "Can't delete yourself."
end
end
Devise/Registrations/new view
<div class="email-capture">
<%=form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<%= f.text_field :email, :class=>"email"%>
<%= f.submit " ", :class=>"reg-btn"%>
<% end %>
</div>
Log message after i click submit
Started POST "/users" for 127.0.0.1 at 2013-12-02 10:36:11 -0500
Processing by RegistrationsController#create as HTML
Parameters: {"utf8"=>"√", "authenticity_token"=>"mytoken", "user"=> {"email"=>"test#email.com"},
"commit"=>" "}
←[1m←[36m (0.0ms)←[0m ←[1mbegin transaction←[0m
←[1m←[35m (0.0ms)←[0m rollback transaction
Rendered devise/registrations/new.html.erb within layouts/application (5.0ms)
Rendered layouts/_messages.html.erb (0.0ms)
Have you modified your registration form?
Make sure that the email input has name="user[email]"
When you submit your registration form, what do you see in the server log for params?
Also, the users_controller shown should be for admins only -- make sure you aren't accidentally routing registrations to it.
UPDATE:
This is a wild guess, but I notice you are getting a DB rollback. Is it possible that you have a database restriction that requires password to not be NULL, for example?
take a look at (https://github.com/plataformatec/devise#strong-parameters), might be strong parameters issue, try to add something like following lines in your application_controller.rb:
before_filter :configure_permitted_parameters, if: :devise_controller?
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_in) { |u| u.permit(:name, :email) }
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:name, :email, :password, :password_confirmation) }
end
I have two models User and Promotion, an user can create has_many promotion and an promotion belong to user so :
promotion.rb
class Promotion < ActiveRecord::Base
belongs_to :user
belongs_to :good
validates :name, :presence => true
validates :title, :presence => true
validates :description, :presence => true
end
for the users i used devise so:
user.rb
class User < ActiveRecord::Base
has_many :promotions ,:foreign_key => "user_id",
:dependent => :destroy
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:omniauthable, :omniauth_providers => [:facebook]
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me,:provider,:uid,:address,:name,:surname,:supplier,:partita_iva,:state,
:gender ,:language,:bio,:work,:education
now when i want create a new promotions get this error
NoMethodError in PromotionsController#create
undefined method `promotions' for nil:NilClass
this is the controller:
def create
#user = User.find_by_id(params[:user_id])
#promotion =#user.promotions.create(:params[:promotion])
redirect_to promotion_patch(#promotion)
respond_to do |format|
if #promotion.save
format.html { redirect_to #promotion, notice: 'Promotion was successfully created.' }
format.json { render json: #promotion, status: :created, location: #promotion }
else
format.html { render action: "new" }
format.json { render json: #promotion.errors, status: :unprocessable_entity }
end
end
end
help please :)
It looks as though params[:user_id] did not contain a valid user id. Since you used find_by_id instead of find, it quietly assigned nil to #user, and of course nil doesn't have a method named #promotions, so that line failed.
You need to either check for #user being nil, or change User.find_by_id to User.find and then rescue ActiveRecord::RecordNotFound. In either case, respond with a custom 404 or whatever other way seems appropriate.
One other question, is it your intention that a user can create promotions for any other user? If they should only be creating promotions for themselves, you can avoid this whole mess by just eliminating the whole User.find_by_id line, and changing the next line to:
#promotion = current_user.promotions.create(params[:promotion])
Devise should have already current_user for you. In any case, you also need to handle what happens if the promotion cannot be created because there are validation errors in the user-supplied parameters.
I've set up gravatar and have got it working for my 'users/*user id goes here*'.
But whenever I try to use it in dashboard/index that's whenever it gives me the error
Undefined method 'email' for nil:NilClass
My dashboard controller is :
class DashboardController < ApplicationController
def index
respond_to do |format|
format.html # index.html.erb
format.json { render json: #posts }
end
end
end
Dashboard View :
<div class="dash-well">
<div class="gravatar-dashboard">
<%= image_tag avatar_url(#user), :class => 'gravatar' %>
<h1 class="nuvo wtxt"><%= current_user.username.capitalize %></h1>
</div>
</div>
My Application Helper :
module ApplicationHelper
def avatar_url(user)
default_url = "#{root_url}images/guest.png"
gravatar_id = Digest::MD5.hexdigest(user.email.downcase)
"http://gravatar.com/avatar/#{gravatar_id}.png?s=200{CGI.escape(default_url)}"
end
def avatar_url_small(user)
default_url = "#{root_url}images/guest.png"
gravatar_id = Digest::MD5.hexdigest(user.email.downcase)
"http://gravatar.com/avatar/#{gravatar_id}.png?s=40{CGI.escape(default_url)}"
end
end
My user model :
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :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, :username, :user_id, :id, :website, :bio, :skype, :dob, :age
has_many :posts
# attr_accessible :title, :body
end
My Dashboard Model :
class Dashboard < ActiveRecord::Base
attr_accessible :status, :author, :email, :username, :id, :user_id, :user, :website, :bio, :skype, :dob, :age
belongs_to :user
end
Sorry, i'm pretty new to Ruby-On-Rails!
Try this:
<%= image_tag avatar_url(current_user), :class => 'gravatar' %>
You really want this in your controller:
def index
#user = current_user
respond_to do |format|
format.html # index.html.erb
format.json { render json: #posts }
end
end
Note the addition of the second line, which assigns the #user variable to the current_user.
Then, the #user you are calling in your view will work. A typical rails pattern that you'll see emerge as you continue to use it is that most variable that begin with the # symbol will be defined in the corresponding controller method for that view. So, if you are using a variable with #, and it's not available, check the controller to ensure it's defined first. (FYI these are called instance variables if you want to learn more).
To address a second concern, if you are current_user and you wanted to visit another user's page:
def show
#user = User.find params[:id]
respond_to do |format|
format.html # index.html.erb
format.json { render json: #user }
end
end
This would work with a URL like /users/1, you can use that very same call to avatar_url, pass the #user, and it will get that user's avatar, where the user is the one that matches the given user ID. You probably already have this exact code in your controller already, but hopefully now you see why it works.
Good luck!
I'm getting 'Can't mass-assign protected attribute' error when trying to save a form that uses 'accepts_nested_attributes_for'. I think I have code the model correctly but not sure what I missed. Any idea?
error: Can't mass-assign protected attributes: organization
user.rb
class User < ActiveRecord::Base
belongs_to :organization
accepts_nested_attributes_for :organization
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
attr_accessible :email, :password, :password_confirmation, :remember_me,
:username, :first_name, :last_name, :organization_attributes
end
organization.rb
class Organization < ActiveRecord::Base
has_many :users
attr_accessible :address1, :address2, :city, :country, :fax, :name, :phone, :state, :zip
end
users_controller.rb
def new
#user = User.new
#organization = Organization.new
respond_to do |format|
format.html # new.html.erb
end
end
def create
#user = User.new(params[:user])
#organization = #user.organizations.build(params[:organization])
respond_to do |format|
if #user.save
#organization.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
else
format.html { render action: "new" }
end
end
end
Manage to get what I want based on the answer on this question: Use both Account and User tables with Devise
By the way I'm also using STI and it works great.