What happen when using as admin? - ruby-on-rails

In Rails 3 in action its based on 3.1 so its kind old because i am using 3.2.12 when i tried to assign admin without make it free for mass assign it raises an error and thats because the differences between 3.1 and 3.2 as the author said.
so its better to use which on of the following and what is the difference ?
first method in controller
def create
#user = User.new(params[:user], :as => :admin)
if #user.save
flash[:notice] = "User has been created."
redirect_to admin_users_path
else
flash[:alert] = "User has not been created."
render :action => "new"
end
end
and in model
attr_accessible :email, :password, :admin, :as => :admin
second method in controller
def create
#user = User.new(params[:user], :without_protection => true)
#user.admin = params[:user][:admin] == "1"
if #user.save
flash[:notice] = "User has been created."
redirect_to admin_users_path
else
flash[:alert] = "User has not been created."
render :action => "new"
end
end
without adding the line above in the model
which one will protect from mass-assign or both are free ?

Both attr_accessible and :without_protection => true can be used to allow mass assignment on attributes of a model they are defined in.
so its better to use which on of the following and what is the difference ?
To answer this concern, I think using attr_accessible is better because you define exactly which attributes you want to allow for mass assignment compared to :without_protection => true which opens up all attributes in your model to be mass assigned.
Usually, passing the :without_protection => true is okay if you know exactly what the user input is, for e.g. when seeding data. But for inputs that come from a form (user input) you want to specify exactly what is allowed for mass assignment.
Hope this helps.
Update:
In the following statement, the as option you supply to attr_accessible confirms that the attributes email, password and admin are allowed only if the user is admin.
attr_accessible :email, :password, :admin, :as => :admin

Related

How to add default values to nested items in a Rails controller?

In the Sign up form of my Rails 6 application an Account with a nested User can be created.
class AccountsController < ApplicationController
def new
#account = Account.new
#account.users.build(
:owner => true,
:language => "FR"
)
end
def create
#account = Account.new(account_params)
if #account.save
redirect_to root_url, :notice => "Account created."
else
render :new
end
end
private
def account_params
safe_attributes = [
:name,
:users_attributes => [:first_name, :last_name, :email, :password, :owner, :language]
]
params.require(:account).permit(*safe_attributes)
end
end
What is the best way to define default values on the new user here?
Right now, I use hidden_fields for the default values in my sign up form thus making them publicly available. This is of course not what I want because it's very insecure.
Is there a better way to deal with this?
I know that there's Rails' with_defaults method but I couldn't get it to work on nested items so far.
try with:
account_params[:users_attributes] = account_params[:users_attributes].with_defaults({ first_name: 'John', last_name: 'Smith'})
in first line of create action

rendering the partials in controller after the validation check

I have two partial views for two different sign up forms. On my home page , based on the link one clicks on, I'm rendering respective form.(views/application/index)
= link_to 'Mentor', new_user_path(user_role: true), :class =>'btn'
= link_to 'Mentee', new_user_path, :class =>'btn'
In views/users/new.html.haml , I'm checking the user role and redirecting to the respective form.
- if params[:user_role]
= render 'mentor'
- else
= render 'mentee'
In the user model I've added validation like this.
class User < ActiveRecord::Base
email_regex = /\A[\w+\-.]+#cisco.com/i
validates :cisco_email, :presence => true,
:format => { :with => email_regex,}
validates :work_city, :presence => true
end
So, when there is any invalid field I want to direct to the same form with a flash message. My controller looks like this.
class UsersController < ApplicationController
def index
end
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def create
#user = User.new(params[:user]) # Not the final implementation!
if #user.save
flash[:success] = "Welcome to the CSG Mentoring Tool!"
redirect_to #user
else
flash[:notice] = "Error regsitering."
if params[:user][:user_role]
render :partial => 'users/mentor'
else
render :partial => 'users/mentee'
end
end
end
end
When an invalid field entry is there, it is redirecting to 'mentee' page no matter on which page the error is made. Also the entire css styling gets changed and flash is also not displayed
Why this is not working?
if params[:user][:user_role]
render :partial => 'users/mentor'
else
render :partial => 'users/mentee'
end
params[:user][:user_role] is nil.
You can check it using lots of way:
Above your if condition raise params[:user].inspect
Why its nil?
Reason of this is You are passing new_user_path(user_role: true) user_role true, but user_role is not true in mentor form.
params[:user_role] will not set user_role = true field in mentor form.
Set user_role
<%=f.hidden_field :user_role, value: params[:user_role] %>
If its supposed to be true for mentor always
<%=f.hidden_field :user_role, value: true %>
By default flash will make them available to the next request, but sometimes you may want to access those values in the same request.
Reference
This works with redirection
flash[:success] = "Welcome to the CSG Mentoring Tool!"
This will work with render
flash.now[:success] = "Welcome to the CSG Mentoring Tool!"

How to validate foreign keys in Rails validations?

In my Rails app I have users who can have many projects which in turn can have many invoices.
How can I make sure that a user can only create an invoice for one of his projects and not for another user's projects?
class Invoice < ActiveRecord::Base
attr_accessible :number, :date, :project_id
validates :project_id, :presence => true,
:inclusion => { :in => ????????? }
end
Thanks for any help.
class InvoicesController < ApplicationController
def new
#invoice = current_user.invoices.build(:project_id => params[:project_id])
end
def create
#invoice = current_user.invoices.build(params[:invoice])
if #invoice.save
flash[:success] = "Invoice saved."
redirect_to edit_invoice_path(#invoice)
else
render :new
end
end
end
I think that shouldn't be on a validation. You should ensure the project the user selected is one his projects.
You could do something on your controller like:
project = current_user.projects.find params[:project_id]
#invoice = Invoice.new(project: project)
# ...
Your create action could look something like this.
def create
#invoice = current_user.invoices.build(params[:invoice])
#invoice.project = current_user.projects.find params[:invoice][:project_id]
if #invoice.save
flash[:success] = "Invoice saved."
redirect_to edit_invoice_path(#invoice)
else
render :new
end
end
project_id is "sensitive" attribute - so remove it from attr_accessible. You are right that you should not believe params from the form and you must check it.
def create
#invoice = current_user.invoices.build(params[:invoice])
# #invoice.project_id is nil now because this attr not in attr_accessible list
#invoice.project_id = params[:invoice][:project_id] if current_user.project_ids.include?(params[:invoice][:project_id])
if #invoice.save
flash[:success] = "Invoice saved."
redirect_to edit_invoice_path(#invoice)
else
render :new
end
end
If user tries to hack your app and change project_id to not owned value then method create render partial new with invalid #invoice. Do not forget to leave the validation of project_id on presence.
If you get exception Can't mass-assign protected attributes... there are several ways what to do. The simplest ways are:
1. remove line from environment configs (development, test, production)
# Raise exception on mass assignment protection for Active Record models
config.active_record.mass_assignment_sanitizer = :strict
2. Reject sensitive parameters from params before assigning.
# changes in method create
def create
project_id = params[:invoice].delete(:project_id)
#invoice = current_user.invoices.build(params[:invoice])
#invoice.project_id = project_id if current_user.project_ids.include?(project_id)
...
end
OK, luckily I managed to come up with a solution of my own this time.
I didn't make any changes to my controller ("let's keep 'em skinny"), but added a validation method to my model instead:
class Invoice < ActiveRecord::Base
attr_accessible :number, :date, :project_id
validates :project_id, :presence => true,
:numericality => { :only_integer => true },
:inclusion => { :in => proc { |record| record.available_project_ids } }
def available_project_ids
user.project_ids
end
end
I am not sure if this is good or bad coding practice. Maybe someone can shed some light on this. But for the moment it seems pretty safe to me and I haven't been able to hack it in any way so far.

Why is this variable in this block for this validation code empty?

I'm using Devise and I'm trying to set up an invite code that the user has to input in order to sign up. I have this code in my user model:
attr_accessor :invite_code
validates_each :invite_code, :on => :create do |record, attr, value|
record.errors.add attr, "is wrong" unless value == "12345"
end
and this text field:
<%= f.text_field :invite_code %>
The problem is that value is always empty, so the validation is always failing, even if the invitation code is "12345".
I'm guessing this might have to do with the fact that the file that contains the text field is not in the user directory but is in a separate registrations directory (I did this from following this railscast when setting up omniauth). I'm really unsure though. How do I fix this?
Here's more information:
This is in my routes.rb:
devise_for :users, :controllers => { :registrations => 'registrations'}
this is in my users controller:
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save!
redirect_to videos_path
else
render :action => 'new'
end
end
This is in my registrations controller:
def create
super
session[:omniauth] = nil unless #user.new_record?
end
I believe that value is passed as an array. So..
record.errors.add attr, "is wrong" unless value[0] == "12345"
should work.
So the problem was that in addition to attr_accessor :invite_code, I needed attr_accessible :invite_code as well

Rails: Can you pass arguments to the after_create method?

In my application, only users with the administrator role may create new users. In the new user form, I have a select box for each of the available roles that may be assigned to new users.
I am hoping to use the after_create callback method to assign the role to the user. How can I access the selected value of the select box in the after_create method?
def create
#user = User.new(params[:user])
respond_to do |format|
if #user.save
flash[:notice] = 'User creation successful.'
format.html { redirect_to #user }
else
format.html { render :action => 'new' }
end
end
end
In the user model I have:
after_create :assign_roles
def assign_roles
self.has_role! 'owner', self
# self.has_role! params[:role]
end
I receive an error because the model doesn't know what role is.
You could use attr_accessor to create a virtual attribute and set that attribute as part of your create action.
The short answer is, no. You cannot pass any arguments to after_create.
However what your trying to do is a pretty common and there are other ways around it.
Most of them involve assigning the relationship before the object in question is created, and let ActiveRecord take care of everything.
The easiest way to accomplish that depends on the relationship between Roles and Users. If is a one to many (each user has one role) then have your users belong to a role and sent role_id through the form.
<%= f.collection_select :role_id, Role.all, :id, :name %>
If there is a many to many relationship between users and roles you achieve the same by assigning to #user.role_ids
<%= f.collection_select :role_ids, Role,all, :id, :name, {}, :multiple => :true %>
The controller in either case looks like
def create
#user = User.new(params[:user])
respond_to do |format|
if #user.save
flash[:notice] = 'User creation successful.'
format.html { redirect_to #user }
else
format.html { render :action => 'new' }
end
end
end

Resources