Field level permissions using CanCanCan or Pundit - ruby-on-rails-4

I am currently using Rails 4.1.14 with CanCanCan 1.13.1 and defined granular permissions on model/record level. Admins can manage all articles but users can edit only articles they authored.
To prevent regular users for editing specific fields I make fields visible in rails_admin depending on role.
visible do
bindings[:object].id == bindings[:view].current_user.roles.include? :admin
end
I am also using https://github.com/aasm/aasm gem and created custom actions so user can move records into new states.
But what I really want is to enable field level permissions depending on user's role / record. I can't find any docs on CanCanCan or https://github.com/elabs/pundit pages.
Does anyone have experience with that?

You mean that an admin should be allowed to edit all fields of a record, but an editor is only allowed to change the fields x and y?
Yes, this is possible in pundit, since it integrates with strong parameters (which you should be using anyway). There's also an example in the pundit readme (see: Strong parameters). I simplified example from the readme:
# post_policy.rb
def permitted_attributes
if user.admin?
[:title, :body, :tag_list]
else
[:tag_list]
end
# posts_controller.rb
#post.update_attributes(permitted_attributes(#post))
the permitted_attributes helper in the controller is provided by pundit and automagically calls the permitted_attributes method of the infered policy.

Related

How do I apply higher permissions to child pages in Wagtail?

I am building an intranet site for my organization with Wagtail and we are in the process of adding a knowledge base. The entire site needs to be restricted to logged-in users, but certain pages need to only be accessible to users in certain groups. For instance, only members of the IT group should be able to access the pages underneath the IT Knowledge Base page.
Currently if I set the top-level page to be accessible only by logged-in users, that permission is applied to every page on the site, and I am barred from setting more specific permissions on any child page. It is imperative that I be able to set more specific permissions on child pages.
I was able to find Wagtail Bug #4277 which seems to indicate that the logic for more specific permissions is implemented but not exposed in the admin UI.
I am not familiar with the inner workings of Wagtail yet, especially how Wagtail permissions intersect with Django permissions. How can I add more specific permissions to child pages?
You can restrict or allow users to view a site. You can also restrict or allow users to do some actions (maybe modifying an article).
To pass these restrictions or allowances django uses groups and permissions. Basically it all is based on permissions but sometimes you want to pass the permission to an entire group rather than passing permissions to users explicitly.
Therefore you could create your it_group. Then you would add the permission, let's call it it_permission to that group. When you then add a user to that group, that user then has all the group permissions. As said you don't need to organize these steps with groups. You could also add a permission, let's call it admin_status to a user directly.
When you build your views there are multiple operators that check for permissions of currently logged in user.
You could decorate your view with the permission-required-operator.
See the example:
from django.contrib.auth.decorators import permission_required
#permission_required('your_user_app.it_permission')
def my_view(request):
# only users with permissions can view this view.
Django and Wagtail are both awful at object authorisation.
For your case, it depends how tight you want to make the security, and how complex the authorization model is.
You can set permissions on a per page basis via the group edit page in the admin menu, any page below will inherit those permissions. The problem with this is that the least restrictive permissions apply and there is no deny option. If they have edit permission on a parent page, they'll have edit permission on the child page.
If you just want a superficial prevention to stop unauthorised people editing all knowledge base pages, you might look at using hooks to assess the permissions of the logged in user.
You can use before_edit_page to check before the page form is rendered for editing and redirect to a notification page if they fail, or use after_page_edit to prevent saving (not very friendly after the editor has spent some time on the page).
For before edit, for a simple one-off case and where there is a class for the KB page, where you want to allow only members of the IT Department and Site Managers groups to have access, it could be something like:
# wagtail_hooks.py
from django.contrib import messages
from django.http import HttpResponseRedirect
from wagtail import hooks
from kb.models import KnowledgeBasePage
#hooks.register("before_create_page")
#hooks.register("before_delete_page")
#hooks.register("before_edit_page")
def check_kb_permissions(request, page, page_class=None):
if (page_class or page.specific_class) == KnowledgeBasePage:
if not request.user.groups.get_queryset().filter(name__in=['Site Managers','IT Department']).exists():
messages.error(
request,
'You do not have permission to add, edit or delete knowledge base articles.\
<br><span style="padding-left:2.3em;">Contact support \
to report this issue</span>'
)
return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/admin/'))
If the user fails, they stay on the same page with an error message in red banner at the top of the page.
You can build this out to a more complex authorisation model, matching user groups with models and permissions if need be, rather than the hard coded example above.
This doesn't affect CRUD permissions for programmatic operations, but if your concern is just the editor interface then this works.

permissions in django for the group of users

In my wen application i mad different login for different person like for HR ,for engineer ,for owner using django user creation form , and authenticate them using Django authentication ,now everything is work very well i am able to add user from the front add
but the problem is that everyone get same power and authority , i can change it from the admin penal but it will not be beneficial for my client/user I WANT ALLOW DIFFERENT KIND OF PERMISSIONS TO DIFFERENT GROUP OF USER(daynamicaliy with using django penal) show How can i do that.
(i'm beginner so its request to all of you to give answer in some detail format)
THANK YOU
If you refer to the docs here, there is already a default system where you can add or remove permissions to a specific group of users. What you should do next is to create different subclasses for different types of users.
So your models will look something like this eventually:
class User(models.Model):
# just keep whatever you had here
# Engineer and Manager will both be subclasses of User
class Engineer(User):
# add whatever permissions you have
class Manager(User):
# add different permissions
In this way, you will be able to apply custom permissions to different groups of people.

How to activate users manually

Django 1.11.2
django-registration-redux==1.6
I'm building an intranet website. And I'd like to control myself whether users are active or not. In Django admin there is such a possibility. But "Active" attribute is set to True automatically when a newly registered user confirms his/her email.
In other words what I'd like to do:
1) Let users register and reset passwords.
2) Admin of the site assigns the new user to a group. Users with the minimum permissions can only view. Special permissions allow edit, delete etc. But the user must be unable even to view anything without approval by the admin.
Now I'm planning to organize can_view permission for every model. The two above conditions will be performed by assigning the user to a group.
Well, this seems to be rather cumbersome. That "Active" attribute in admin is much more elegant. But "Active" is automatically set to True when the user confirms his/her email.
Could you give me a piece of advice here?
While it's tempting, never use is_active to deny permissions. The flag is meant to be equivalent to "deleting a user". That also means the user cannot login (with default authentication backend). So it's not an authorization guard, but an authentication guard.
That said, if you don't grant permissions, users don't have them. So if you implement can_view and set it to guard the relevant models and views, then the user can log in, but cannot see anything you don't want them to (it's convenient for a user to see that she successfully logged in though :) ).
Follow-up question from comments
It's fine to use one global permission that is checked per view. When using class based views, I recommend extending LoginRequiredMixin, tuck a few other goodies in a IntranetCommonMixin and have each view combine it with one of the generic base views. See also my answer here.
The only reason you don't want to do it, is that it's tough to code exceptions on the rule, because the first "object" that says "yes", wins.

How to restrict access to models in Ruby on Rails 4 using Pundit and Rolify

I have only just jumped on the Rails in the last few months and have run into my first real snag in my current project which I have been unable to find an answer for.
My aim is to implement some fine grained control over which user role(s)/group(s), generated from Rolify and controlled by Pundit, have access to a particular category, subcategory or article via the Upmin admin console. A category/article should be capable of allowing more than one user role/group access to its contents.
The answer here demonstrates scoping a role to a particular instance of a model, which is nice, but I would also like to have that control from the model instance via a simple checkbox form in my applications admin console (Upmin_admin for those interested).
Am I right in thinking that there isn't too much to produce this functionality, other than creating a category/article instance view partial in the admin console which lists all of the roles/groups and their current CRUD settings for that particular category/article instance. Or am I missing a few intermediary steps?
A nudge in the right direction would be greatly appreciated.
Thanks!
Some background of my app:
In my web application I have nested resources, categories, articles and comments like so:
resources :categories do
resources :articles do
resources :microposts, only: [:new, :create, :edit, :destroy
end
end
(I am aware it is not best practice to have nested resources deeper than one level, however I've only just jumped on the Rails train and :shallow = true wasn't delivering the results I was looking for.)
The categories act as nested sets courtesy of Awesome Nested Set and are capable of "holding" both categories and articles (and by extension comments).
Users are authenticated via an LDAP server using Devise - I will be configuring this in the near future to automatically allot a user to the correct group/role.
From re-reading the Rolify documentation I have come up with the beginnings of the solution to my problem.
Whenever a user creates a category, subcategory or article I'll run
user.add_role :current_role Model.Instance_id
which I can then query from the admin portal by getting the instances id. Then by querying all of the user roles within the system and comparing them against the instances associated roles, I can create the view partial for the admin console.
model_instance = Model.find(instance_id)
model_instance.roles #returns all of the roles associated with that instance
I would also need to create a few methods to handle the (mass)assignment/reassignment of roles so that when a checkbox is (de)selected that the expected result is achieved, such as adding a role/group to a instance and vice versa. Probably something along the lines of (ruby flavoured pseudocode to follow!!)
users = User.with_any_role(:some_role)
def assignRoleToModel(model_instance, users, role)
if model_instance.roles.empty?
users.each { |u| u.add_role creatorRole model_instance }
end
flash[:warning] = "#{Model_instance.name} already has that role assigned to it!"
end
where model_instance is the instance of the model I want to control group/role access to, users is a list of users who have the role I want to add to the model_instance and role is the role I wish to allow access to the model_instance.
Update
An example of how to control roles via a form https://github.com/RolifyCommunity/rolify/issues/246

Rolling out own permission app or modifying django permission

I am working on a project which needs a separate admin interface. The django admin interface is for the super super user, there will be companies who will sign up for our app and then they will have their own admin interface. Everything is set and done despite the permission. We want model level permission that's what Django provides.
What I did is:
class CompanyGroup(models.Model):
name = models.CharField(max_length=254)
permissions = models.ManyToManyField(Permissions)
Now this list all the permissions of the site itself. So, Should I start working on my own permission app or I can modify django Permissions to provide object level permissions for only some models.
Thanks
Try one of the several existing 'row level' / 'per object' permissions apps for Django:
http://django-guardian.readthedocs.org/en/v1.2/
http://django-object-permissions.readthedocs.org/en/latest/
...there are probably others, those are just the first two in Google
We are using django-guardian in current project at work, seems fine.
I am assuming that you need to control access to sub-sets of each model's objects, depending on which company the current user belongs to (i.e. you don't want people from Company A to see items from Company B). For this reason you need row level permissions.
You probably also need to limit the permissions of all 'company users' to only certain actions:
You do not need to create a CompanyGroup class.
Instead just enable the admin site, log in as super user and create a django.contrib.auth.models.Group which contains the global permissions applicable to company users.
then ensure when you create any new company user logins that they are added to that Group