dfunckt / django-rules Goto Github PK
View Code? Open in Web Editor NEWAwesome Django authorization, without the database
License: MIT License
Awesome Django authorization, without the database
License: MIT License
Hi, I've been a fan of this lib for a long time, so now I'm implementing it on a bigger scale than usual in a project and ran into an enhancement.
The context is that we want to connect some custom rules/permissions with roles, and build an UI around this. We like the format of appname:permission_name
to name a permission, but this is not very readable for the end-user assigning permissions to roles. Similar to Django's permission system, we'd like to be able to provide a human readable name to the permission.
API wise, I suggest extending the API from
rules.add_perm('appname:permission_name', some_predicate)
to
rules.add_perm('appname:permission_name', some_predicate, verbose_name=_("A translatable string") )
Looking at the source code, this would make the RuleSet
a bit more complex object than a simple dict, a single rule within a RuleSet would probably need to be an object itself where the verbose_name is also kept.
Thoughts? Comments? I'm willing to contribute on this with a PR!
Quoting the release docs:
Django 1.4 added the assignment_tag helper to ease the creation of template tags that store results in a template variable. The simple_tag() helper has gained this same ability, making the assignment_tag obsolete. Tags that use assignment_tag should be updated to use simple_tag.
As this prevents a timely upgrade to Django 2.0 for me, I'd ask you to consider merging a PR and releasing a new version soon-ish.
Not sure this is really an issue, but good for your documentation:
By default I have found rules don't work with CreateView because there is no object instance yet, so it throws a PK error. However, I have defined rules as such that will accept just the user object and will return the appropriate True/False. I really needed some security rules on the CreateView, so I started messing with the get_object function (unused for CreateView) and I made this work by adding the following function 'get_object' to the CreateView class:
class CustomCreateView(PermissionRequiredMixin, CreateView): """ other stuff """ def get_object(self): pass
Now I'm trying to get this working with ListView, which is proving to be a bit more difficult...
So I've read in the documentation that you can have rules autodiscover rule.py files in your applications. That is great, but the documentation doesn't clarify is both rules
and rules.apps.AutodiscoverRulesConfig
should be in INSTALLED_APPS
.
rules
, then rules.py
files are not discoveredrules.apps.AutodiscoverRulesConfig
then it says that it cannot import predicate
from rules
in from rules import predicate
.Is this intended behavior?
Hi @dfunckt, I've been building a test application w/ rules that I ultimately want to submit as a pull request. You can look at it in https://github.com/highpost/rules-testapp. It's a simple blog app with a number of users with different privilege levels.
I'm also writing an article that you can either use in your documentation or we can find another home. The first chunk (in explore.txt) is an overview of basic Django permissions. I'll get to rules after that.
My problem now is that my app works ... except with rules. If you look at views.py, you'll see that I'm using CBVs. If you remove PermissionRequiredMixin from each view, you can use the various test URLs in the README.txt file without any problem. But if you include them, then the buttons that access the views will fail with "127.0.0.1 redirected you too many times." I haven't had any luck tracking this down. I will note that the CreateView seems to work, and DetailView, UpdateView and DeleteView (with all take pk arguments) fail.
Thanks for your help.
I am having difficulty using template tags in my templates. For example this works
In the view:
from __future__ import absolute_import
import rules
...
def detail(request, slug):
obj = get_object_or_404(NewsStory, slug=slug)
return render(request, 'news/detail.html', {
'story': obj,
'can_publish_newsstory': rules.has_perm('can_publish_newsstory', request.user)
})
In the template
{% if can_publish_newsstory %}
<li><a href="{% url 'news:edit' story.slug %}" class="button expand secondary">Edit Story</a></li>
<li><a href="{% url 'news:delete' story.slug %}" class="button expand secondary">Delete Story</a></li>
{% endif %}
If I change this to remove 'can_publish_newsstory' from the view and include it in the template, nothing shows.
Revised Template
{% load rules %}
{% has_perm 'can_publish_newsstory' user as can_publish_newsstory %}
{% if can_publish_newsstory %}
<li><a href="{% url 'news:edit' story.slug %}" class="button expand secondary">Edit Story</a></li>
<li><a href="{% url 'news:delete' story.slug %}" class="button expand secondary">Delete Story</a></li>
{% endif %}
I am using Python 2.
I've posted a question a few days ago with all my code.
My user is redirected even if they have the proper permissions.
https://stackoverflow.com/questions/46672950/using-django-rules-with-cbvs-doesnt-seem-to-work
It's probable that something is wrong with my code, but maybe it's a bug instead?
Thanks for the great library! I love the philosophy to use rules written in simple python functions over database tables.
I ran into a few troubles trying to integrate it with a django rest framework project - we don't use django permissions, which means we can't use DjangoObjectPermissions easily.
I thought to connect django-rules directly to drf permissions - ends up similar-ish to dry-rest-permissions but using django-rules.
I also wanted to be able to return custom error messages explaining why a permission was denied.
I managed to implement this by subclassing stuff in django-rules so that you can use it like this:
# in rules.py
@predicate(messages=('Team is archived', 'Team is not archived'))
def is_team_archived(_, team):
return team.status = 'archived'
can_archive_team = ~is_team_archived
# standalone use
result, message = can_archive_team.test_with_message(user, team)
if result:
print('yes')
else:
print('no:', message) # prints "no: Team is archived"
# use in a @detail_route
# creates a drf compatible permission class
CanArchiveTeam = create_permission_class('CanArchiveTeam', can_archive_team)
@detail_route(
methods=['POST'],
permission_classes=(IsAuthenticated, CanArchiveTeam)
)
def archive(self, request, pk=None):
# do archiving
If the CanArchiveTeam
does not pass, then it returns a permission denied error with the message Team is archived
.
There is a bit more information in our project discussion.
I wanted to avoid magic naming conventions (the approach taken by dry-rest-permissions) or referring to permissions using string names - explicit imports are much clearer to me.
Actually, the only API change for rules is changing/adding a method that returns (result, message)
instead of result
, and accepting messages as predicate args.
My questions are:
The alternative is a fork, but I don't want to contribute to the ever fragmenting django permissions ecosystem. Thanks!
I have start implementing django-rules to my application and I would like to know how can I integrate the permission backend with CBVs in django.
My settings are:
AUTHENTICATION_BACKENDS = ( 'account.auth_backends.EmailAuthenticationBackend',
'rules.permissions.ObjectPermissionBackend',
'django.contrib.auth.backends.ModelBackend')
INSTALLED_APPS = (
....,
'rules.apps.AutodiscoverRulesConfig',
...,)
views.py
class BookEditView(SuccessMessageMixin, PermissionRequiredMixin, UpdateView):
model = Book
template_name = 'book.html'
form_class = BookFormSettings
success_message = "%(title)s was created successfully"
### PermissionRequiredMixin settings
permission_required = 'books.change_course'
rules.py
@predicate
def is_author(user, course):
return book.author == user
add_perm('books.change_book', is_author)
If this does not work, what are the best practices to integrate django-rules through views.
Thanks.
A decorator was introduced to resolve #12, but it isn't explicitly mentioned in the README.
I really like how rules
works for individual object permissions, but it doesn't really cover permission-based queryset filtering (which comes naturally for database-centric permission systems like django-guardian
). While thinking about how to compensate for that, I thought of an extension to the API that could help fill that gap. Rather than rushing off to write a pull request, I figured I'd outline it here for feedback first. I'm envisioning something like this:
from django.db.models import Q
@rules.filter
def is_book_author(user):
return Q(author=user)
is_book_author_or_superuser = is_book_author | rules.predicates.is_superuser
rules.add_filter('books.view_book', is_book_author_or_superuser)
Book.objects.filter(rules.q(user, 'books.view_book')
Filters would have to be defined separately from the object permission predicates, but would work very similarly; Q objects can be combined in ways that are pretty compatible with the predicate combinations already supported in rules
. Existing predicates which only depend on properties of the user could be combined with Q-based filters, with predicate outcomes being represented as always-True (like Q(pk__isnull=False)
) or always-False (like Q(pk__isnull=True)
) Q objects.
This would also make it pretty straightforward to create a Django REST Framework filter that would use the filter associated with the correct permission:
from rest_framework.compat import get_model_name
class DjangoPermissionRulesFilter(BaseFilterBackend):
perm_format = '%(app_label)s.view_%(model_name)s'
def filter_queryset(self, request, queryset, view):
user = request.user
model_cls = queryset.model
kwargs = {
'app_label': model_cls._meta.app_label,
'model_name': get_model_name(model_cls)
}
permission = self.perm_format % kwargs
return queryset.filter(rules.q(user, permission))
Some of the things I like about this design:
Some downsides that I don't see good ways to work around yet:
Q(author=user)
while another has Q(owner=user)
, Q(status__user=user)
, or even Q(creator__organization=user.organization)
.Book.objects.has_perm(user, 'books.view_book')
, but it doesn't seem worth the effort to create a model manager mixin for it that would need to be explicitly included in all relevant models.Thoughts? Do you think something like this would fit in rules
or should go into a separate app which depends on it? And can you think of any good improvements on the API?
Hi,
I am trying to integrate rules with DRF's viewsets but I keep getting an AttributeError: 'OrgViewSet' object has no attribute 'request'
Here is my viewset code:
class OrgViewSet(PermissionRequiredMixin,
mixins.ListModelMixin,
viewsets.GenericViewSet):
permission_required = CHANGE_ORG
queryset = Org.objects.all()
serializer_class = OrgSerializer
Here is the stack trace
Internal Server Error: /orgs/
Traceback (most recent call last):
File "/Users/dre/code/treebeard/venv/lib/python3.6/site-packages/django/core/handlers/exception.py", line 41, in inner
response = get_response(request)
File "/Users/dre/code/treebeard/venv/lib/python3.6/site-packages/django/core/handlers/base.py", line 187, in _get_response
response = self.process_exception_by_middleware(e, request)
File "/Users/dre/code/treebeard/venv/lib/python3.6/site-packages/django/core/handlers/base.py", line 185, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/Users/dre/code/treebeard/venv/lib/python3.6/site-packages/django/views/decorators/csrf.py", line 58, in wrapped_view
return view_func(*args, **kwargs)
File "/Users/dre/code/treebeard/venv/lib/python3.6/site-packages/rest_framework/viewsets.py", line 86, in view
return self.dispatch(request, *args, **kwargs)
File "/Users/dre/code/treebeard/venv/lib/python3.6/site-packages/django/contrib/auth/mixins.py", line 90, in dispatch
if not self.has_permission():
File "/Users/dre/code/treebeard/venv/lib/python3.6/site-packages/rules/contrib/views.py", line 51, in has_permission
return self.request.user.has_perms(perms, obj)
AttributeError: 'OrgViewSet' object has no attribute 'request'
Any help would be greatly appreciated.
Hi,
You've removed using the group_names_cache here because of #43.
As far as I can tell, that cache was only valid for the lifetime of one request and saves many duplicate DB queries. It wasn't 'undesired', in fact it was very desirable, imo. I have multiple {% has_perm %}
on most pages, all based on group membership. Another example: I have a list of objects which many groups can view, but one part of determining whether an 'edit' link should be displayed for an object from that list is based on group membership.
What used to be 9 queries for this page is now over 60, all of them are fetching the group names for the same user again and again.
I'm not exactly sure what ljsjl's use case was, but I think that changing group membership and then checking it again for that same user within one single request is not the typical use case. Even the Django docs regarding Permission Caching say that it's fine to cache them by default.
If you decide against using the cache by default again, what would be the best way forward for people who want to use it? Right now I've basically copied the predefined is_group_member
predicate and modified it. Is that the best solution? Or would you be willing to add another predicate to your package, something like cached_is_group_member
? Or change the signature to
def is_group_member(*groups, cache=False):
...
@predicate(name)
def fn(user):
if cache:
# Use cache.
else:
# Refetch them every time.
and use it like this is_admin = is_group_member('admin', cache=True)
? Not a big fan of the last one, but it would work.
I'm not able to get django-rules working. I know that there is other issues that are similar #10 , #8 , and #16 ; but I don't have django-authority installed and I'm using python 3.4 and Django 1.7.6
This is the error I'm receiving:
ImportError at /
No module named 'rules.apps.AutodiscoverRulesConfig'; 'rules.apps' is not a package
Request Method: GET
Request URL: http://localhost:8080/
Django Version: 1.7.6
Exception Type: ImportError
Exception Value:
No module named 'rules.apps.AutodiscoverRulesConfig'; 'rules.apps' is not a package
Exception Location: /usr/lib/python3.4/importlib/__init__.py in import_module, line 109
Python Executable: /usr/bin/python3.4
Python Version: 3.4.1
Thanks for the help.
I've gotten the Django permission tests to work as per the docs, but when attempting admin integration, I'm unable to get the model instance to be accepted by the predicate. The user is passed appropriately, but the model instance (book) in the example) is always None. I'm following the tutorials exactly, and am on Django 1.11. What could cause this?
@rules.predicate
def in_section(user, person_instance):
#This always produces an exception, because person_instance is always None.
return user.person.section == person_instance.section
rules.add_perm('myapp', rules.always_allow)
rules.add_perm('myapp.add_person', in_section)
Documentation of the decorator for permissions on views is good, but focuses on views with passed-in objects and a user's permissions to access them. The simpler case, where you have set up group membership rules with django-rules and want to protect an entire view based on group membership, is not obvious.
I finally figured out that django-rules can be used in conjunction with Django's user_passes_test
. I recommend adding to that section of the documentation something like this:
If you want to protect an entire view with a rules decorator, irrespective of any particular object, django-rules can be used in conjunction with Django's user_passes_test
decorator. Rather than using the permission_required
, use something like:
from django.contrib.auth.decorators import user_passes_test
import rules
is_participant = rules.is_group_member('Participants')
@user_passes_test(is_participant)
def participant_sample(request):
....
I thought I understood autodiscovery (see #35), but I don't. So I've built a very simple Django project with a set of permissions stored in perms.py and loaded and used in views.py. The relevant line is probably
module = import_module('myapp.perms')
in myapp/views.py. This project app completely works: I can run it with app-setup.sh and (importantly) I can run the related tests with pytest. But if I try to move this app to another environment, say something built with Cookiecutter Django, then I have problems. Modifying import_module() to give it a relative path (.perms) also fails. In addition, note that my permissions file is named perms.py. If I rename it to rules.py and change the above line, it fails.
So can you show me an explicit way of loading a permissions file?
Thanks.
I'm new to django-rules. I like its simplicity and it's always helpful when there's more documentation than code. But I'm wondering how I should handle an app where I spin up a bunch of objects, each with its own set of permissions (and these are added to by users as time goes by) and, well, my server goes down. How do I rebuild that permission structure?
I (really) like that django-rules doesn't require a database hit each time I make a permission check, but its also important to record those permissions.
Thanks.
Very noob in Python/Django:
Reading the docs, I get the basic idea of applying django-rules in a django template.
However, I feel completely overwhelmed when it comes to applying it in django-admin.
Could you please provide a hint?
try:
# Requires SingleObjectMixin or equivalent ``get_object`` method
return self.get_object()
except AttributeError: # pragma: no cover
return None
If the get_object()
implementation has a bug that ends up causing AttributeError, the mixin gobbles it, which can lead to a very confusing result and debugging experience :)
Instead of looking at side effects of calling a missing method, what about using hasattr
?
pip install git+https://github.com/dfunckt/django-rules.git@master
won't install the compat
package.
As a result, doing something like from rules.contrib.views import LoginRequiredMixin
will fail.
I'm implementing a custom rule system where a user can have different answers to a permission depending on the obj
that is supplied to the predicates. In order to check these permissions fully, I need access to the name of the rule being checked in my predicates.
Is this possible without making obj
in my predicates a tuple containing the name as well as the object I want to check on?
My problem is accurately described by the comment I found here https://groups.google.com/forum/#!topic/django-rest-framework/M5q6pI8vcZQ:
I have been using DjangoObjectPermissions which inherits from DjangoModelPermissions. DjangoModelPermissions has a method has_permission which checks for Model level permissions. However, if you are working with object level permissions, usually the model level permissions evaluate to false. Consequently DjangoObjectPermissions raises a permission denied.
I have easily fixed that issue be overwriting the has_permission method of DjangoObjectPermissions, but again I am wondering, have I missed anything or should that not be the default behavior of DjangoObjectPermissions.
I have a code like this:
# rules.py
import rules
############################
# Predicates
############################
@rules.predicate
def is_tweet_owner(user, tweet):
if not tweet:
return False
return tweet.owner == user
############################
# Permissions
############################
rules.add_perm('tweets.add_tweet', rules.is_authenticated)
rules.add_perm('tweets.change_tweet', is_tweet_owner)
rules.add_perm('tweets.delete_tweet', is_tweet_owner)
# views.py
from rest_framework import viewsets, mixins
from .models import Tweet
from .permissions import TweetPermission
from .serializers import TweetSerializer
class TweetViewSet(mixins.CreateModelMixin,
mixins.DestroyModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
viewsets.GenericViewSet):
queryset = Tweet.objects.all()
serializer_class = TweetSerializer
permission_classes = (TweetPermission, )
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
# permissions.py
from rest_framework import permissions
class TweetPermission(permissions.DjangoObjectPermissions):
authenticated_users_only = False
def has_permission(self, request, view):
"""
WTF?!
"""
return True
I want the tweet can be updated or deleted only by the user that owns this tweet. But user.has_perms method will always return false, because django calls has_permission method before has_object_permission.
So, django will call this:
>>> user.has_perms(['tweets.change_tweet'])
False
But I want this:
>>> user.has_perms(['tweets.change_tweet'], tweet)
True
I need to create a lot of rulesets in a lot of models, and it appears that I will need to add an is_superuser predicate to every single one, which is not very DRY. Is there a way (or a best practice) to grant full access to superusers on everything touched by rules?
Visiting https://github.com/dfunckt/django-rules/releases it seems like v1.1.1
is the latest release, but there is also a v1.2.0
, which also seems available on PyPI. What is the latest official (stable?) release, and what changed in v1.2.0
?
Replaced 'rules',
with 'rules.apps.AutodiscoverRulesConfig',
on INSTALLED_APPS
settings to enable auto discover mode of rules.py
modules as docs said and got this error:
ImportError: No module named 'rules.apps.AutodiscoverRulesConfig'; 'rules.apps' is not a package
P.S: I'm using Python 3.5.2 inside venv
Hi,
I'm raising a question here before submitting any pull request.
I think there is a design issue with Predicate.test(), because if I defined my predicate to accept only
two positional arguments and the caller gives only one argument, my predicate will be called with None for the second argument.
@rules.predicate
def are_equal(a, b):
return a == b
rules.add_rule('test_this', are_equal)
rules.test_rule('test_this', 'a', 'a') # OK there is two positional argument
rules.test_rule('test_this', 'a') # why b is None in this case ??
This behaviour troubles me, I do not know yet how it should behave, but I would like, first, to hear from you.
Do you think there is room for improvement ?
When using the permission_required decorator as follows:
@permission_required('perm_name', fn=objectgetter(model_name, view_arg)),
it is not possible to leverage default argument values as specified in the view declaration.
If for example, I declare my view as follows:
def edit_circle(request, cid=0):
...
When the view is called without the 'cid' argument, the following exception is raised:
ImproperlyConfigured: Argument cid is not available. Given arguments: []
Shouldn't it be something else then rules
, to avoid importing itself instead of rules
app:
#myapp/rules.py
import rules
(sorry for my english)
Information:
Django 1.9
Settings:
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
'rules.permissions.ObjectPermissionBackend',
)
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rules.apps.AutodiscoverRulesConfig',
'debug_toolbar',
Autre
]
rules.py files with the predicate and permission(add_perm)
views.py with import rules.py
when I use permission_required, it always return True, when I test the function(predicate) in the views for see the result with print, it's good but permission_required don't work.
ex:
If I have rules.py:
@rules.predicate
def is_author(user, book):
return book.author == user
rules.add_perm('edit_book', is_author)
if I used @permission_required('edit_book', fn=objectgetter(Book, 'id') it doesn't work but if I use in the views: is_author(object_user, object_book) it's work.
Using function annotations in rules will cause this error (Python 3.5):
ValueError: Function has keyword-only arguments or annotations, use getfullargspec() API which can support them
File "…/app/rules.py", line 3, in <module>
from .predicates import (
File "…/app/predicates.py", line 13, in <module>
def is_superuser(user: User):
File "…/django-rules/rules/predicates.py", line 246, in predicate
return inner(fn)
File "…/django-rules/rules/predicates.py", line 241, in inner
p = Predicate(fn, name, **options)
File "…/django-rules/rules/predicates.py", line 67, in __init__
argspec = inspect.getargspec(fn)
File "/usr/lib64/python3.5/inspect.py", line 1045, in getargspec
raise ValueError("Function has keyword-only arguments or annotations"
Ref: https://docs.python.org/3/library/inspect.html#inspect.getfullargspec
I am trying to use Django's Client.force_login
during tests, which appears to trigger an AttributeError
when using ObjectPermissionBackend
in settings.AUTHENTICATION_BACKENDS
:
[25] …/app/tests/test_middleware.py(23)test_ddt_middleware_normal()
-> response = client.get('/api/', HTTP_ACCEPT='text/html')
[26] …/Vcs/django/django/test/client.py(531)get()
-> **extra)
[27] …/Vcs/django/django/test/client.py(333)get()
-> return self.generic('GET', path, secure=secure, **r)
[28] …/Vcs/django/django/test/client.py(409)generic()
-> return self.request(**r)
[29] …/Vcs/django/django/test/client.py(478)request()
-> response = self.handler(environ)
[30] …/Vcs/django/django/utils/six.py(686)reraise()
-> raise value
[31] …/Vcs/django/django/core/handlers/exception.py(39)inner()
-> response = get_response(request)
[32] …/app/middleware.py(37)__call__()
-> if not (request.user and
[33] …/Vcs/django/django/utils/functional.py(234)inner()
-> self._setup()
[34] …/Vcs/django/django/utils/functional.py(380)_setup()
-> self._wrapped = self._setupfunc()
[35] …/Vcs/django/django/contrib/auth/middleware.py(24)<lambda>()
-> request.user = SimpleLazyObject(lambda: get_user(request))
[36] …/Vcs/django/django/contrib/auth/middleware.py(12)get_user()
-> request._cached_user = auth.get_user(request)
[37] > …/Vcs/django/django/contrib/auth/__init__.py(187)get_user()
-> user = backend.get_user(user_id)
The pytest test looks like this:
def test_foobar(db, client, some_user, some_group):
some_user.groups.add(some_group)
some_user.save()
client.force_login(some_user)
Setting a password and using login
works:
def test_foobar(db, client, some_user, some_group):
some_user.groups.add(some_group)
some_user.set_password('password')
some_user.save()
assert client.login(username=some_user.username,
password='password')
According to the documentation the get_user
method is required:
https://docs.djangoproject.com/en/1.10/topics/auth/customizing/#writing-an-authentication-backend.
Hey. Rules is proving quite useful, so thanks!, but a head scratcher in a test suite for a django app I am building led me to this issue.
In your built in factory is_group_member() you have the following code just before returning:
if not hasattr(user, '_group_names_cache'): # pragma: no cover
user._group_names_cache = set(user.groups.values_list('name', flat=True))
I'm not sure why you are caching the user's groups other than to save possible DB look ups in the future? A side effect is that if a user is removed from a group while your _group_names_cache attribute is in existence rules based on group membership will miss that change, as you've replaced a callable with a cached attribute. This results in unexpected rule failure. I'd suggest removing the attribute creation but you may other reasons for its existence?
To confirm:
It seems like there is no way to pass a request parameter to the rules.contrib.views.objectgetter() function in order to retrieve the object to perform the permission check on.
In the documentation it's not specified how to limit a view to users that have object based permissions. Just like normally we can do:
@permission_required('app.action_object')
def my_view(request, ...):
pass
I think that we should be able to somehow take incoming object IDs arguments and automatically block the request if the user does not meet the requirements.
For example, something along the lines of:
@permission_required('app.view_posts', 'post_id')
def view_post(request, post_id):
pass
What do you think?
Assume a rule such as:
my_rule = first_condition | second_condition
I would assume that second_condition
would not be evaluated if first_condition
evaluated to True
. It appears that django-rules
doesn't doesn't apply this optimization.
Is there a way to handle this with the library? If not, what would be involved in adding it? (This is particularly valuable for my use-case, as I am abusing the predicates to mutate data when the predicates are called.)
Thanks for a great library!
Thanks a lot for a great package, dfunkct!
The documentation is great, and I'm trying to get onboard. But even though a lot of things are pretty clear, certain things are still a bit confusing, and I think making them clearer in the documentation would really make this more approachable.
Namely;
rules.predicate
and rules.add_rule
, but at no point is there an import rules
statement. For the decorator, this is kinda understandable, as it's common to import decorators from modules. But for add_rule
, it wasn't clear what that variable was. Are these running on the module itself?rules.add_rule
, which one does it get added to? Is the Django one implicit?rule.py
module in the app, which makes sense. What is the content of this module then? Just import rules
, and then add the rules? And then do another import rules
in views.py
, and start using the decorators?In general, I imagine many people will use rules from a Django app. A minimal example would really help. Also, a lot of info about settings things up is spread around the file. Installing using pip is a no-brainer. But there should also be a Django configuration section for things like:
Sorry if this is long, but it's by no means a rant. On the contrary, I'm really excited about using this library, and I think it would really benefit from a few tweaks to the documentation!
If user already logged in, and have no specific permission for accessing View - redirect loop happen, here:
# Check for permissions and return a response
if not user.has_perms(perms, obj):
# User does not have a required permission
if raise_exception:
raise PermissionDenied()
else:
return _redirect_to_login(request, view_func.__name__,
login_url, redirect_field_name)
since raise_exception
is always set to False (default value) with CBV.
Currently the user is redirected to the login page regardless of whether she is logged or not.
I believe that instead of always redirecting to login, it would be helpful to let users specify a redirection view that is more useful for them.
Assuming I have a django project directory like so:
AppA:
-#views
-#models
-#rules.py
etc
AppB
-#settings
-#wsgi
etc
Rules
-compat
-contrib
-templatetags
-#apps
etc
In App A I am defining predicates in the rules.py file, which has the absolute import from future at the top. I want to use these predicates in the App A views file, so I go from 'rules.contrib.views import permission_required'. This however tries to import contrib.views from AppA/rules.py, rather than from the Rules package. What am I doing wrong?
The documentation isn't super clear about this I don't think! I have 'rules.apps.AutodiscoverRulesConfig' in my installed apps.
Hello,
I'm getting the following error (I didn't install the other package, django-rules, 🤐 ) on Django 1.9.
Unhandled exception in thread started by <function wrapper at 0x104001758>
Traceback (most recent call last):
File "/Users/dacian/.virtualenvs/venv/lib/python2.7/site-packages/django/utils/autoreload.py", line 226, in wrapper
fn(*args, **kwargs)
File "/Users/dacian/.virtualenvs/venv/lib/python2.7/site-packages/django/core/management/commands/runserver.py", line 109, in inner_run
autoreload.raise_last_exception()
File "/Users/dacian/.virtualenvs/venv/lib/python2.7/site-packages/django/utils/autoreload.py", line 249, in raise_last_exception
six.reraise(*_exception)
File "/Users/dacian/.virtualenvs/venv/lib/python2.7/site-packages/django/utils/autoreload.py", line 226, in wrapper
fn(*args, **kwargs)
File "/Users/dacian/.virtualenvs/venv/lib/python2.7/site-packages/django/__init__.py", line 18, in setup
apps.populate(settings.INSTALLED_APPS)
File "/Users/dacian/.virtualenvs/venv/lib/python2.7/site-packages/django/apps/registry.py", line 85, in populate
app_config = AppConfig.create(entry)
File "/Users/dacian/.virtualenvs/venv/lib/python2.7/site-packages/django/apps/config.py", line 116, in create
mod = import_module(mod_path)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/importlib/__init__.py", line 37, in import_module
__import__(name)
File "/Users/dacian/Desktop/dev/project_name/extras/dashboard/rules.py", line 6, in <module>
from rules import predicate
ImportError: cannot import name predicate
INSTALLED_APPS = [
'modeltranslation',
'django.contrib.sites',
'jet',
'django.contrib.admin',
'django.contrib.auth',
'polymorphic',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.humanize',
# 'rules',
'rules.apps.AutodiscoverRulesConfig',
'django_extensions',
'rest_framework',
]
extras/dashboard/rules.py
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import unicode_literals
from rules import predicate
@predicate(name='has_menu_perm')
def has_menu_perm(user, menu_item):
return any([user.has_perm(perm) for perm in menu_item.perms])
Hi @dfunckt,
I'm a happy user of the master branch for several month now.
I think django-rules
is stable and mature feature wised to be released and pushed to pypi.
Let me know if you need help.
Hello!
I am using django 1.7
and trying to enable autodiscovering of rules.py
files as it shown in the README file:
INSTALLED_APPS = (
# ...
'rules',
'rules.apps.AutodiscoverRulesConfig',
)
But I am getting the following error:
Traceback (most recent call last):
File "./manage.py", line 10, in <module>
execute_from_command_line(sys.argv)
File "/usr/lib/python3.4/site-packages/django/core/management/__init__.py", line 385, in execute_from_command_line
utility.execute()
File "/usr/lib/python3.4/site-packages/django/core/management/__init__.py", line 354, in execute
django.setup()
File "/usr/lib/python3.4/site-packages/django/__init__.py", line 21, in setup
apps.populate(settings.INSTALLED_APPS)
File "/usr/lib/python3.4/site-packages/django/apps/registry.py", line 89, in populate
"duplicates: %s" % app_config.label)
django.core.exceptions.ImproperlyConfigured: Application labels aren't unique, duplicates: rules
When I am disabling rules
application:
INSTALLED_APPS = (
# ...
#'rules',
'rules.apps.AutodiscoverRulesConfig',
)
I am getting another error (when I am trying to load any page):
ImportError at /
No module named 'rules.apps.AutodiscoverRulesConfig'; 'rules.apps' is not a package
Request Method: GET
Request URL: http://localhost:8000/
Django Version: 1.7
Exception Type: ImportError
Exception Value:
No module named 'rules.apps.AutodiscoverRulesConfig'; 'rules.apps' is not a package
Exception Location: /usr/lib/python3.4/site-packages/authority/__init__.py in autodiscover, line 21
Python Executable: /usr/bin/python
Python Version: 3.4.1
What is the correct way of using autodiscover?
In python 3.6 the getargspec is to be removed.
Django already provides some implementations to overcome this deprecation: see https://github.com/django/django/pull/4846/files (especially in here: https://github.com/django/django/pull/4846/files#diff-661c241427e347ab93c204317d4f68dc)
....
File ".../_venv35/lib/python3.5/site-packages/rules/rulesets.py", line 1, in <module>
from .predicates import predicate
File ".../_venv35/lib/python3.5/site-packages/rules/predicates.py", line 260, in <module>
always_true = predicate(lambda: True, name='always_true')
File ".../_venv35/lib/python3.5/site-packages/rules/predicates.py", line 253, in predicate
return inner(fn)
File ".../_venv35/lib/python3.5/site-packages/rules/predicates.py", line 248, in inner
p = Predicate(fn, name, **options)
File ".../_venv35/lib/python3.5/site-packages/rules/predicates.py", line 70, in __init__
argspec = inspect.getargspec(fn)
File "/usr/lib/python3.5/inspect.py", line 1040, in getargspec
stacklevel=2)
DeprecationWarning: inspect.getargspec() is deprecated, use inspect.signature() instead
Hey all, I'm thinking of releasing Rules 2.0 soon and was wondering if there are ideas for things that can be implemented now but couldn't due to having to keep backwards compatibility. I intend to include (breaking) changes related to #44 and #52. It would also be nice if we tackled #32 too (I remember there were some compatibility issues when I looked at it back when it was reported).
1.2.X will still be supported for some time, back-porting fixes for bugs that are severe enough to warrant it but no new features will be implemented.
What do you all think? I'll go ahead and release 2.0 in a couple of weeks if nothing comes up.
with django-rules imported from pip:
....
Django==1.8.5
django-rules==0.2
django-social-auth==0.7.28
django-wysiwyg-redactor==0.4.9.1
djangorestframework==3.4.7
httplib2==0.9.2
...
and rules included in my INSTALLED_APPS
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rules.apps.AutodiscoverRulesConfig',
'rest_framework.authtoken',
'rest_framework',
'accounts',
'bms',
'cms',
'socialmedia',
# 'v1_api',
)
Attempting to start the django shell, I get the following error
Traceback (most recent call last):
File "manage.py", line 10, in <module>
execute_from_command_line(sys.argv)
File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 351, in execute_from_command_line
utility.execute()
File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 325, in execute
django.setup()
File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/__init__.py", line 18, in setup
apps.populate(settings.INSTALLED_APPS)
File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/apps/registry.py", line 85, in populate
app_config = AppConfig.create(entry)
File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/apps/config.py", line 112, in create
mod = import_module(mod_path)
File "/usr/lib/python2.7/importlib/__init__.py", line 37, in import_module
__import__(name)
ImportError: No module named rules.apps
I can't think of what might be wrong..
There is no Django 1.11 in the test matrix. Is this project still alive and being kept up to date with new Django releases?
I couldn't find any solution online to this issue so I'm hoping you could help me out. So here's the scenario. I first created the permission below weeks ago. Back then, I wanted to display the list of companies only to admin users:
add_perm('mining.list_company', is_admin_level)
The above code worked perfectly. However, couple of days ago, we have made some changes and we want the list of companies to be available to all users. So I have this now:
add_perm('mining.list_company', is_authenticated)
However, when I go to the company list page using a regular user, I still get a permission denied error even though I have already changed the permission. I went into a little bit of digging and it looks like the add_perm
method gets executed only ONCE, that's why the is_authenticated
does not get recognized. I was able to confirm this because when I tried adding pdb debugging
inside the predicate
itself, it does not get executed and just proceeds to permission denied error.
Am I missing anything here? It seems I'm the only one having this issue. Below are the necessary codes:
predicates.py
from __future__ import unicode_literals, absolute_import
from rules import predicate
@predicate()
def is_authenticated(user):
return user.is_authenticated()
@predicate()
def is_admin_level(user):
return user.is_admin_level
rules.py
from __future__ import unicode_literals, absolute_import
from rules import add_perm
from .predicates import is_authenticated
add_perm('mining.list_company', is_authenticated)
views.py
class CompanyList(LoginRequiredMixin, PermissionMixin, ListView):
logger = logging.getLogger(__name__)
context_object_name = 'companies'
permission_required = 'mining.list_company'
template_name = 'mining/company/list.html'
paginate_by = 10
Again, everything is working perfectly fine before. The error appeared when I changed is_admin_level
to is_authenticated
. I would really appreciate it if you could point me to the right direction here. Thanks in advance!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.