I am a junior developer working on my first web application for a customer, an electronic commerce portal using Rails 4.2.4, Devise and a pins scaffolding.
Devise is working great and anyone can signup and login and CRUD a pin.
Problem: Users cannot be given access to CUD as the pins contain live customer products that are for sale.
I am therfore trying to implement pundit so that users are purely RESTRICTED to read only and CANNOT create, update or destroy pins, Only the business owner can do so in an Admin capacity.
Currently, I am getting the error "Pundit::NotDefinedError in PinsController#new
1.) How can I add myself as the admin(is this through a migration?)
2) How can I get Pundit working.
class PinsController < ApplicationController
before_action :set_pin, only: [:show, :edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
def index
if params[:search].present? && !params[:search].nil?
#pins = Pin.where("description LIKE ?", "%#{params[:search]}%").paginate(:page => params[:page], :per_page => 15)
else
#pins = Pin.all.order("created_at DESC").paginate(:page => params[:page], :per_page => 15)
end
end
def show
end
def new
#pin = current_user.pins.build
authorize #pins
end
def edit
end
def create
#pin = current_user.pins.build(pin_params)
if #pin.save
redirect_to #pin, notice: 'Pin was successfully created.'
else
render :new
end
end
def update
if #pin.update(pin_params)
redirect_to #pin, notice: 'Pin was successfully updated.'
else
render :edit
end
end
def destroy
#pin.destroy
redirect_to pins_url
end
private
# Use callbacks to share common setup or constraints between actions.
def set_pin
#pin = Pin.find_by(id: params[:id])
end
def correct_user
#pin = current_user.pins.find_by(id: params[:id])
redirect_to pins_path, notice: "Not authorized to edit this pin" if #pin.nil?
end
# Never trust parameters from the scary internet, only allow the white list through.
def pin_params
params.require(:pin).permit(:description, :image)
end
end
Blockquote
class ApplicationPolicy
attr_reader :user, :pin
def initialize(user, pin)
raise Pundit::NotAuthorizedError, "must be logged in" unless user
#user = user
#pin = pin
end
def index?
true
end
def show?
scope.where(:id => record.id).exists?
end
def create?
user.admin?
end
def new?
user.admin?
end
def update?
user.admin?
end
def edit?
update?
end
def destroy?
user.admin?
end
def scope
Pundit.policy_scope!(user, record.class)
end
class Scope
attr_reader :user, :scope
def initialize(user, scope)
#user = user
#scope = scope
end
def resolve
scope
end
end
end
Blockquote
class ApplicationController < ActionController::Base
include Pundit
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
protect_from_forgery with: :exception
before_filter :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) << :name
devise_parameter_sanitizer.for(:account_update) << :name
end
private
def user_not_authorized
flash[:warning] = "You are not authorized to perform this action."
redirect_to(request.referrer || root_path)
end
end
Blockquote
class Pin < ActiveRecord::Base
belongs_to :user
has_attached_file :image, :styles => { :medium => "300x300>", :thumb => "100x100>" }
validates_attachment_content_type :image, :content_type => ["image/jpg", "image/jpeg", "image/png"]
validates :image, presence: true
validates :description, presence: true
end
Blockquote
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :pins, dependent: :destroy
validates :name, presence: true
end
Related
Don't know why this is happening here.
NoMethodError in PostsController#update
undefined method `user' for nil:NilClass
My user has admin : true and I can't update other users.posts.
I want to let all users see the content, but only registered users can create content. And if the user is admin or the creator of that content he can edit/update/destroy it as well.
post.rb
class Post < ApplicationRecord
belongs_to :user
has_many :comments, dependent: :destroy
validates :title, presence: true, length: { minimum: 5 }
validates :body, presence: true, length: { minimum: 240 }
end
user.rb
class User < ApplicationRecord
include Encryptable
has_many :posts, dependent: :destroy
has_many :comments, dependent: :destroy
has_attached_file :avatar
validates_attachment_content_type :avatar, content_type: /\Aimage\/.*\z/
validates :level, numericality: { less_than_or_equal_to: 100, only_integer: true }, allow_blank: true
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable
before_validation :downcase_email #, :populate_iv_fields #if you need/want iv to change more often
before_create :create_encryption_key
after_create :save_encryption_key
after_create :build_user_consents
attr_encrypted :email, key: :encryption_key
has_many :user_consents, dependent: :destroy
#entry point for exporting user's personal information
def self.export_personal_information(user_id)
return nil unless User.exists?(user_id)
descendants = ApplicationRecord.descendants.reject{|model| !model.has_personal_information?}
result = Hash.new
descendants.each do |descendant|
result[descendant.name] = descendant.export_personal_information_from_model(user_id)
end
return result
end
#simplest example, we just export to json
def self.export_personal_information_from_model(user_id)
return User.find(user_id).to_json
end
#overwrite this to true for methods that you will want to be included in export_personal_information
def self.has_personal_information?
true
end
#helper method if you are creating a user from console and want them to have all consents set
def fill_consents
hash = Hash.new
ConsentCategory.all.map(&:id).each do |id|
hash[id]='on'
end
self.registration_consents=hash
end
#unfortunately not having an email field that you can just "write to" breaks
#Devise. Below some necessary workarounds
def email_changed?
encrypted_email_changed?
end
def downcase_email
self.email = self.email.downcase
end
def registration_consents=(consents)
#consents = consents
end
def registration_consents
#consents
end
validate :validate_consents_completeness
validates_presence_of :email, if: :email_required?
validates_uniqueness_of :username, allow_blank: false, if: :username_changed?
validates_length_of :username, within: 6..20, allow_blank: true
validate :validate_email_uniqueness #validates_uniqueness_of :email, allow_blank: true, if: :email_changed?
validates_format_of :email, with: Devise.email_regexp, allow_blank: true, if: :email_changed?
validates_presence_of :password, if: :password_required?
validates_confirmation_of :password, if: :password_required?
validates_length_of :password, within: Devise.password_length, allow_blank: true
def password_required?
!persisted? || !password.nil? || !password_confirmation.nil?
end
#end original devise
def email_changed?
self.encrypted_email_changed?
end
def email_required?
true
end
def email_unique?
records = Array(self.class.find_by_email(self.email))
records.reject{|u| self.persisted? && u.id == self.id}.empty?
end
#unfortunately, this is an O(n) operation now that has to go through ALL the users to see if an email is unique. Sorry!
#if you need it to ne O(1) then consider adding email_hash field instead
def self.find_by_email(email)
users = User.all
users.each do |user|
return user if user.email.downcase == email.downcase
end
return nil
end
protected
def validate_email_uniqueness
errors.add(:email, :taken) unless email_unique?
end
def validate_consents_completeness
return if self.id #we assume that already created user has all consents
errors.add(:registration_consents, 'Sie müssen allen erforderlichen Bedingungen zustimmen um fortzufahren.') and return unless self.registration_consents
consents = ConsentCategory.where(mandatory: true).map(&:id)
ids = self.registration_consents.keys.map(&:to_i) #we are relying on a fact that checkboxes that are not checked are not sent to Rails back-end at all
consents.each do |consent_type|
errors.add(:registration_consents, 'Sie müssen allen erforderlichen Bedingungen zustimmen um fortzufahren.') and return unless ids.include?(consent_type)
end
end
def build_user_consents
ids = registration_consents.keys
categories = ConsentCategory.where(id: ids)
raise 'Die vom Benutzer eingereichte Zustimmungsliste enthält Objekte, die nicht in der Datenbank vorhanden sind!' if categories.count != ids.count
categories.each do |category|
consent = UserConsent.new
consent.consent_category = category
consent.user = self
consent.requires_revalidation = false
consent.agreed_at = self.created_at
consent.save!
end
end
end
post_policy.rb
class PostPolicy < ApplicationPolicy
attr_reader :user, :post
def initialize(user, post)
#user = user
#post = post
end
def index?
true
end
def create?
user.present?
end
def new?
user.present?
end
def update?
return true if post.user_id == user.id || user == user.admin?
end
def destroy?
return true if post.user_id == user.id || user == user.admin?
end
private
def post
record
end
end
application_policy.rb
class ApplicationPolicy
attr_reader :user, :record
def initialize(user, record)
#user = user
#record = record
end
def index?
false
end
def show?
false
end
def create?
false
end
def new?
create?
end
def update?
user.admin?
end
def edit?
user.admin?
end
def destroy?
user.admin?
end
class Scope
attr_reader :user, :scope
def initialize(user, scope)
#user = user
#scope = scope
end
def resolve
scope.all
end
end
end
post_controller
class PostsController < ApplicationController
before_action :find_post, only: %i[destroy edit update comment_owner upvote downvote]
after_action :verify_authorized, except: [:index, :show]
layout '_app_nav'
def index
return redirect_to post_path(params[:post_id]) if params[:post_id]
return redirect_to user_path(params[:user_id]) if params[:user_id]
#post = Post.all.order('created_at DESC')
#posts = Post.all.order('created_at DESC')
#user = User.all
#posts = if params[:suche]
else
Post.all.order('created_at DESC')
end
#comments = Comment.all
end
def new
#post = Post.new
end
def create
#post = current_user.posts.build(post_params)
authorize #post
if #post.save!
redirect_to #post
else
render 'new'
end
end
def show
#post = Post.find(params[:id])
#user = #post.user
#comments = Comment.where(post_id: #post).order('created_at DESC').paginate(:page => params[:page], :per_page => 5)
end
def edit
authorize #post
#post = Post.find(params[:id])
end
def update
#user = User.find(params[:id])
#post = Post.find(params[:id])
authorize #post
#post.update(title: params[:title], body: params[:post_params])
redirect_to post_path(#post)
end
def destroy
#post = Post.find(params[:id])
#post.destroy
authorize #post
redirect_to posts_path
end
=begin
def upvote
#post.upvote_from current_user
redirect_to post_path(#post)
end
def downvote
#post.downvote_from current_user
redirect_to post_path(#post)
end
=end
private
def post_params
params.require(:post).permit(:title, :body, :user_id)
end
def find_post
#post = Post.find(params[:id])
end
end
application_controller
class ApplicationController < ActionController::Base
include Pundit
protect_from_forgery with: :exception
before_action :authenticate_user!
before_action :configure_permitted_parameters, if: :devise_controller?
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up) do |user_params|
user_params.permit(:username, :email, :password, :password_confirmation, registration_consents: {})
end
end
private
def user_not_authorized
flash[:alert] = 'test'
redirect_to(root_path)
end
end
registrations_controller:
class RegistrationsController < Devise::RegistrationsController
private
def account_update_params
params.require(:user).permit(:email, :username, :avatar, :current_password, :password, :password_confirmation)
end
end
edit:
updating my post_policy.rb with #post such as return true if user.present? && user == #post.user || user == user.admin? resolves in -> Pundit::NotAuthorizedError in PostsController#update
not allowed to update?
In your PostsController, you need to include update method inside array where you specify before which methods should authenticate_user before action run:
before_action :authenticate_user!, only: [:create, :destroy, :new, :edit, :update]
If you're using devise you can use this callback, in your application_controller.rb to validate you have a user logged in.
before_action :authenticate_user!
With that you avoid doing
user.present?
Your post_policy.rb should look like this
class PostPolicy < ApplicationPolicy
attr_reader :user, :post
def initialize(user, post)
#user = user
#post = post
end
def index?
true
end
def create?
user.present?
end
def new?
user.present?
end
def update?
return true if record.user_id == user.id || user == user.admin?
end
def destroy?
return true if record.user_id == user.id || user == user.admin?
end
private
def post
record
end
end
Also, to avoid that users can enter the links in the browser, you can do an extra step on you controller, which is adding the following callback and method
class PostsController < ApplicationControl
before_action :authorization
private
def authorization
authorize(Post)
end
end
EDIT:
Make sure your ApplicationController looks like this one to prevent the error Pundit::NotAuthorizedError.
class ApplicationController < ActionController::Base
include Pundit
before_action :authenticate_user!
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
private
def user_not_authorized
flash[:alert] = 'You are not authorized to perform this action.'
redirect_to(root_path)
end
end
i have a rails app with users and posts, i have added another sacffold called channels, now the relationship goes like the user can create both post and channels and the channels belongs to users, post belongs to both users and channels, to add user id to channels i have created a migration, everything looks good but i am getting this error while creating a channel.
(Here is a screenshot of the exact error)
This is what am getting on the command line:
Started POST "/channels" for 127.0.0.1 at 2017-12-16 13:30:32 +0530
Processing by ChannelsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"IJvvEe+TR6buacH5UwtiLSJglMkq7a+Q4x7VOTqALcGka4j6tG7lPi/7kYnCQ/nzmO7PNe2eSan3sBz9NqKV2g==", "channel"=>{"name"=>"dhfkdhfk", "description"=>"jdfjdfh", "tagline"=>"jdfjdfhj", "category"=>"jdfjdf", "avatar"=>""}, "commit"=>"Create Channel"}
User Load (2.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT 1 [["id", 1]]
(2.6ms) BEGIN
(1.8ms) ROLLBACK
Rendered channels/_form.html.erb (11.5ms)
Rendered channels/new.html.erb within layouts/application (13.3ms)
Rendered layouts/_avatar_dropdown.html.erb (7.4ms)
Rendered layouts/_header.html.erb (12.6ms)
Rendered layouts/_alert_messages.html.erb (0.5ms)
Completed 200 OK in 367ms (Views: 345.3ms | ActiveRecord: 6.7ms)
Add_user_id_to_channel.rb
class AddUserIdToChannels < ActiveRecord::Migration
def change
add_reference :channels, :user, index: true, foreign_key: true
end
end
channel.rb
class Channel < ActiveRecord::Base
validates :name, :description, :user_id, presence: true
belongs_to :user
has_many :posts, dependent: :destroy
end
user.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:omniauthable, :omniauth_providers => [:facebook, :twitter, :google_oauth2]
act_as_mentionee
validates :username, presence: true
validate :avatar_image_size
has_many :posts, dependent: :destroy
has_many :channels, dependent: :destroy
has_many :responses, dependent: :destroy
has_many :likes, dependent: :destroy
after_destroy :clear_notifications
after_commit :send_welcome_email, on: [:create]
mount_uploader :avatar, AvatarUploader
include UserFollowing
include TagFollowing
include SearchableUser
include OmniauthableUser
private
# Validates the size on an uploaded image.
def avatar_image_size
if avatar.size > 5.megabytes
errors.add(:avatar, "should be less than 5MB")
end
end
# Returns a string of the objects class name downcased.
def downcased_class_name(obj)
obj.class.to_s.downcase
end
# Clears notifications where deleted user is the actor.
def clear_notifications
Notification.where(actor_id: self.id).destroy_all
end
def send_welcome_email
WelcomeEmailJob.perform_later(self.id)
end
end
user_controller
class UsersController < ApplicationController
before_action :authenticate_user!, only: [:edit, :update]
before_action :authorize_user, only: [:edit, :update]
before_action :set_user, only: [:show, :edit, :update]
def show
#followers_count = #user.followers.count
#following_count = #user.following.count
#latest_posts = #user.posts.latest(3).published
#recommended_posts = #user.liked_posts.latest(4).published.includes(:user)
end
def update
if #user.update(user_params)
redirect_to #user
else
render :edit, alert: "Could not update, Please try again"
end
end
private
def set_user
#user = User.find(params[:id])
end
def user_params
params.require(:user).permit(:description, :avatar, :location, :username)
end
def authorize_user
unless current_user.slug == params[:id]
redirect_to root_url
end
end
end
channel_controller
class ChannelsController < ApplicationController
before_action :set_channel, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:show]
before_action :authorize_user, only: [:edit, :update, :destroy]
# GET /channels
# GET /channels.json
def index
#channels = Channel.all
end
# GET /channels/1
# GET /channels/1.json
def show
end
# GET /channels/new
def new
#channel = Channel.new
#channel = current_user.channels.build
#user = current_user
end
# GET /channels/1/edit
def edit
end
# POST /channels
# POST /channels.json
def create
#channel = current_user.channels.build(channel_params)
#channel = Channel.new(channel_params)
#user = current_user
respond_to do |format|
if #channel.save
format.html { redirect_to #channel, notice: 'Channel was successfully created.' }
format.json { render :show, status: :created, location: #channel }
else
format.html { render :new }
format.json { render json: #channel.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /channels/1
# PATCH/PUT /channels/1.json
def update
respond_to do |format|
if #channel.update(channel_params)
format.html { redirect_to #channel, notice: 'Channel was successfully updated.' }
format.json { render :show, status: :ok, location: #channel }
else
format.html { render :edit }
format.json { render json: #channel.errors, status: :unprocessable_entity }
end
end
end
# DELETE /channels/1
# DELETE /channels/1.json
def destroy
#channel.destroy
respond_to do |format|
format.html { redirect_to channels_url, notice: 'Channel was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_channel
#channel = Channel.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def channel_params
params.require(:channel).permit(:name, :description, :tagline, :category, :avatar, :user_id)
end
def authorize_user
begin
#channel = current_user.channels.find(params[:id])
rescue
redirect_to root_url
end
end
end
In model of channel you made it 'user_id' compulsory filed by adding presence: true
validates :name, :description, :user_id, presence: true
so you have to pass user_id in your parameters. you can do that by adding hidden_filed in your view as
<%= f.hidden_field :user_id, value: #user %>
or if you don't required 'user_id' remove form validates like these:
validates :name, :description, presence: true
Change
#channel = current_user.channels.build(channel_params)
#channel = Channel.new(channel_params)
To
#channel = current_user.channels.build(channel_params)
I'm using devise for my user auth and registration. I can register a user no problem. Im also using friendly. My issue is, I can only create one user profile.
The setup...
user.rb:
class User < ActiveRecord::Base
extend FriendlyId
friendly_id :name, use: :slugged
validates :name, uniqueness: true, presence: true
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_one :profile # each user should have just one profile
end
profile.rb:
class Profile < ActiveRecord::Base
belongs_to :user
end
profiles_controller.rb:
class ProfilesController < ApplicationController
before_action :authenticate_user!
before_action :only_current_user
def new
# form where a user can fill out their OWN profile
#user = User.friendly.find( params[:user_id] )
#profile = Profile.new
end
def create
#user = User.friendly.find( params[:user_id] )
#profile = #user.build_profile(profile_params)
if #profile.save # Not saving!!
flash[:success] = 'Profile Created!'
redirect_to user_path( params[:user_id] )
else
render action: :new # keeps rendering!
end
end
private
def profile_params
params.require(:profile).permit(:first_name, :last_name, :avatar, :job_title, :phone_number, :business_name)
end
end
Why is it that only one user can create a profile and not others? Is it has to do with the relations?
We use this setup with some of our apps - User -> Profile.
In short, you should build the profile at User creation. Then you can edit the profile as you need. Your problem of having a Profile.new method is very inefficient...
#app/models/user.rb
class User < ActiveRecord::Base
has_one :profile
before_create :build_profile #-> saves blank associated "Profile" object after user create
end
This will mean that each time a User is created, their corresponding Profile object is also appended to the db.
This will give you the capacity to edit the profile as required:
#config/routes.rb
resources :users, path_names: { edit: "profile", update: "profile" }, only: [:show, :edit, :update]
This will give you the opportunity to use the following:
#app/controllers/users_controller.rb
class UsersController < ApplicationController
before_action :authenticate_user!, only: [:edit, :update]
before_action :authorize, only: [:edit, :update]
def show
#user = User.find params[:id]
end
def edit
#user = current_user
end
def update
#user = current_user
#user.update user_params
end
private
def authorize
id = params[:id]
redirect_to user_show_path(id) if current_user.id != id #-> authorization
end
def user_params
params.require(:user).permit(:x, :y, :z, profile_attributes: [:homepage, :other, :profile, :attributes])
end
end
The view/form would be the following:
#app/views/users/edit.html.erb
<%= form_for #user do |f| %>
<%= f.fields_for :profile do |f| %>
<%= f.text_field :homepage %>
...
<% end %>
<%= f.submit %>
<% end %>
In regards your current setup:
def new
#profile = current_user.profile.new
end
def create
#profile = current_user.profile.new profile_params
if #profile.save
redirect_to user_path(params[:id]), notice: "Profile Created!"
else
render action: :new
end
end
private
def profile_params
params.require(:profile).permit(:x, :y, :z)
end
Not sure why you don't create the profile in the after_create event of the user. As soon the user is created - create an empty (but associated) profile.
class User
has_one :profile, dependent: :destroy
after_create {
build_profile unless profile
profile.save
}
end
class Profile
belongs_to :user, autosave: true
end
so then, in your controller you just need the update method.
def update
if current_user.profile.update_attributes(user_params)
flash_it :success
return redirect_to edit_user_profile_path
else
flash_it :error
render :edit
end
end
I have set up users with devise and each user can select a role. What I am trying to do is allow admins to be able to edit any user on the site if they have role admin. I currently have a UsersController setup like this:
class UsersController < ApplicationController
before_filter :authenticate_user!, only: [:index, :new, :edit, :update, :destroy]
skip_before_filter
def index
#users = User.order('created_at DESC').all
end
def show
#user = User.friendly.find(params[:id])
#users_authors = User.all_authors
end
# get authors index in here
def authors
end
def create
#user = User.create(user_params)
end
def edit
#user = User.find(params[:id])
end
def update
respond_to do |format|
if #user.update(user_params)
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
def destroy
#user = User.find(params[:id])
#user.destroy
if #user.destroy
redirect_to users_url, notice: "User deleted."
end
end
private
def user_params
params.require(:user).permit(:avatar, :email, :name, :biography, :role_id, :book_id, :username, :password, :password_confirmation)
end
end
This is trying to create a CRUD to edit users which works but I need to be able to populate the forms in the users/edit view wityh the correct selected users details. I my devise controller I have this setup:
class Admin::UsersController < Admin::BaseController
helper_method :sort_column, :sort_direction
before_filter :find_user, :only => [:edit, :update, :show, :destroy]
def index
#q = User.search(params[:q])
#users = find_users
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
redirect_to admin_users_path, :notice => "Successfully created user."
else
render :new
end
end
def show
end
def edit
#user = User.find(params[:id])
end
def update
if #user.update_attributes(user_params)
redirect_to admin_users_path, :notice => "Successfully updated user."
else
render :edit
end
end
def destroy
#user.destroy
redirect_to admin_users_path, :notice => "User deleted."
end
protected
def find_user
#user = User.find(params[:id])
end
def find_users
search_relation = #q.result
#users = search_relation.order(sort_column + " " + sort_direction).references(:user).page params[:page]
end
def sort_column
User.column_names.include?(params[:sort]) ? params[:sort] : "created_at"
end
def sort_direction
%w[asc desc].include?(params[:direction]) ? params[:direction] : "desc"
end
private
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params.require(:user).permit(:email,:username,:name,:biography,:role_id,:book_id,:role_name,:password,:password_confirmation,:encrypted_password,:reset_password_token,:reset_password_sent_at,:remember_created_at,:sign_in_count,:current_sign_in_at,:last_sign_in_at,:current_sign_in_ip,:last_sign_in_ip)
end
end
For clarity here is the user model:
class User < ActiveRecord::Base
belongs_to :role
has_many :books, dependent: :destroy
has_many :ideas, dependent: :destroy
accepts_nested_attributes_for :books
accepts_nested_attributes_for :ideas
def confirmation_required?
false
end
extend FriendlyId
friendly_id :username, use: [:slugged, :finders]
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable
has_attached_file :avatar, styles: {
large: "600x450#",
medium: "250x250#",
small: "100x100#"
}, :default_url => "/images/:style/filler.png"
#validates_attachment_content_type :avatar, :content_type => ["image/jpg", "image/jpeg", "image/png", "image/gif"]
validates_attachment_content_type :avatar, :content_type => /\Aimage\/.*\Z/
validates :avatar, :email, :username, :password, presence: true
def self.all_authors
User.select('users.id, users.username, users.role_id AS USER_ROLE')
.joins(:role).where(users: {role_id: '2'})
end
before_create :set_default_role
private
def set_default_role
self.role ||= Role.find_by_name('Admin')
end
end
In my routes I added a new route for users below the devise users resource as suggested on devise wiki like so:
devise_for :users, :path_prefix => 'my', :path_names => { :sign_up => "register" }
namespace :admin do
resources :users
end
Can anyone help with adding the ability of admins being able to edit all users here, I think its right but I cannot get the correct data into the forms in edit, it uses the current logged users details only.
A first draft for your ability.rb would be:
class Ability
include CanCan::Ability
def initialize(user)
# ...
if user.admin?
can :manage, User
end
# ...
end
end
And then in your user's controller remove the before_filter :find_user, :only => [:edit, :update, :show, :destroy] and related method, and use
load_and_authorize_resource :user
That would load the user from the URL and authorize! it using CanCan. You'll also need to handle the CanCan::AccessDenied exception for non-admin users visiting those pages, but that is another question that you can check in the CanCan docs.
When you visit admin_users_path routes you'll be able to CRUD them if you have the views ready and working.
I have 2 controllers User and Rota. I want the user to be able to create their own Rota but only be able to edit, show and destroy their own. I need to be able to code so that my rotum object belongs to the user object.
ROTA CONTROLLER:
class RotaController < ApplicationController
respond_to :html, :xml, :json
before_action :set_rotum, only: [:show, :edit, :update, :destroy]
def edit
#rotum = #user.rota.find params[:id]
end
def index
#rota = Rotum.all
respond_with(#rota)
end
def show
respond_with(#rotum)
end
def new
#rotum = Rotum.new
respond_with(#rotum)
end
def edit
end
def create
#rotum = Rotum.new(rotum_params)
#rotum.save
respond_with(#rotum)
end
def update
#rotum.update(rotum_params)
respond_with(#rotum)
end
def destroy
#rotum.destroy
respond_with(#rotum)
end
private
def set_rotum
#rotum = current_user.rotums.find(params[:id])
if #rotum.nil?
render :html => "Not authorized", :status => 401
end
end
def rotum_params
params.require(:rotum).permit(:name, :email, :mobile, :category)
end
end
USER CONTROLLER
class UsersController < ApplicationController
before_filter :authenticate_user!
after_action :verify_authorized
def index
#users = User.all
authorize User
end
def show
#user = User.find(params[:id])
authorize #user
end
def update
#user = User.find(params[:id])
authorize #user
if #user.update_attributes(secure_params)
redirect_to users_path, :notice => "User updated."
else
redirect_to users_path, :alert => "Unable to update user."
end
end
def destroy
user = User.find(params[:id])
authorize user
user.destroy
redirect_to users_path, :notice => "User deleted."
end
def edit
#rotum = #user.rota.find params[:id]
end
private
def secure_params
params.require(:user).permit(:role)
end
end
So far my rota allows anyone to create, show, edit and destroy the rota on the rotas page. I only want the user to be able to edit only THEIR OWN rota that they created. For that I have been told tell the rota object to belong to the user object. How can I do this in my controllers or models.
USER MODEL
class User < ActiveRecord::Base
has_many :rota, dependent: :destroy
enum role: [:user, :vip, :admin]
after_initialize :set_default_role, :if => :new_record?
def set_default_role
self.role ||= :user
end
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
ROTUM MODEL
class Rotum < ActiveRecord::Base
belongs_to :user
end
I get the error:
NoMethodError at /rota/15
undefined method `rotums' for #
You are not showing your models, so I'm assuming that you have a has_many relationship
class User < ActiveRecord::Base
has_many :rota, dependent: :destroy
end
class Rotum < ActiveRecord::Base
belongs_to :user
end
then in your controller you can use the following:
class UsersController < ApplicationController
....
def edit
#rotum = #user.rota.find params[:id]
end
Note that this will raise an ActiveRecord::RecordNotFound exception if a user is triying to edit a rotum that does not belong to him.
You can avoid that problem with the following:
class UsersController < ApplicationController
....
def edit
#rotum = #user.rota.find_by id: params[:id] # returns nil in case the record does not exist or does not belong to #user
redirect_to "somewhere", alert: 'You cannot edit this element' if #rotum.blank?
end