I am building a web app using Django 3.0.7 and Python 3.8 that will use some business intelligence tool, like Tableau or Power BI, as a reporting source.
There is absolutely no issue with the code I am using, however I need to be able to reduce visibility to certain pages, based on a created group. For example:
If I have three pages/dashboards:
127.0.0.1:8000/director/report1, 127.0.0.1:8000/manager/report2, 127.0.0.1:8000/employee/report3
and I have three users:
Director, Manager, Employee
How can I create the site in such a way that when a user registers to the site, their profile is created and subsequently assigned a group THEN restrict access to certain pages based on the user group (this would be easier than assigning permission to every user). For example:
The user Director would belong to a group called, directors, and would have access to 127.0.0.1:8000/director/report1, 127.0.0.1:8000/manager/report2, 127.0.0.1:8000/employee/report3.
The user Manager would belong to a group called, managers, and have access to 127.0.0.1:8000/manager/report2, 127.0.0.1:8000/employee/report3.
The user Employee would belong to a group called, employees, and have access to 127.0.0.1:8000/employee/report3.
I found some information related to permissions here: https://docs.djangoproject.com/en/2.1/_modules/django/contrib/auth/decorators/ but I cannot find information related to creating groups AND assigning permissions.
You could extend the default user class in django with a new model named staff and add a charfield with director, manager and employee as given below:
from django.contrib.auth.models import User
class Staff(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
DIRECTOR = "DR"
MANAGER = "MG"
EMPLOYEE = "EM"
DESIGNATION_CHOICES = [
(DIRECTOR, "Director"),
(MANAGER, "Manager"),
(EMPLOYEE, "Employee"),
]
designation = models.CharField(
max_length=2,
choices=DEISGNATION_CHOICES,
default=DIRECTOR,
)
Then you can provide the necessary if,else conditions in your views which will prevent/allow members of certain category to make requests for certain pages.
One way to implement this could be:
#assuming you are using this view function corresponding to '/director/' url
def director_page(request):
if request.user.is_authenticated:
user = request.user
staff = Staff_objects.get(user__id=1)
if staff.designation == "Director":
...#allow director to proceed
else:
...#return an error response
else:
... # Do something for anonymous users.
Related
I am working on a small site using Wagtail. This site is all about a "mainpage" and several "subpages". So far it is pretty simple! But, depending on what group the user (not admin) is in, the right subpages should show up!
See the following setup (minimized), to get an idea of what I am talking about.
If I set permissions on ToolKitPart (like requiring explicit user-login and group-membership), then the following is happening:
when going to the page using the fully qualified path, the user is requested to login and, in the case of insufficient rights, the user will not see the content!
when going to the ToolkitIndex-Page, all children are displayed, including the ones the user never should see, without the need to be logged in or being a member of a certain group.
class ToolkitIndex(Page):
def get_context(self, request):
# Update context to include only published posts, ordered by reverse-chron
context = super().get_context(request)
blogpages = self.get_children().live().order_by('-first_published_at')
context['pages'] = blogpages
return context
class ToolkitPart(Page):
body = StreamField([
('staff', MpStaff()),
('news', MpNews()),
('stuff', MpStuff()),
('teditor', blocks.RichTextBlock()),
('reditor', blocks.RawHTMLBlock()),
], blank=True)
content_panels = Page.content_panels + [
StreamFieldPanel('body'),
]
class MpNews(blocks.StructBlock):
head = blocks.TextBlock(required=True, help_text='Schlagzeile')
lead = blocks.TextBlock(required=False, help_text='Einleitung')
body = blocks.RichTextBlock(required=True, help_text='Inhalt')
image = ImageChooserBlock(required=False)
type = blocks.ChoiceBlock(
choices=[('default', 'Standard'),
('highlight', 'Hervorgehoben'),
], required=True)
class Meta:
template = 'home/mparts/toolkit_news.html'
icon = 'code'
Any idea how to solve this?
Assuming you've set these permissions up using Wagtail's private pages feature, these are stored in the PageViewRestriction model. Unfortunately Wagtail doesn't currently provide a way to apply these permission checks against anything other than the current page request, so you'd have to recreate this logic yourself to filter a queryset to the user's view permissions. This would be something like (untested):
from django.db.models import Q
class ToolkitIndex(Page):
def get_context(self, request):
context = super().get_context(request)
blogpages = self.get_children().live().order_by('-first_published_at')
if not request.user.is_authenticated:
blogpages = blogpages.public() # only pages with no view restrictions at all
else:
blogpages = blogpages.filter(
# pages with no view restrictions
Q(view_restrictions__isnull=True)
# pages restricted to any logged-in user
| Q(view_restrictions__restriction_type='login')
# pages restricted by group
| Q(view_restrictions__restriction_type='groups', view_restrictions__groups__in=request.user.groups.all())
)
Disclaimers:
This doesn't account for pages that are protected by a shared password
To be fully correct, we'd need to account for the fact that view restrictions propagate down the tree (and so a subpage may still be restricted even if it doesn't have a view restriction record directly attached to it); however, we're only looking at immediate children of the current page (which they evidently do have access to...) so that issue doesn't come up here.
PageViewRestriction is not a public Wagtail API and may change in future releases - in particular, see RFC 32 for a proposed change that may happen in the fairly near future.
I'm building a social network where user are supposed to be able to follow each other. So I define a class user with a field: ManyToMany to stock the users that follow this user. This is what I have done in my model.py:
followings = models.ManyToManyField('self', blank=True)
This is my view.py:
#login_required
def follow_test(request):
name = request.POST.get('name', '')
user_followed = Dater.objects.get(username=name)
current_user = Dater.objects.get(id=request.user.id)
print current_user.followings # display my_app.Dater.None
current_user.followings.add(user_followed)
print current_user.followings # display my_app.Dater.None
I retrieve correctly my users (current (The one who follow someone) and the followed one) but I can't add the followed user in the set followings of the current user. Can you see something I don't do properly in my view?
followings is a manager; to show the members of that relationship, you need to call .all() on it (or another manager/queryset method like order_by).
print current_user.followings.all()
I would like to separate users into two different groups, sellers or buyers, at signup. I'm using django-userena and for the authentication and registration of users. I'm thinking of using a clone of the same signup view except with a different url tied to it.
So whoever signs up at url(r'^account/signup/seller/$) linked to a seller signup button will be added to the seller group
and whoever signs up at url(r'^account/signup/$) linked to a buyer signup button will be added to the buyer group.
Note: I will be using this grouping to grant access to view functions in another django app in my project via signals/decorators.
in my accounts/form.py file, I have:
class SellerSignupFormExtra(SignupForm):
def save(self):
new_user = super(SignupFormExtra, self).save()
new_user.groups.add(Group.objects.get(name='seller'))
return new_user
and I added this to accounts/urls.py file
url(r'^accounts/signup/seller$', 'userena_views.signup', {'signup_form': SellerSignupFormExtra}),
So my question is that can I add the other users that click the buyer signup button by doing the same thing I did for sellers above or is their a better way to achieve this so that I remain DRY.
I am building a simple app using User Authentication.
My app has 3 models:
Users : The standard Django user model
Locations: A model for an office (address, site name, etc)
Employees: A model for an employee (name, email, etc)
I also have a series of views that allow a user to login, create, and edit locations/sites, etc.
What I want to know, is what is the best practice to restrict editing of model instances to those which the user has created? E.g. with no amendment, two users could create data and both could edit the others. How do I restrict this to editing their own?
I know the long form way is to put a ForeignKey(User) on each model to restrict the view with a queryset, but this seems lengthy and cumbersome. Is there a Django trick I am missing? Perhaps a decorator?
What's the best practice?
The easiest way would be to edit your models so that they have an owner or user field that is a ForeignKey to the creator.
class Locations(models.Model):
owner = ForeignKey(User)
...
And in your views:
def edit_location(request, location_id):
location = Locations.objects.get(pk=location_id)
if request.user is not location.owner:
# return a 401 or redirect to somewhere
else:
# do stuff
You can use Django's ManyToManyField option if each Location and User can have multiple relationships. For example:
class Locations(models.Model):
owners = models.ManyToManyField(User)
user = User.objects.create(username='Ian')
location = Locations.objects.create(...)
location.owners.add(user)
And then the User/Locations are available on both:
>>> location.owners.all()
[<User: Ian>]
>>> user.locations_set.all()
[<Locations: ...>]
The set on User will be automatically created and it will be named <Model Name>_set.
You can then use the in operator to check ownership:
def edit_location(request, location_id):
location = Locations.objects.get(pk=location_id)
if request.user in location.owners.all():
# return a 401 or redirect to somewhere
else:
# do stuff
I am trying to workout how / the best, most secure way to keep a user's data separate within a django site that I need to write.
Here is an example of what I need to do...
example app ToDoList
Using django contrib.auth to manage users / passwords etc, I will have the following users
tom
jim
lee
There will be a ToDo model (in my real app there will be additional models)
class ToDo(models.Model):
user = models.ForeignKey(User)
description = models.CharField(max_length=20)
details = models.CharField(max_length=50)
created = models.DateTimeField('created on')
The issue that I am having - and may be over thinking this: How would this be locked down so tom can only see Tom's todo list, lee can only see his todo list and so on...
I have seen a few posts stating that you could use filter in every query, or use urls, so the url could look like www.domain.com/username/todo
But either way I am not sure if this is the right way / best way, or bonkers in terms of stopping users seeing each others data
cheers
Richard
One approach is to filter the ToDo items by the currently logged in user:
from django.contrib.auth.decorators import login_required
from django.shortcuts import render
from your_app.models import ToDo
#login_required
def todos_for_user(request):
todos = ToDo.objects.filter(user=request.user)
return render(request, 'todos/index.html', {'todos' : todos})
This locks down the view for authenticated users only, and filtering by the logged in user from the request, another user, even if logged in, can't access another user's ToDo records. Hope that helps you out.
Make url like www.domain.com/username/todo is one way to implement it, but it doesn't guarantee you achieve security.
What you should do keep your user's login information in a session data after user login, and every time you check certain view,
check whether that particular user has right to see this view.
using user's login info (ID, or username) when querying user's Todo list.
And I guess this link will help you to do your job.
Sessions, Users, and Registration.