Rails mass-assign for admin users - ruby-on-rails-3

I'm using rolify to manage user roles and when I try to update the roles via a user form I receive this:
can't mass-assign protected attributes: role_ids
This is comforting, but it has me wondering, how do I then allow admin to update user roles via mass-assign yet not allow normal users to?

You can use a different attr_accessible list inside your User model:
attr_accessible :name
attr_accessible :name, :role_ids, :as => :admin
And then inside the create and update actions for your admin controller:
User.create(params[:user], :as => :admin)
user.update_attributes(params[:user], :as => :admin)

Related

Devise - mass assignment error when changing other users passwords in specific password change page

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.

Trouble with Mass-Assignment Error for Admin User

I was trying to follow the railscasts tutorial that explains how to handle mass-assignment errors and attr_accessible for admins, but since that was a little outdated, I'm trying to follow what's in the rails API dock for 3.2.6 here.
All I want to do is allow the admin user the ability to access the "winning" attribute for the Proposal Model on the Update action.
Here's my Proposal Model showing the current attr_accessible.
class Proposal < ActiveRecord::Base
attr_accessible :email, :email_confirmation, :link, :name, :references, :short_description
belongs_to :idea
Here's my code for the Proposal Controller's Update action.
class ProposalsController < ApplicationController
include ActiveModel::MassAssignmentSecurity
attr_accessible :email, :email_confirmation, :link, :name, :references, :short_description
attr_accessible :email, :email_confirmation, :link, :name, :references, :short_description, :winning, :as => :admin
def update
#idea = Idea.find(params[:idea_id])
#proposal = #idea.proposals.find(params[:id])
if #proposal.update_attributes(proposal_params)
redirect_to idea_proposals_url(#idea)
else
render 'edit'
end
end
protected
def proposal_params
role = current_user.admin ? :admin : :default
sanitize_for_mass_assignment(params[:proposal], role)
end
Check out this Railscast. I had a similar issue with an Admin field Boolean and didn't want any user to circumvent the security by sending a curl post. If the user is an admin then it gave them the ability to access the field, otherwise Mass Assignment would protect the field from being modified.
http://railscasts.com/episodes/237-dynamic-attr-accessible?view=asciicast

Omniauth Facebook auth + identity using the same model instead of two

I've setup Omniauth Facebook authentication according to this tutorial: http://net.tutsplus.com/tutorials/ruby/how-to-use-omniauth-to-authenticate-your-users/
And now I'm trying to combine it with omniauth-identity using the same User model instead of a separate Identity model as in this tutorial: http://railscasts.com/episodes/304-omniauth-identity?view=asciicast , but I cannot get it to work properly.
This is is my initializers/omniauth.rb file:
Rails.application.config.middleware.use OmniAuth::Builder do
provider :facebook, 'xxxxx', 'xxxxx'
provider :identity, :fields => [:email], :model => User
end
I've added 'password_digest' column that is needed by omniauth-identity to my User model/table and changed the User model code
from
class User < ActiveRecord::Base
has_many :authorizations
#validates :name, :email, :presence => true
def add_provider(auth_hash)
# check if the provider already exists, so we don't add it twice
unless authorizations.find_by_provider_and_uid(auth_hash["provider"], auth_hash["uid"])
Authorization.create :user => self, :provider => auth_hash["provider"], :uid => auth_hash["uid"], :token => auth_hash["token"]
end
end
end
to
class User < OmniAuth::Identity::Models::ActiveRecord
...
end
but when I do that the code in the Authorization model that creates the User and the Authorization models does not work properly
When the User model extends from ActiveRecord::Base the records are created just fine but when I extend the user model from OmniAuth::Identity::Models::ActiveRecord the user model is not stored in the database when you create a new authorization.
This is the Authorization model code:
class Authorization < ActiveRecord::Base
belongs_to :user
validates :provider, :uid, :presence => true
def self.find_or_create(auth_hash)
unless auth = find_by_provider_and_uid(auth_hash["provider"], auth_hash["uid"])
user = User.create :name => auth_hash["info"]["name"], :email => auth_hash["info"]["email"]
auth = create :user => user, :provider => auth_hash["provider"], :uid => auth_hash["uid"], :token => auth_hash["credentials"]["token"]
end
auth
end
end
When I extend the User model from ActiveRecord::Base and try to create a new registration with Identity I get this error:
ActiveRecord::UnknownAttributeError
unknown attribute: password
Is there any way to get this working this way? I don't know what to do now.
not sure you're still having the problem, but maybe someone on the interwebz will.
I just posted a solution on by blog, should solve your problems:
http://bernardi.me/2012/09/using-multiple-omniauth-providers-with-omniauth-identity-on-the-main-user-model/
try to add attr_accessor :password and may be attr_accessor :email

updating user record with devise that has_one relationship

Im using devise to handle my user authentication and in my user model Ive stated that each user has_one :role.
Im using another table to hold all my user roles/permissions and I was wondering how to update the role?
EDIT - here is my user model
has_one :role, :dependent => :destroy
accepts_nested_attributes_for :role, :allow_destroy => true
attr_accessible :stuff.... :role
My role model
belongs_to :user
Ive added this to my form:
<%= f.fields_for :role, Role.new do |r| %>
<li class="full_width">
<%= r.label "User type" %>
<%= r.select(:status, %w[member artist commercial],{:include_blank => false}) %>
</li>
<% end %>
but it never saves the role record, I guess its because the user model didnt have attr_accessible :role so I set that up and now when I try to save I get a AssociationTypeMismatch error
EDIT - added the accepts_attributes_for and now I dont get the error but the role record isnt saved. Console shows
WARNING: Can't mass-assign protected attributes: role_attributes
See http://api.rubyonrails.org/classes/ActiveModel/MassAssignmentSecurity/ClassMethods.html#method-i-attr_accessible. You have to declare
attr_accessible :role_attributes
From the code snippet that you have pasted, it's not clear where you are building the association between the new Role and the User. You may need to do something like #user.build_role(...) or #role.build_user(...) in order to associate the user and the role prior to saving.

declarative_authorization: control access to nested resource that doesn't have an explicit model

I have a model that allows a User to mark other Users as Favorites.
This HABTM relationship is defined in the User model:
class User < ActiveRecord::Base
has_and_belongs_to_many :favorites, :class_name => "User", :join_table => "favorites", :association_foreign_key => "favorite_id", :foreign_key => "user_id"
end
The FavoritesController only requires three actions (index, create,
destroy) to manage a User's Favorites.
Rule: Only an authenticated user (current_user) is allowed to manage
their Favorites.
Initially, I tried to represent this rule in the authorization_rule.rb
file:
# allow authenticated user to update profile
has_permission_on :users, :to => :change do
if_attribute :id => is { user.id }
has_permission_on :favorites, :to => [:index,:create,:destroy]
end
This didn't work, probably because the Favorite doesn't have an
explicit model (i.e. favorite.rb). Though I could be wrong about
this.
It seems like the correct approach would be to represent the rule in
the FavoritesController:
filter_access_to :all, :nested_in => :users
...
But I'm not certain how to represent the rule properly here.
Assistance is really appreciated.
** edit **
It was suggested that I use a context to control access in this situation:
setting permissions for a no-model controller .
I tried modifying the FavoritesController:
filter_access_to :all, :context => :favorites
This change had no effect.
** /edit **