Disclaimer: I'm new to django and django-rules.
I have defined my model. The Model has 2 foreign keys to the user table. Creator and supervisor.
Instances should be changeable/updated by staff, creator or supervisor.
I have defined predicates for is_creator and is_supervisor and made a rule:
#rules.predicate
def is_creator(user, mymodel):
return mymodel.creator == user
#rules.predicate
def is_supervisor(user, mymodel):
return mymodel.supervisor == user
can_edit = is_supervisor | is_creator | rules.is_staff
And in the models meta class I added:
rules_permissions = {
'change': can_edit
}
In my view I then want to show an edit button that links to edit form based on these permissions.
{% block content %}
{% has_perm 'mymodel.change_mymodel' user instance as can_edit %}
{% if can_edit %}
<button type="button" class="btn btn-warning"><h6>Edit</h6></button>
{% endif %}
{% endblock %}
When I log in as superuser, the button is displayed as expected.
When I user a test user that should be able to edit a specific instance, the button is not displayed at all. So there are certain checks made but not as expected.
I have a second similar functionality for the index page. Showing only actions the users has the privilege for. Again, the superuser sees it all but the test user does not.
In this case I have below predicate which is used as "add" permission" on a different Model:
#rules.predicate
def can_add_xyz(user):
return rules.is_staff | rules.is_group_member("Add_XYZ")
It seems in both cases all the checks besides the is_staff seem to fail. What am I doing wrong?
There are two issues going on. First RTFM and do it correctly:
In my template I used has_perm entity_name.add_entity_nameinstead of using as properly described in the documentation has_perm myapp.add_entity_name.
After fixing that the predicates now actually got called and I could debug them. And there a bigger problem revealed itself. I was able to fix it but don't like the fix.
The predicate:
#rules.predicate
def is_creator(user, mymodel):
return mymodel.creator == user
The issue is that in the template I'm checking this permission, mymodel is a related entity generated by a nested django-rest-framework serializer. This means that the instance used in the template that is then submitted to this predicate is not a mymodel instance but an OrderedDict and hence I get an Exception like OrderedDict has no attribute 'creator'.
An additional issue is that creator isn't directly a Django auth_user but an OneToOne extended User. So mymodel.creator == user would never be true.
#rules.predicate
def is_creator(user, mymodel):
#if called from template as related entity,
#mymodel is an OrderedDict from a serializer
if isinstance(mymodel, collections.OrderedDict):
return mymodel["creator"] == user.userprofile.pk
return mymodel.creator == user.userprofile
This fixes the issue and now the right things are displayed but I'm not entirely happy with this (type checking). So any advice to make this better is still welcome.
Related
This is my first time using flask-login, and I need to support anonymous users. Authenticated users will have special privileges. My User model implements its own version of the UserMixin functions; the relevant part is:
def is_authenticated(self):
"""Return True if the user is authenticated."""
return self.authenticated
def is_anonymous(self):
"""If you're not authenticated, you're anonymous."""
return not self.authenticated
Some templates will have code like the following:
{% if current_user is not defined or current_user.is_anonymous %}
...render HTML 1
{% else %}
...render HTML 2
{% endif %}
This works fine for anonymous users, but authenticated users will also see HTML 1 because current_user.is_anonymous evaluates to <bound method User.is_anonymous of <User *email-address*>. Meanwhile, if I change the condition to current_user.is_anonymous(), that will throw an error for anonymous users ("TypeError: 'bool' object is not callable"). My understanding is that the User Model needs is_anonymous to be a function, but its a Boolean attribute of current_user. This inconsistency is kind of irritating, but I feel like I'm missing something obvious.
What is the best way to correctly check to see if a user is anonymous?
EDIT: This is my user_loader:
#login_manager.user_loader
def load_user(user_id):
if user_id is not None:
return User.query.filter_by(email=user_id).first()
return None
EDIT 2: I removed my custom context processor bc I learned that LoginManager provides one for me. Still, the problem persists. In my templates, current_user.is_anonymous still evaluates to <bound function....
Victor is correct; I was missing the #property decorator. I knew it was something simple. Thanks Victor!
I'm in the learning stages of django. I just dived into a project to learn the framework and am having a series of questions throughout the process.
I basically want to have individual pages for users who create a task list and have them post tasks to their own page.
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class UserProfile(models.Model):
# This line is required. Links UserProfile to a User model instance.
user = models.OneToOneField(User)
# The additional attributes we wish to include.
website = models.URLField(blank = True)
# Override the __unicode__() method to return out something meaningful!
def __unicode__(self):
return self.user.username
class TaskItem(models.Model):
taskn = models.CharField(max_length = 400)
usern = models.ForeignKey(User)
In my template, if the user has their task entered, how do I call it to render onto the page?
My View:
def profile_page(request, username):
user = User.objects.get(username=username)
taskitems = user.taskn_set.all()
return render_to_response('profile.html', {}, context)
Current issue:
'User' object has no attribute 'taskn_set'
{{ request.user.taskitem_set.all }} would give you all the related task items. Now, to display it in your template:
{% for task_item in user.taskitem_set.all %}
{{ task_item.task_n }}
{% endfor %}
would display the list of tasks.
Here is the documentation on reverse-queries on foreign key (related_name) Also, read this
you would do something like this:
{% for task in user.taskitem_set.all %}
{{ task.task_n }}
{% endfor %}
This will fetch all TaskItem instances related to your user. (notice the extra database query)
While i don't know how your view works, i will assume that you are making the right checks to make sure that every user can only see his own tasks.
One performance trick you will find most useful is to use prefetch_related('taskitem_set'), this will prefetch the TaskItem instances as long as your UserProfile instance with one query:
user = User.objects.filter(id=user_id).prefetch_related('taskitem_set')
You can tune the code to match your preferences.
Hope this helps!
I have this models in Django:
News
Comments
Reactions
Relations are:
a News has various Comments
a Comment has various Reactions
The problem is the user (in request / session): the user may subscribe to a reaction, or a comment; he may be logged in or not. (it's a foo example, it doesn't have much sense)
I can't do in template:
{% for reaction in this_news.comments.reactions %}
{{ reaction.name }}
{% if reaction.user_subscribed %} #reaction.user_subscribed(request.user)...
You have subscribed this reaction!
{% endif %}
{% endfor %}
Problems are:
I can't call the method in the template with a parameter (see the comment above)
Models don't have access to request
Now i'm calling an init_user method in News Model, passing the request. Then i have the same method in Comment and Reaction model, and i have to set the user_subscribed property cycling the children of each model.
Isn't there a smarter way to do this?
EDIT: thanks to the Ignacio's hint about using custom tag i'm trying to do a generic mode to pass the user (avoiding the use of closures because i don't know how to use them atm):
def inject_user(parser, token):
try:
# split_contents() knows not to split quoted strings.
tag_name, method_injected, user = token.split_contents()
except ValueError:
raise template.TemplateSyntaxError("%r tag requires exactly three arguments" % token.contents.split()[0])
return InjectUserNode(method_injected, user)
class InjectUserNode(template.Node):
def __init__(self, method_injected, user):
self.method_injected = template.Variable(method_injected)
self.user = template.Variable(user)
def render(self, context):
try:
method_injected = self.method_injected.resolve(context)
user = self.user.resolve(context)
return method_injected(user)
except template.VariableDoesNotExist:
return ''
When i use it {% inject_user object.method_that_receives_a_user request.user %} i come to this error 'str' object is not callable in method_injected(user); how i can fix that?
Write custom template tags that take the user and set a context variable to indicate presence or absence of the criterion.
I've resolved it in a less elegant way, but it worked for me.
I've created a sort of singleton in my User defined class, with a property that i set in every view i need it.
The property is User.current.
Then, inside the models, where i need that i get the current user looking in User.current.
My Django app currently has URLs which are protected by 'permission_required()' functions.
This function is called in three different ways.
As a decorator in views.py, with hardcoded parameters.
As a plain function, with autogenerated parameter, in custom Class Based Generic Views.
As a function invoking views in urls.py, with hardcoded parameters.
I'm now adding a menu system to the app, and I need to make menu entries reflect whether the user has permission to request the URL of each menu entry. (Either by greying-out or hiding said entries.)
Is there a way of query the permissions required to a URL without requesting the URL?
The only solution I've thought of so far is to replace the decorator with a parameterless 'menu_permssion_required()' decorator and hardcode all of the permissions into a Python structure. This seems like a step backwards, as my custom Class Based Generic Views already autogenerate their required permissions.
Any suggestions on how to make a menu system which reflects URL permissions for the current user?
Here is an example of how to solve your problem:
First, Create a decorator wrapper to use instead of permission_required:
from django.contrib.auth.decorators import login_required, permission_required, user_passes_test
from django.core.exceptions import PermissionDenied
from functools import wraps
from django.utils.decorators import available_attrs
def require_perms(*perms):
def decorator(view_func):
view_func.permissions = perms
#wraps(view_func, assigned=available_attrs(view_func))
def _wrapped_view(request, *args, **kwargs):
for perm in perms:
return view_func(request, *args, **kwargs)
raise PermissionDenied()
return _wrapped_view
return decorator
Then, use it to decorate your views:
#require_perms('my_perm',)
def home(request):
.....
Then, add a tag to use for your menu items:
from django.core.urlresolvers import resolve
def check_menu_permissions(menu_path, user):
view = resolve(menu_path)
if hasattr(view.func, "permissions"):
permissions = view.func.permissions
for perm in permissions:
if user.has_perm(perm):
return True # Yep, the user can access this url
else:
return False # Nope, the user cannot access this url
return True # or False - depending on what is the default behavior
And finally, in your templates, when building the menu tree:
<button href="{{ some_path }} {% if not check_menu_permissions some_path request.user %}disabled="disabled"{% endif %} />
N.B. I've not tested the last part with the tag, but I hope you got the idea. The magic thing here is to add the permissions to the view_func in the decorator, and then you can access this using resolve(path). I'm not sure how this will behave in terms of performance, but after all that's just an idea.
EDIT: Just fixed a bug in the example..
Is there a way of query the permissions required to a URL without requesting the URL?
User.has_perm() and User.has_module_perms()
Any suggestions on how to make a menu system which reflects URL permissions for the current user?
I really like this question, because it concerns anyone that makes a website with django, so I find it really relevant. I've been through that myself and even coded a menu "system" in my first django project back in 2008. But since then I tried Pinax, and one of the (so many) things I learnt from their example projects is that it is completely unnecessary bloat.
So, I have no suggestion which I would support on how to make a menu "system" which respects the request user permissions.
I do have a suggestion on how to make a simple menu which respects the request user permissions, so that might not be completely unrelated.
Just make your menu in plain HTML, it's not like it's going to change so often that it has to be generated. That will also keep your Python code simpler.
Add to settings.TEMPLATE_CONTEXT_PROCESSORS: 'django.core.context_processors.PermWrapper'
Use the {{ perms }} proxy to User.has_perms.
Example:
{% if perms.auth %}
<li class="divider"></li>
{% if perms.auth.change_user %}
<li>
{% trans 'Users' %}
</li>
{% endif %}
{% if perms.auth.change_group %}
<li>
{% trans 'User groups' %}
</li>
{% endif %}
{% endif %}
{# etc, etc #}
That's how I keep navigation simple, stupid, and out of the way. But also I always include an autocomplete not far from the menus to allows the user to navigate to any detail page easily. So, that's all I know about navigation in django projects, I'm eager to read other answers !
I had a similar issue, but it went a little deeper. Instead of just permissions, I also wanted other tests based on the lidded in user (ie, is_staff, or user.units.count() > 1). Duplicating these in the view and the template seems prone to errors.
You can introspect a view object, and see all of the decorators wrapping it, and work out if they are checks (in my case: the first argument I'd u or user). If they all pass, then allow rendering the link.
Get all decorators wrapping a function describes the technique in a little more detail. You can find the app that wraps this up into a handy replacement for {% url %} at Django-menus.
I'm trying, in vain, to create a simple Django template tag to either show or hide a "delete" link next to a submitted comment on my site.
In a nutshell, I want to pass the comment object to the template tag, determine if the currently logged in user is authorized to delete the comment and then either show or not show the link.
The usage in my template would be like so:
{% load access_tags %}
{% if_authorized comment %}
Delete
{% endif_authorized %}
Rest assured that I also check in the appropriate view if the user is authorized to delete the comment.
Does this type of tag have a specific name? It would certainly help me with my Google searches if it did. Thanks for your help!
UPDATE 1:
The way my site works, two people are potentially authorized to delete a comment: 1) the comment creator and 2) the owner of the post where the comment was left. Because of this, I need to determine, per comment, if one of those conditions is present.
I don't think I can use something like Django's built-in permission sytem, since it requires that permissions "be set globally per type of object, no per specific object instance".
In my case, user "Bob" may have permissions to delete a comment (if he wrote it or it is on a post he created), but he also may not be allowed to delete it (if he is looking at a comment on someone else's post).
UPDATE 2:
It appears that you can't pass objects to a template tag, only strings: "Although you can pass any number of arguments to a template tag using token.split_contents(), the arguments are all unpacked as string literals." I guess I'll pass the id of the comment object in question and pull it in the tag.
I was wrong about this, just have to access the passed in object like:
self.comment.resolve(context).user
vs.
self.comment.user
OK, this is how I did it...
The tag is used like this in the template:
{% load access_tags %}
{% if_authorized comment.user object.user user %}
Delete
{% endif_authorized %}
The template tag file is called "access_tag.py" and is in my app's "templatetags" directory. This is the contents of "access_tag.py":
from django.template import Node, NodeList, TemplateSyntaxError
from django.template import Library, Variable, VariableDoesNotExist
register = Library()
def do_if_authorized(parser, token):
"""
Outputs the contents of the block if the 'comment owner' or the
'page owner' is also the 'authenticated user'. As well, you can use
an {% else %} tag to show text if the match fails.
Takes three parameters:
1) the comment owner
2) the page owner
3) the current authenticated user
"""
bits = token.contents.split()
if len(bits) != 4:
raise TemplateSyntaxError("%s tag takes three arguments: \
1) the comment owner \
2) the page owner \
3) the current authenticated user" % bits[0])
nodelist_true = parser.parse(('else', 'endif_authorized'))
token = parser.next_token()
if token.contents == 'else':
nodelist_false = parser.parse(('endif_authorized',))
parser.delete_first_token()
else:
nodelist_false = NodeList()
return IfAuthorizedNode(bits[1], bits[2], bits[3], nodelist_true, nodelist_false)
class IfAuthorizedNode(Node):
def __init__(self, comment_owner, page_owner, authenticated_user, nodelist_true, nodelist_false):
self.nodelist_true = nodelist_true
self.nodelist_false = nodelist_false
self.comment_owner = Variable(comment_owner)
self.page_owner = Variable(page_owner)
self.authenticated_user = Variable(authenticated_user)
def render(self, context):
try:
comment_owner = self.comment_owner.resolve(context)
page_owner = self.page_owner.resolve(context)
authenticated_user = self.authenticated_user.resolve(context)
except VariableDoesNotExist:
return ''
if comment_owner == authenticated_user or page_owner == authenticated_user:
return self.nodelist_true.render(context)
else:
return self.nodelist_false.render(context)
register.tag('if_authorized', do_if_authorized)
Done. In the end, it would have been pretty easy to just use the built-in {% if %} tag to do this comparison, but since I'll have other per-object authorizations to do, I will continue to build out these custom "access_tags". Plus, the template code looks so much tidier :)
There already exists a project that aims to do what you would like to do.
django-authority allows for fine grain control over permissions in templates.
Django 1.2 also contains user permissions in templates, too.
how about this... create a custom tag that writes a variable in the context, then test that variable using {% if %}
it'd be something like this:
{% check_access comment %}
{% if has_access %}
Delete
{% endif %}
of course the "check_access" tag would write the "has_access" in the context.
Good Luck