module Abilities
class ProjectsAbility < Ability
def abilities
can :manage, Project, user_id: user.id
if user.is?(:super_admin)
can :read, Project
end
if user.is?(:district_admin)
can [:read, :manage_details, :download], Project, user: { district: { id: user_district_ids } }
end
if user.is?(:school_admin)
can [:read, :manage_details, :download], Project, user: { school_id: user_school_ids || user.district.school_ids }
end
if user.is?(:advisor)
can [:read, :manage_details, :download], Project, user: { advisors: { id: user.id } }
end
# alias_action :manage_details, :download, to: :read
end
end
end
As district_admin, school_admin and advisor has same type of abilities. It can be agreed upon that all the users that have read access, have access to manage_details and download functionality.
So I was thinking that maybe I could achieve something like
alias_action :manage_details, :download, to: :read
But upon doing this I receive
CanCan::Error
You can't specify target (read) as alias because it is real action name
The reason I decided to go with the above approach because I don't want to place
authorize! :read, #project in the controller action
However if I do
alias_action :read, :manage_details, :download, to: :view_manage_details
and give users access to view_manage_details it works fine. Is there something that can be done to achieve alias_action :manage_details, :download, to: :read or is it something that requires extra mile work?
Related
All the other authorizations for other forms are working fine. But for this create form below, the authorization keeps letting the form to be seen by others.
<%= form_for([#book]) do |form| %>
Authorization code:
<% if can? :create, #book %>
When I switch #book to Book or [#user, #book] or [#book, #post], none works. #user, #book hides the form to both the current user and other users.
What can I do?
Ability.rb:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new
if user.present?
can :read, Book
can :read, Post
can :read, Comment
can :manage, Book, user_id: user.id
can :manage, Post, book: { user: { id: user.id } }
can :manage, Comment, post: { book: { user: { id: user.id }}}
end
```
Ok I think your mistake is Here:
can :manage, Book, user_id: user.id
Basically in create action your object has no user_id yet because your object is not saved. So you should update your ability files with the following code:
can [:read, :create], Book
can [:destroy, :update], Book, user_id: user. id
Let me know.
Edit
Ok I Am missing a point. You have a Statement to check if you have a current_user above, so I think you don't need to add user_id: in your Book ability.
Can you please try: can manage, Book
Also be sure to restart your server, sometimes abilities can be capricious.
I have declared a role ability in the cancancan gem (rails 5, postgres, devise):
can [:update, :show, :destroy, :index], SalesDatum do |datum|
datum.try(:user_id) == user.id #the ids just access the id's from the user object that is passed into can can and from the table
end
SalesDatum has a user_id field.
This works on the show actions, because I can only show the SalesDatum that has the logged in user_id For example:
http://localhost:8080/projects/19/sales_data/961
correctly gets authenticated because the logged in user_id matches the user_id on the sales_data
http://localhost:8080/projects/19/sales_data/800
correctly does not get authenticated because the logged in user_id does not match the user_id on the sales_data
However when I go to the get index action:
http://localhost:8080/projects/19/sales_data
it shows all of the sales data from the #sales_data variable. So, it would show the data.id 800 and 961.
sales data controller:
load_and_authorize_resource
def index
#sales_data = SalesDatum.where(project_id:params[:project_id])
end
How do I get the index action to only show the data with the relevant user_id? Isn't cancan supposed to filter that according to the user_id?
You need to define the rule with the hash syntax.
can [:update, :show, :destroy, :index], SalesDatum, user_id: user.id
you may also want to define the edit action and therefore:
can [:edit, :update, :show, :destroy, :index], SalesDatum, user_id: user.id
which can be simplified into:
can :manage, SalesDatum, user_id: user.id
if SalesDatum has a belongs_to :user defined you can also write:
can :manage, SalesDatum, user: user
I have a rails application in which I'm trying to add a future to ban existing users. My react request is as follows:
handleUserBan(evt) {
evt.preventDefault();
let user_id = evt.target.dataset.userId;
API.put('admin/users/'+user_id, {user: {banned: true}}, function(res) {
this.loadUsers();
}.bind(this))
}
And my 'UsersController' inside admin namespace is:
before_action :enforce_admin!
def show
#user = User.find(ban_params[:id])
end
def update
#user = User.find(ban_params[:id])
prms = ban_params
if prms.include?(:banned)
#user.update_attributes!(prms)
#user.save!
return render :status=>200, :json => {success: true}
end
end
private
def ban_params
params.require(:user).permit(:banned)
end
But I'm getting an error:
ActiveRecord: Record not found
Couldn't find User with 'id'=
Even though a user exists with the selected id in my database. My request is structured as follows:
Request
Parameters:
{"user"=>{"banned"=>"true"},
"id"=>"7",
"format"=>"json"}
And here are my routes for admin namespace:
namespace :admin do
put 'ban_user', :to => 'users#ban_user'
resources :charges
resources :coaches
resources :events
resources :invoices
resources :reviews
resources :users
end
try just params[:id] instead of ban_params[:id]
method ban_params will return only value of banned from params. In this case params contains { id: 'user_id', action: "your action", controller: 'controller', ..., user: { banned: true } }
def ban_params
params.require(:user).permit(:banned)
end
This code is filtering out the id parameter, since it's only permitting the banned parameter:
def ban_params
params.require(:user).permit(:banned)
end
Something like this might work, although it loses the permit constraint:
params.permit(:id, :user => [:banned])
I'm trying to define abilities in CanCanCan.
I can't figure out the syntax to get started.
I use Role Model for my roles and the roles are defined in my Profile.rb. Profile.rb belongs to User.rb.
I am trying to check if the user has the role :student.
When I try:
if {user_signed_in?, user.profile.has_role? :student}
I get a syntax error that says:
syntax error, unexpected ',', expecting =>
if {user_signed_in?, user.profile.has_role? :student}
When I try:
if {user_signed_in? && user.profile.has_role? :student}
I get a syntax error that says:
syntax error, unexpected tSYMBEG, expecting =>
if {user_signed_in? && user.profile.has_role? :student}
I have also tried replacing the curly braces with regular brackets and removing them altogether.
When I try removing the devise part (user_signed_in) and using the suggestion in the comments below, I try:
if user.profile.has_role?(:student)
And I get this error:
undefined method `has_role?' for nil:NilClass
When I try:
if user.profile.has_role? :student
I get this error:
undefined method `has_role?' for nil:NilClass
When I try:
if user.profile.has_role?(student)
I get this error:
undefined local variable or method `student' for #<Ability:0x007fd034a78968>
I have the following roles defined in my profile.rb:
roles :admin, :manager, #
:student, :educator, :researcher, :ktp, :faculty_manager, :ip_asset_manager, # for universities
:sponsor, # for industry
:project_manager, :representative, # for both uni and industry
:grantor, :investor, :adviser, :innovation_consultant, :panel_member, # external
:participant, :guest # public
When I try:
can :crud, Profile, :user_id => user.id if user.profile.has_role? :student
I don't get any errors, but my problem with this approach is that a student can do many things (there are 10 lines of permissions, so I would need to add the if statement individually to each of the 10 'can' statements, unless there is a way that the if statement can be applied to all lines, before the next 'elsif' statement.
The first part of my ability.rb is pasted below (there are a lot of roles and a lot of abilities, so I haven't pasted the whole thing).
class Ability
include CanCan::Ability
def initialize(user)
alias_action :create, :read, :update, :destroy, :to => :crud
# Define abilities for the passed in user here. For example:
#
user ||= User.new # guest user (not logged in)
#users who are not signed in can create registration or login
# can read publicly available projects, programs and proposals
can :read, Project, {:active => true, :closed => false, :sweep => { :disclosure => { :allusers => true } } }
# {:active => true, :closed => false && :Project.sweep.disclosure.allusers => true}
# if user role is student
can :crud, Profile, :user_id => user.id if user.profile.has_role? :student #[for themselves]
can :read, ProjectInvitation, {:student_id => #current_user && :expiry_date_for_students_to_claim_project > Time.now}
# can read project invitations addressed to them
# can read projects to which they are invited whilst the invite is available to accept;
can :read, Project, {} #if invited to the project?
# and they can create responses to invitations to those projects
can :update, ProjectInvitation.student_accepted
# they can create questions on those projects before the invite expiry date;
can :read, ProjectQuestion, {} #if intvited
can [:create, :update, :destroy], ProjectQuestion #if they created the question
can :read, ProjectAnswer #if its on a project they can see
# they can update term sheets and template agreements for those projects
can [:read, :update], TermSheet #{where created for project in which they are participating}
can [:read, :update], ProjectAgreement #{where created for a project in which they are participating}
can [:read, :update], FinanceAgreement #{where created for a project in which they are participating}
can [:read, :update], Nda #{where created for a project in which they are participating}
can [:create, :update], Feedback #{where created for a project in which they are participating and the feedback is on other project team members and the project is completed}
elsif user.profile.has_role? :educator
When I try (the suggestion below):
if user.try(:profile).present? && user.profile.has_role? :student
I get this error:
syntax error, unexpected tSYMBEG, expecting keyword_then or ';' or '\n'
...nt? && user.profile.has_role? :student
Please can someone see what I'm doing wrong?
For following codes
if {user_signed_in?, user.profile.has_role? :student}
if {user_signed_in? && user.profile.has_role? :student}
if {user_signed_in? && user.profile.has_role? :student}
You can not user {} for an ruby statement, it expect a key value pair. You can rewrite your code as following
if user_signed_in? && user.profile.has_role? :student
But you are getting an null pointer errors, so you have fix it in you codes as following, first check if user.try(:profile).present? then you can call user.profile.has_role? :student because your profile getting nil.
if user.try(:profile).present? && user.profile.has_role?(:student)
I've started a Rails application with Devise and CanCan. I have users which has a one-to-many relationship to articles. I'm new to CanCan, here's what I'm planning to do:
Admin
can do any action on articles
Logged in user
can read and create articles
can edit and destroy his own articles
Guest user
can read articles
But I'm having trouble understanding the syntax of CanCan. I understand it would be something like this.
def initialize(user)
user ||= User.new
if user.admin?
can :manage, Article
else
can :read, Article
end
end
But this is just for the admin and guest user, I'm not sure how to differentiate a guest user from a logged in user because it creates a new User object when user is empty. I've seen that the code should be something like this can [:edit, :destroy], Article, :user_id => user.id, but I'm not sure how this would fit in the initialize method.
And one last question, if I only define a can :read, Article on guests, would it block the other actions such as create and update, like white listing the read action?
Any help would be appreciated. Thanks a lot!
Here's what I did:
In ability.rb
def initialize(user)
if user.nil?
can :read, Article
elsif user.admin?
can :manage, Article
else
can [:read, :create], Article
can [:update, :destroy], Article, :user_id => user.id
end
end
And for displaying the links, I've used this:
- if can? :read, Article
= link_to 'Show', article
- if can? :create, Article
= link_to 'New Article', new_article_path
- if can? :update, article
= link_to 'Edit', edit_article_path(article)
- if can? :destroy, article
= link_to 'Destroy', article, method: :delete, data: { confirm: 'Are you sure?' }
And it seems to be working now, not sure if that's the best way though.
You can pass hash of conditions:
can :manage, Article, :user_id => user.id
Look at https://github.com/ryanb/cancan/wiki/defining-abilities for details.