Need a validation for specific params of attribute - ruby-on-rails

I need write validation for role parameter of accounts_controller. If records with role admin only one, this record cannot update attribute role to user.
This validation must close security bug. But I don't know how it write.
accounts_controller.rb
class AccountsController < ApplicationController
def index
#accounts = Account.all
end
def update
#account = Account.find(params[:id])
redirect_to accounts_path if account.update(role: params[:role])
end
end
account.rb
class Account < ApplicationRecord
enum role: [:user, :admin]
end
schema.rb
create_table "accounts", force: :cascade do |t|
t.integer "role", default: 0
end
I trying write something like this: validate :role, less_than_or_equal_to: 1 but it didn't works.

account.rb
validate :the_last_admin
protected
def the_last_admin
if Account.admin.count < 2
errors.add(:role, 'You are the last admin!')
end
end

Related

How do I prevent model update from changing an attribute?

I have Account model with attribute role. Roles wrote with enum role: [:user, :admin]. I want that if left one account with role admin he can't update his role to user. I think need to write something like this: #account = Account.where(role: params[: role])... and then I don't know.
account.rb
class Account < ApplicationRecord
enum role: [:user, :admin]
end
accounts_controller.rb
class AccountsController < ApplicationController
def index
#accounts = Account.all
end
def update
#account = Account.find(params[:id])
redirect_to accounts_path if account.update(role: params[:role])
end
end
schema.rb
create_table "accounts", force: :cascade do |t|
t.integer "role", default: 0
end
I think what you want is a model callback before_update that acts like a validation.
class Account < ApplicationRecord
enum role: [:user, :admin]
before_update :insure_admin, if: -> { role == :admin && role_changed? }
private
def insure_admin
errors.add(:role, "admin cannot switch to regular user")
end
end
This should prevent the account.update(role: params[:role]) from returning true but you'll probably want to handle the error in your controller, something like:
class AccountsController < ApplicationController
def index
#accounts = Account.all
end
def update
#account = Account.find(params[:id])
if account.update(role: params[:role])
redirect_to accounts_path
else
redirect_to :back, flash: account.errors.full_messages
end
end
end
You might also want to add front end form validation to not allow the form to change role if the account is already persisted.

Validation give me not correct result

I wrote validation which check if role admin only one, role doesn't update on user. It's work, but then I have only one admin I can't update another users to admin. Wrote validation exception.
I try write math operators, like <= and <=> but it not working. I need what I can update user to admin if I have only one admin.
account.rb
class Account < ApplicationRecord
enum role: %i[user admin]
validate :admin_role
private
def admin_role
errors.add(:role, 'Must be one admin') if Account.where(role: :admin).count == 1
end
end
accounts_controller
class AccountsController < ApplicationController
def index
#accounts = Account.all.order(:id)
end
def update
#account = Account.find(params[:id])
if #account.valid?
#account.update(role: params[:role])
else
flash[:danger] = #account.errors.full_messages
end
redirect_to accounts_path
end
end
schema.rb
create_table "accounts", force: :cascade do |t|
t.integer "role", default: 0
end

Rails says that "role", a property I made doesn't exist

I clearly made the role for users (as you can see down below) but it says it doesn't exist. Help please? By the way, you can see how I'm hardcoding myself.
app/controllers/application-controller.rb:
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
helper_method :current_user
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
def require_user
redirect_to '/login' unless current_user
end
def require_admin
redirect_to '/' unless current_user.admin
end
end
User.create(first_name: "Johnny", last_name: "Appleseed", email: "j.appleseed#example", password: "MY AWESOME PASSWORD THAT NOBODY KNOWS", role: "admin")
db/migrate/20160109170743_create_users:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :first_name
t.string :last_name
t.string :email
t.string :password_digest
t.string :role, :default => "reader"
t.timestamps null: false
end
end
end
app/controllers/users-controller:
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
session[:user_id] = #user.id
redirect_to '/'
else
redirect_to '/signup'
end
end
private
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :password, :role)
end
end
Without knowing the specific error message, I can only speculate that your error is here:
def require_admin
redirect_to '/' unless current_user.admin
end
Regardless of the attributes you have in your model / db, you'll only get instance methods you've defined. You don't have admin in your User object, thus making current_user.admin invalid.
You'd need to use the following:
#app/models/user.rb
class User < ActiveRecord::Base
def admin?
self.role == "admin"
end
end
current_user.admin? #-> true / false
Whilst the question mark isn't required, it denotes the evaluation of an object's properties (true / false).
As an aside, you may want to look at adding an enum to your User model:
#app/models/user.rb
class User < ActiveRecord::Base
enum role: [:reader, :admin]
end
This will give you a series of instance & class methods to better help your logic:
#user = User.find params[:id]
if #user.admin?
...
#admins = User.admin
#-> collection of "admin" users
To do it, you'll need to change your role column from string to integer
I would suggest that you use boolean for the column role.
User.rb
def admin?
self.role == true
end
so you can do
redirect_to '/' unless current_user.admin?

Associating new model with user id

I'm (very) new to ror and have read many tutorials for this issue but none seem to work. I'm trying to let one user create one booth to sell things.
This is my db migration:
class CreateBooths < ActiveRecord::Migration
def change
create_table :booths do |t|
t.string :name
t.references :user, index: true
t.timestamps null: false
end
add_index :booths, [:user_id]
end
end
Here is the booth controller:
class BoothsController < ApplicationController
before_action :logged_in_user
def new
#booth = Booth.new
end
def create
#booth = current_user.booths.build(booth_params)
if #booth.save
flash[:success] = "Congrats on opening your booth!"
redirect_to root_url
else
render 'new'
end
end
private
def booth_params
params.require(:booth).permit(:name)
end
end
And this is the booth model:
class Booth < ActiveRecord::Base
belongs_to :user
validates :user_id, presence: true
end
I also added this to the user model:
has_one :booth, dependent: :destroy
When I include validates :user_id, presence: true it won't save to the db. When I exclude it, it saves but does not include a user id in the database. If you are still reading thank you and I hope you can help!
You need to change create method of your BoothsController to this:
def create
#booth = current_user.build_booth(booth_params)
if #booth.save
flash[:success] = "Congrats on opening your booth!"
redirect_to root_url
else
render 'new'
end
end
Here, you have one-to-one association between user and booth, and that's why you have to instantiate booth for current_user using build_<singular_association_name>, which is build_booth and pass params to it: build_booth(booth_params).
booths.build(booth_params) works for one-to-many association, for example: user has many booths, not vice a versa.

Save originator_id, Activity Feed from Scratch

I am currently following Ryan Bates tutorial on activity feed from scratch.
**I added a originator_id to the database so that I can save the ID of the Owner who originated the post. But for some reason I can't get it to work.
My Database from Scratch
class CreateActivities < ActiveRecord::Migration
def change
create_table :activities do |t|
t.belongs_to :user
t.string :action
t.belongs_to :trackable
t.string :trackable_type
###I want to save the id corresponding to User who created the object
t.belongs_to :originator
t.string :originator_type
t.timestamps
end
add_index :activities, :user_id
add_index :activities, :trackable_id
add_index :activities, :originator_id
end
end
Here is my Code
Models
class Activity < ActiveRecord::Base
belongs_to :user
belongs_to :trackable, polymorphic: true
belongs_to : originator, polymorphic: true
attr_accessible :action, :recipient, :trackable
###how can i set the originator_id value
after_create :set_originator
def set_originator
self.originator.update_attribute(:originator, ???)
end
end
Controllers
class ApplicationController < ActionController::Base
###sets the action and trackable values
###how can i the originator here. i keep getting an error saying undefined method
###why is it that rails recognizes trackable?
def track_activity(trackable, action = params[:action])
current_user.activities.create! action: action, trackable: trackable,
originator: originator
end
end
class LikesController < ApplicationController
def create
#like = Like.create(params[:like])
#dailypost = #like.dailypost
###Used to call track activity method above
track_activity #like
respond_to do |format|
format.js
format.html { redirect_to :back }
end
end
end
Don't know how solid this answer will be as i add more models to the activities, but this worked for my likes model.
If anyone can provide another solution that will work with multiple models i would really appreciate it. :)
class ApplicationController < ActionController::Base
def track_activity(trackable, action = params[:action])
current_user.activities.create! action: action, trackable: trackable,
originator: originator
end
def originator
#like.dailypost.user
end
end

Resources