Im new to CanCan and am using it in Rails 4.2.0. I have Both User and Admin privileges set up and have hit a hiccup when setting can and cannot statement in the abilitiy.rb when trying to set the user permissions that talk to the Visitor_parking Model.
my ability.rb file looks like:
class Ability
include CanCan::Ability
def initialize(user)
if user.admin?
#Grants Admin Permissions to Sites Model
can :create, Site
can :read, Site
can :update, Site
can :destroy, Site
#Grants Admin Permissions to Residents Model
can :create, Resident
can :read, Resident
can :update, Resident
can :destroy, Resident
#Grants Admin Permissions to Vehicles Model
can :create, Vehicle
can :read, Vehicle
can :update, Vehicle
can :destroy, Vehicle
#Grants Admin Permissions to Parking Model
can :create, Parking
can :read, Parking
can :update, Parking
can :destroy, Parking
#Grants Admin Permissions to Visitor Parking Model
can :create, Visitor_parking
can :read, Visitor_parking
can :update, Visitor_parking
can :destroy, Visitor_parking
#Grants Admin Permissions to Trespass Model
can :create, Trespass
can :read, Trespass
can :update, Trespass
can :destroy, Trespass
#Grants Admin Permissions to Reports Model
can :create, Report
can :read, Report
can :update, Report do |report|
report.user == user
end
cannot :destroy, Report
else
#Grants User Permissions to Sites Model
cannot :create, Site
can :read, Site
cannot :update, Site
cannot :destroy, Site
#Grants User Permissions to Residents Model
cannot :create, Resident
can :read, Resident
can :update, Resident
cannot :destroy, Resident
#Grants User Permissions to Vehicles Model
cannot :create, Vehicle
can :read, Vehicle
can :update, Vehicle
cannot :destroy, Vehicle
#Grants User Permissions to Parking Model
can :create, Parking
can :read, Parking
can :update, Parking
cannot :destroy, Parking
#Grants User Permissions to Visitor Parking Model
##can :create, Visitor_parking
##can :read, Visitor_parking
##can :update, Visitor_parking
##cannot :destroy, Visitor_parking
#Grants User Permissions to Trespass Model
can :create, Trespass
can :read, Trespass
cannot :update, Trespass
cannot :destroy, Trespass
#Grants User Permissions to Reports Model
can :create, Report
can :read, Report
can :update, Report do |report|
report.user == user
end
cannot :destroy, Report
end
I know some are going to say that i should use the manage all feature of can can however there will be rules in the admin segment as well so I wanted to CRUD it all out and change as needed.
the Problem lies in the user (else) portion of the code above. (Section commented out with the ## ) the admin privileges work perfectly but when i attempt to set the user privileges i get the following error and am not sure where to go with it.
Error Message I get:
Unable to autoload constant Visitor_parking, expected /Users/TaurenLTD1/Desktop/PatrolPro/Patrol/app/models/visitor_parking.rb to define it
cannot :destroy, Parking
#Grants User Permissions to Visitor Parking Model
--> can :create, Visitor_parking is what is hilighted red in the error message (but matches the admin perfectly)??? --->
can :read, Visitor_parking
can :update, Visitor_parking
cannot :destroy, Visitor_parking
This is what the VisitorParking Model Looks Like in full
class VisitorParking < ActiveRecord::Base
# Adds Relationships to Visitor Parking
belongs_to :user
belongs_to :site
# Adds Import / Export Functionality To Reports
def self.import(file)
CSV.foreach(file.path, headers: true) do |row|
VisitorParking.create! row.to_hash
end
end
def self.to_csv
CSV.generate do |csv|
csv << column_names
all.each do |visitor_parking|
csv << visitor_parking.attributes.values_at(*column_names)
end
end
end
end
i am a bit of a rails amateur and this is the first time I've ever worked with cancan so it may be something small I'm just not picking up... Any help is greatly appreciated!
You spelled VisitorParking wrong: Visitor_parking. Try to fix it.
I'm not sure how it can work in the if branch of your conditional, to be honest.
Related
In my application I have models File & FileAccess. User A can upload files and give access to many other users (by other users asking for access).
My Models:
class File < ActiveRecord::Base
belongs_to :user
has_many :file_accesses
class FileAccess < ActiveRecord::Base
belongs_to :user
belongs_to :file
My FilesController:
class FilesController < ApplicationController
def accepted_users
FileAccess.create(user_id: params[:user_id], file_id: params[:file_id], accept: true)
end
My routes.rb:
get 'i/:user_id/:file_id', to: 'files#accepted_users', as: :file_access_accepted
My View:
= link_to "Give Access", file_access_accepted_path(#file, other_user.id)
Other users can ask for access & user A can select users that they want to give file access to by clicking on Give Access button.
In my FilesController I have a access_file action & view:
class FilesController < ApplicationController
def access_file
redirect_to #file, alert: "You don't have access to this page!" if #file.user != current_user
end
Currently this view/page is only viewable to File owner and if user is not the file owner, they'll be send back with a alert.
How can I achieve it so this page/view is accessible to File owner AND all other users that have been accepted accept: true by the file owner.
Try this one
def access_file
if #file.user != current_user && !#file.file_accesses.map(&:user).include?(current_user)
redirect_to #file, alert: "You don't have access to this page!"
end
end
or
def access_file
if #file.user != current_user && !FileAccess.exists?(file: #file, user: current_user)
redirect_to #file, alert: "You don't have access to this page!"
end
end
The latter should be just one SQL query
I have a basic authorization class in a Rails application which looks like this:
class Ability
include CanCan::Ability
def initialize(user)
if user
can :access, :rails_admin # only allow admin users to access Rails Admin
can :dashboard
if user.admin?
can :manage, :all
else
can :manage, [Agreement, Attachment, Contact, Deadline, Event, Image, Photo, Project, Submission, Talk]
can :update, User, id: user.id
end
end
# Current user cannot delete his account
cannot :destroy, User, id: user.id
end
end
Now, I get an unauthorized error when trying to access the dashboard with a simple user, but once I put can :manage, :all for a simple user condition it is misteriouslly let through and see the dashboard.
What is :manage, :all having more than :manage, [All_my_tables] and why is my user not let in using this way?
Here is the answer, I just need to to :manage, :all for a simple user and then override the permissions.
class Ability
include CanCan::Ability
def initialize(user)
#Check if the user is logged in
if user
#Grant access to the dashboard
can :access, :rails_admin
can :dashboard
can :manage, :all
#Simple user permissions set here
if !user.admin?
alias_action :create, :update, :destroy, to: :cud
can :manage, :all
cannot :cud, User
cannot :destroy, [Agreement, Submission]
end
end
can :update, User, id: user.id #User can edit his/her own account
cannot :destroy, User, id: user.id #User cannot delete his/her own account
end
end
Thanks for the down votes, but this question has been well researched before coming here
I want to restrict access to report models according to roles via CanCan. Specifically, I want :admin roles to manage all, :expert roles create, read and edit all reports ascribed to their team and delete only their own, and user roles to create, read, edit, and delete their own reports.
#reports = report.pending_approval.by_team(current_user.team_id)
Based on the following abilities, and authorize_resource in all relevant controllers, I expected the above query to only return reports the user has created -- but it returns all the reports for a given team. I want the query to return all "pending" reports belonging to a particular team if the current_user is an :expert and is assigned to the team in question.
Does anyone know why it returns all reports belonging to a team, regardless of whether the current_user is an expert or not? Should I modify the query to check for (:expert) role? To limit access to the team expert only, should I modify the query to check for team membership and role in the query or CanCan ability?
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new
if user.role? :admin
can :manage, :all
elsif user.role? :expert
can :read, Report, :user_id => user.id, :submitted => false
can :create, Report
can :update, Report, :user_id => user.id, :submitted => false
can :destroy, Report, :user_id => user.id, :submitted => false
else
can :read, Report, :user_id => user.id, :submitted => false
can :create, Report
can :update, Report, :user_id => user.id, :submitted => false
can :destroy, Report, :user_id => user.id, :submitted => false
end
end
end
The query you gave has no CanCan methods in it, so it doesn't know about your restrictions.
Try using accessible_by to scope the relation:
Report.accessible_by(Ability.new(current_user)).pending_approval # ... etc
I am using devise with CanCan. I am using my user model.
My user index page is localhost:3000/users (It is enabled just for :administrator role).
The problem is that CanCan (or Devise) is not checking for authorization on this route. All other routes (i.e. localhost:3000/tasks) are being checked. i.e. If I logout of the system and type the users index page it displays its content. If I type the tasks route it redirects me to the login screen (correct behaviour).
I think that this is happening because of Devise's routes.
My simplified User model is:
class User < ActiveRecord::Base
has_and_belongs_to_many :roles
# Include default devise modules. Others available are:
# :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :name, :role_ids, :role, :email, :password, :password_confirmation, :remember_me
def role?(role_check)
self.roles.each do |role|
return true if (role.name.eql? role_check.to_s.humanize )
end
return false
end
def role=(role_id)
self.roles.clear
self.roles << Role.find(role_id)
end
def role
self.roles.first unless self.roles.length == 0
end
end
My Routes are the following:
devise_for :users
resources :users
devise_for :users, :controllers => { :registrations => "users/registrations" }
My ability.rb is the following (I havenĀ“t defined yet, it is allowing all):
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user
if user.role? :administrator
can :manage, :all
elsif user.role? :department_header
can :manage, :all
elsif user.role? :staff
can :manage, :all
end
end
end
How can I fix this?
Thanks!
My problem was on the following line:
load_and_authorize_resource :only => [:show,:new,:destroy,:edit,:update]
I have to include :index
Using Rails 3.1.3 with Devise 1.5.3. My app has accounts and users. Each account can have multiple users. A user can have one role, "user" or "account_admin". Devise signup routes to accounts#new. The account and an initial account_admin user are created simultaneously. Got this working as described here (although things have evolved some since then).
An account_admin signs should be able to create additional users in that account. That's where I'm running into trouble: instead of creating new users, it's just redirecting to the user_path (users#show) page with the message "You are already signed in." (user_path is my overridden after_sign_in_path.)
This article asks a similar question. The answer suggests a separate namespace for admins, but since admins are part of my regular app I don't think that applies.
I've defined a complete UsersController. According to the log, GET "/users/new" renders from my "UsersController#new". However POST "/users" is intercepted by Devise and rendered from "Devise::RegistrationsController#create".
config/routes.rb
devise_for :users
devise_scope :user do
get "signup", :to => "accounts#new"
get "signin", :to => "devise/sessions#new"
get "signout", :to => "devise/sessions#destroy"
end
resources :users
resources :accounts
app/controllers/users_controller.rb
class UsersController < ApplicationController
before_filter :authenticate_user!
load_and_authorize_resource # CanCan
...
def new
# CanCan: #user = User.new
end
def create
# CanCan: #user = User.new(params[:user])
#user.skip_confirmation! # confirm immediately--don't require email confirmation
if #user.save
flash[:success] = "User added and activated."
redirect_to users_path # list of all users
else
render 'new'
end
end
...
end
I've tried overriding the Devise controller, thinking I could tell it to use my users#create action if the user is already signed in. The log tells me it is using my controller ("Processing by RegistrationsController#create as HTML"), but it doesn't seem to execute its code. I've commented out my custom actions and just left in the logger lines, but I don't get my logging messages. And in spite of super being commented out, the behavior doesn't change--it still redirects with "You are already signed in."
config/routes.rb
devise_for :users, :controllers => {:registrations => "registrations"}
app/controllers/registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
def new
logger.info "Custom RegistrationsController: new"
super
end
def create
logger.info "Custom RegistrationsController: create"
# super unless user_signed_in?
# render "users#create"
end
def update
super
end
end
What am I missing? How can I let the account_admin user create additional users?
The main issue is that Devise was intercepting the POST "/users" (and a few other routes). Found this workaround to allow my Users controller handle those routes: change the devise_for to skip registrations, then add back in the routes for which Devise normally defines aliases:
routes.rb
devise_for :users, :skip => [:registrations]
devise_scope :user do
get "signup", :to => "accounts#new"
get "signin", :to => "devise/sessions#new"
get "signout", :to => "devise/sessions#destroy"
get "cancel_user_registration", :to => "devise/registrations#cancel"
post "user_registration", :to => "users#create"
get "new_user_registration", :to => "accounts#new"
get "edit_user_registration", :to => "users#edit"
end
resources :users
resources :accounts
Never figured out why the Devise controller override wasn't working.
A user on this thread pointed out the devise_invitable gem which might be an interesting alternative.
This is how I ended up getting this to work with my setup (Devise tied to my User model -- no separate Editor/Admin model):
class UsermakersController < ApplicationController
before_action :authenticate_user!
def new
#user = User.new
end
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
format.html { redirect_to users_path, notice: 'User was successfully created.' }
format.json { render :show, status: :created, location: #user }
else
format.html { render users_path }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
private
def user_params
userParams = params.require(:user).permit(:name, :email)
end
end
From here, when you want to add a new User as another User, simply call the Usermakers#new url in your form, e.g., I have this at the bottom of my Users index page:
<%= link_to 'Add User', new_usermaker_path, id: 'new_user_link' %>
And the Usermakers form looks like:
= simple_form_for #user, url: usermakers_path, html: {multipart: true} do |f|
=f.error_notification errors: true
= f.input :name
= f.input :email
%p
= f.button :submit, 'Create User', class: 'button'
Of course, you'd need to add a dummy new.html.erb file which simply renders _form.html.erb to use this.
Just add the new and create methods to your routes.rb file (whether by resources :usermakers, or more specific routes) and you should be good to go. Hope this helps!