Is there a security risk associated with defining multiple "params" in rails? - ruby-on-rails

I am new to rails and making myauthentication/authorization system since devise was way overkill for what I needed. I realize that since I am not a security expert it will probably have holes. I have tested to the best of my knowledge whether the following would present a security issue, but maybe someone more experienced would be kind enough to give me their two cents :)
Here's the code, it is pretty self explanatory what I'm trying to do:
def user_params
if is_super_user
params.require(:user).permit(:firstname, :lastname, :email, :password, :password_confirmation, :role_id)
else
params.require(:user).permit(:firstname, :lastname, :email, :password, :password_confirmation)
end
end
So a regular user should not be able to alter permissions levels whereas a super user can. Is this okay to do? Is there a better solution?

Related

Rails strong parameters, user require and get an hash

I have this method
def create_billing_address(data)
address_data = data.permit(:first_name,
:last_name,
:phone,
:address_1,
:city,
:postcode,
:country)
service_customer.create_address(customer_id, address_data)
end
But now I want to check that all the keys are present. I tried to do this
address_data = data.require(:first_name,
:last_name,
:phone,
:address_1,
:city,
:postcode,
:country)
But require return an array instead of an hash.
How can I do to have the same behaviour of permit but with require ?
require is only intended to ensure that the params have the correct general structure.
For example if you have:
params.require(:foo).permit(:bar, :baz)
require lets us bail early if the :foo key is missing since we cant do anything with the request.
require is not intended to validate the presence of individual params - that is handled with model level validations in Rails.
If you really had to you could do:
def create_billing_address!(data)
keys = [:first_name, :last_name, :phone, :address_1, :city, :postcode, :country]
keys.each do |k|
raise ActionController::ParameterMissing and return unless data[k].present?
end
service_customer.create_address(customer_id, data.permit(*keys))
end
But thats just bad application design as you're letting the model level business logic creep into the controller.
permit and require are not interchangeable the way you think. If you establish that all the required keys are present, they still need to be permitted to be used in a mass assignment.
So you'd likely need to do...
def create_billing_address(data)
fields = %i(first_name last_name phone address_1 city postcode country)
address_data = data.permit(data.require(fields))
service_customer.create_address(customer_id, address_data)
end
The data.require will raise an ActionController::ParameterMissing exception if a key is missing, if not it will return the array of keys which can be used by permit.
Generally, what you want to do with require is more typically handled by model validation.
Documentation on require and permit is here...
http://api.rubyonrails.org/classes/ActionController/Parameters.html

Rails 4 : Jquery token Input is not saving attributes

I have been working with jQuery token input and Rails 4.
Devise gem Application controller
devise_parameter_sanitizer.for(:account_update) { |u| u.permit( :email, :first_name, :last_name,
:password, :password_confirmation, :current_password, :passion_tokens => [] ) }
I want to save users :passion_tokens, but not able to save it. I have mentioned it in controller as per strong parameters.
user.rb
attr_reader :passion_tokens
def passion_tokens= (ids)
self.author_ids = ids.split(",")
end
So, please provide possible way to save the data.
attr_reader does not write to the attributes, which is the probable cause of your attributes not saving. You should use attr_accessor if you want to both read and write or attr_writer if only writing to attribute is required.

Rails - Strong Parameters and attr_accessible

Rails newb, just working on my first app and my first coding experience outside of a little python and bash for sysadmin type work. Using rails 4.1 on C9 and kind of curious why I am experiencing a situation. From what I have read regarding using strong parameters, this replaces the old Rails 3 use of attr_accessible.
I am using devise and setup auth on my app, then wanted to add some custom fields like username and zipcode to be updated via a user profile page. Here is the code that I have to accomplish this. From what I have read the strong parameters should be moved from the model into the controller.
app/controllers/registrations_controller.rb # this inherits the devise::reg
def sign_up_params
params.require(:user).permit(:username, :email, :password, :zipcode, :password_confirmation)
end
def account_update_params
params.require(:user).permit(:username, :email, :password, :zipcode, :password_confirmation, :current_password)
end
However, even after adding this it is still not saving the fields via my /users/edit page without the addition of the following.
app/models/user.rb
attr_accessible :email, :password, :password_confirmation, :remember_me, :username, :zipcode
Once the username and zipcode symbols (not sure of that is the correct term) have been added to the user.rb then the database is populated. The only thing I can think of is the fact that I added the gem 'protected_attributes' awhile back when trying to figure this out. Is this what is forcing me to use the attr_accessible statement in the user.rb?
If there is any additional code that would be useful just ask. Thanks for taking a look, it's much appreciated as I start to understand this.
I guess I should have just attempted this myself before asking here. I removed the gem 'protected_attributes' and commented out the attr_accessible line and restarted rails and viola, it still works. :D

validates password length conflicting with form_helper user input

Is there a way to get around this? Basically in my user model i have a password validation to check and make sure the user has a minimum password length
validates :password, length: {minimum: 6 }
However, when I get the user to update additional info through a form helper, it refuses to update if I dont comment/delete the line above.
The following is some of my code in my user controller to update some user attributes in a db table
def additional_info
#user = User.find params[:id]
end
def update
#user = User.find(params[:id])
if #user.update(user_addinfo)
redirect_to #user
else
render action: 'additional_info'
end
end
def user_addinfo
params.permit(:years_business, :years_relationships, :years_careers, :years_lifeoutlook)
end
end
Help is greatly appreciated. My website is structured around Michael Hartl's tutorial but i made some personal modifications.But for the most part, its exactly the same.
Ah, I think I understand.
You can look at the has_secure_password source to see what validations, attributes, and other stuff it adds to your model.
As you probably know, has_secure_password adds an attr_reader :password along with a #password= setter that calculates the password_digest whenever you assign something to the password attribute. This prevents the password attribute from being persisted to the database. (Good!)
But that means that when you call #user.update, it will be on a #user which will have a nil password.
The trick is that you only want to validate the length of the password when the password is initially set. You should be able to fix this problem by changing your validation line to read:
validates :password, length: {minimum: 6 }, on: :create
If you want to allow users to change their passwords, you will have to do something else, like
validates :password, length: { minimum: 6 }, if: Proc.new { |u| u.password.present? }
Let me know if that works. I'm not 100% sure I understand your problem so let me know if this isn't the fix you were looking for.

Make rails ignore inputs if user is admin

I have a user object, he can update his profile which includes name, user_name, password (blank), password_confirmation (blank), email, email_confirmation (blank), bio and picture url.
My model states that all of the (blank) MUST be filled in. but if your admin and your just going to the users page to update the user's role - You as the admin should not have to fill in user data you obviously don't know.
So how does one get around this? should I instead create a list of users with a drop down beside them? is this not, essentially , a giant form? If so - how would this get created?
essentially: What's the best way to deal with this situation?
This is currently how users get updated
def update
#user = User.friendly.find(params[:id])
#user.update_attributes(user_update_params)
if #user.save
render :show
else
render :edit
end
end
private
def user_update_params
params.require(:user).permit(:name, :user_name, :email, :email_confirmation, :password,
:password_confirmation, :bio, :picture_url, :role)
end
The real problem seems to be that you have a logical error in your User model validations.
You seem to have a validation of the form,
validates :password, presence: true, confirmation: true
which is enforced EVERY TIME, i.e. a new password has to be selected every single time a user object is saved. But this is likely not what you want. You likely want this validation to only be enforced when the user is created for the first time, i.e. when it is a new record.
You can do this with,
validates :password, presence: true, confirmation: true, if: :new_record?
update_attribute
Updates the attribute without doing validations, you need this one.
check out this api doc
EDIT:
Speaking about reading documentation
Here is an abstract from the method documentation
update_attribute(name, value) public
Updates a single attribute and saves the record. This is especially
useful for boolean flags on existing records. Also note that
Validation is skipped.
Callbacks are invoked.
updated_at/updated_on column is updated if that column is available.
Updates all the attributes that are dirty in this object.
EDIT:
If you still need to validate with this method, note that it says that callbacks are invoked, so what you can do is write your own code to validate input and use callbacks as described here.

Resources