I have rails4 app. It has (among others) Client and Developer models. I also have Submission model.
I use activeadmin with cancan gems.
I try to make Submission's comments to be visible for both Client and Developer (which are related to certain submission), but when I check, developer only sees his comments and client correspondingly his. Assume, it has something to do with Ability class definitions. Here is mine (partially):
def developer_rules(developer)
can [:read, :create], ActiveAdmin::Comment
end
def client_rules(client)
can [:read, :create], ActiveAdmin::Comment
can :manage, Client, id: client.id
end
Has anyone faced anything similar? Would be grateful for any hints. Thank you!
UPD: SOLVED
Eventually, issue has nothing to do with Ability class.
ActiveAdmin shows comments separately for every namespace (even though these comment are associated to same resource), therefore the solution is to override this method
def self.find_for_resource_in_namespace(resource, namespace)
where resource_type: resource_type(resource),
resource_id: resource_id_cast(resource),
namespace: namespace.to_s
end
deleting last line namespace: namespace.to_s so that comments are shown independent of namespace.
Related
I am trying to manage some permission in my RailsAdmin, but am having trouble getting it to do exactly what I want to.
Using the ability file I have some set up. I want to be able to allow a specific kind of user to create, read, trash, export a particular model.
I decided to change:
can :manage, Terms, company_id: company_id
to:
can [:create, :read, :trash, :export], Terms, company_id: company_id
thinking that it would still show the little "info" and "delete" icons removing the "edit" icon. Instead it just shows the "info" icon.
I want the pencil to be gone. Any help or guidance is appreciated. I looked at the cancan page and didn't get much help.
Original:
After:
Below:
can :manage, Terms, company_id: company_id
I added:
cannnot :update, Terms, company_id: company_id
which seemed to work properly
I'm putting together a side project for a teacher/student type of website where the student will share a dashboard with the teacher. The teacher and student can both upload files and leave comments in the dashboard.
The part that I'm stuck on is the permission. For student, I've set up the index controller to this method
def index
#Homework = Homework.where(:user_id = current_user)
end
With this, I'm able to have the student only see the work that they have, but I'm confused on how to get the teacher to see each individual student's work?
Suggestions? Thanks
Here's a simple solution if you only ever need to support a single class in your application:
def index
if current_user.teacher?
#homeworks = Homework.all
else
#homeworks = Homework.where(user_id: current_user)
end
end
Otherwise, your Homework schema does not seem to be correctly designed. See your query below:
Homework.where(:user_id = <student_id>)
This works to retrieve a student's homeworks, but it does not work to retrieve a teacher's students' homeworks. You may need a class_id for a teacher to see each individual student's work:
Homework.where(:class_id = <class_id>, :user_id = <student_id>)
A Class has_many Teachers and has_many Students. This design will allow you to support multiple classes.
Some more guiding questions:
Is teacher/student both kept in the same User model?
How do you differentiate between teacher/student in your current User model?
Is there a "user_type" column somewhere in User?
What happens if the current_user is of the "teacher" user_type?
For complex user permissions, use CanCanCan: https://github.com/CanCanCommunity/cancancan
Don't use uppercase instance variables. ex: #Homework should be #homework
Check out the gem CanCan. Install it (follow the instructions, you should have to put something in application controller), Then, put in your ability file:
class Ability
include CanCan::Ability
def initialize(user)
can :manage, Homework, user_id: user.id
end
end
Then at the top of your StudentController put
load_and_authorize_resource
And the index action should look like:
#homework = #student.homework
Now, you didn't post your whole controller so this is a much as I can help.
I believe you may have a bigger underlying issue. You have students and teachers has_many homework i read in your comment. Then in your example you use user_id. You are likely overriding your students and teacher ownership of homework. You would need a has_and_belongs_to_many relationship OR you would need a student_id and teacher_id columns on the homework table
Cancan automatically generate a number of instance variables which can make it feel like magic. Watch the free railscasts on cancan the same guy who made the video wrote the CanCan library.
I am writing a rails application for an organization. Every user may have 1 or more roles and can only access certain controller actions depending on those roles.
For example, only admins can create, destroy and update certain fields of Users. Also, there are Teams which each have a team leader, and only the team leader can update certain information about the Team (like the member list, for example). However, Admins are the one who assign the team leader in the first place.
The specific details of my scenario are not important, I merely hope I described the situation where there are many different roles and permissions.
My question is: what gem to use? My first thought was CanCan, but the last commit was almost a year ago and there is no mention of Rails 4 compatibility. Is there a currently maintained alternative?
Your first guess was right, use cancancan and you'll be good with it.
EDIT Jul 24, 2015
I've been using cancancan for a long time now and it was always working great. I've recently started working on a project where Pundit is used for authorization.
It is awesome. It prompts you to define the policy for each resource and it feels more natural than one bloated Ability class.
For bigger projects, I would definitely recommend Pundit.
To control access to actions I'd recommend Action Access, it boils down to this:
class UsersController < ApplicationController
let :admin, :all
let :user, [:index, :show]
# ...
end
This will automatically lock the controller, allowing admins to access every action, users only to show or index users and anyone else will be rejected and redirected with an alert.
If you need more control, you can use not_authorized! inside actions to check and reject access.
It's completely independent of the authentication system and it can work without User models or predefined roles. All you need is to set the clearance level for the current request:
class ApplicationController < ActionController::Base
def current_clearance_level
session[:role] || :guest
end
end
You can return whatever you app needs here, like current_user.role for example.
Although it isn't required, it bundles a set of handy model additions that allow to do things like:
<% if current_user.can? :edit, :team %>
<%= link_to 'Edit team', edit_team_path(#team) %>
<% end %>
Here :team refers to TeamsController, so the link will only be displayed if the current user is authorized to access the edit action in TeamsController. It also supports namespaces.
You can lock controllers by default, customize the redirection path and the alert message, etc.
It's very straightforward and easy, I hope you find it useful.
Something that was suggested to me that we are now using is the petergate gem. Easy to use and very clean looking with a great rails feel.
Works well with devise.
Here is some examples from the readme.
If you're using devise you're in luck, otherwise you'll have to add following methods to your project:
user_signed_in?
current_user
after_sign_in_path_for(current_user)
authenticate_user!
This comes in your User.rb. Adding more roles is as easy as adding them to the array.
petergate(roles: [:admin, :editor], multiple: false)
Instance Methods
user.role => :editor
user.roles => [:editor, :user]
user.roles=(v) #sets roles
user.available_roles => [:admin, :editor]
user.has_roles?(:admin, :editors) # returns true if user is any of roles passed in as params.
Controller access syntax.
access all: [:show, :index], user: {except: [:destroy]}, company_admin: :all
I'm using the gem called acts_as_commentable_with_threading
It automatically creates comments table, and in which there are these columns such as commentable_type, commentable_id, and etcs.
It is made to be polymorphic, and it's working fine.
I have these three models.
User
Community
Topic
They have comments under of each.
I'd like to enable User to delete his comment or somebody else's comment on his User page.
(He can delete all the comments on his User page)
I figured out to make it enable coding like this code below.
But I found out the same thing will happen to Community Page and Topic page.
Even if the user was not original creator of the comment, it will be possible for him to delete all the comments if the ID of the Community/Topic is the same as USER ID.
I only like it enable to USER model. How can I customize this?
models/ability.rb
can [:create, :destroy], Comment, {:user_id => user.id}
can [:destroy], Comment, {:commentable_id => user.id}
Since you're Comment class uses a polymorphic association, the delete action is currently allowed when the commentable_id matches the id of the user. Since you can have conflicting ids across the different tables, you need to match on the commentable_type as well. Try something like this:
can [:destroy], Comment, {:commentable_id => user.id, :commentable_type => user.class.name}
I am trying to solve an issue I'm having with setting up admin on my site. Here is a simple version of my routes.
resources :users
resources :articles
resources :collaborators
end
resources :admin
Admin users are very similar to Collaborators, because they can edit and create Articles, but Admin has the ability to also c.r.u.d. Users, Collaborators, and Articles for every User. Collaborators can only c.r.u.d. Articles that are associated with the User they have been assigned to.
The part where this gets a little bit confusing is how to set up the controller and views that Admin uses for CRUD operations. As of now the way I am trying to implement it is to create separate CRUD views within admin/. The problem with this is that the functionality is so simimlar to a collaborator, I feel like it could be DRYer somehow.
Anybody know the most basic way to implement something like this? Thanks!
Update: I'd like to update this to say that it's super easy to google admin tools for rails. I'm more wondering how to implement this without an admin tool, or if that sounds like a bad idea, why?
The answer I came up with is to create Collaborators with an admin field true / false. I also set up CanCan and it's working pretty nicely.
Here is my CanCan ability.rb
def initialize(user)
if user.admin?
can :manage, :all
else
can :manage, Article, :id => user.article_id
cannot :index, Article
end
end
I just used "user" because it's more readable and shorter than collaborator and basically a user...
Try ActiveAdmin (http://activeadmin.info/). I think it is the most simplest way to build administrator backend for your application.