using UserProfile to restrict access in view - django

I have an app that displays client assets on html posting pages. Each client authorized to use the system is assigned a profile:
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
fullname = models.CharField(max_length=64, unique=False)
company = models.CharField(max_length=50, choices=CLIENT_CHOICES)
position = models.CharField(max_length=64, unique=False, blank=True, null=True)
...
User.profile = property(lambda u: UserProfile.objects.get_or_create(user=u)[0])
def __unicode__(self):
return u'%s' % self.fullname
class Meta:
ordering = ['fullname']
class Admin:
pass
and there's a model for the post pages:
class PostPage(models.Model):
client = models.CharField(max_length=50, choices=CLIENT_CHOICES)
job_number = models.CharField(max_length=30, unique=True, blank=False, null=False)
job_name = models.CharField(max_length=64, unique=False, blank=False, null=False)
page_type = models.CharField(max_length=50, default='POST')
create_date = models.DateField(("Date"), default=datetime.date.today)
contact = models.ForeignKey(UserProfile)
contact2 = models.ForeignKey(UserProfile, related_name='+', blank=True, null=True)
contact3 = models.ForeignKey(UserProfile, related_name='+', blank=True, null=True)
contact4 = models.ForeignKey(UserProfile, related_name='+', blank=True, null=True)
def __unicode__ (self):
return u'%s %s %s' % (self.client, self.job_number, self.job_name)
class Admin:
pass
and finally, a very simple view function to display the pages:
def display_postings(request, job_number):
records = PostPage.objects.filter(job_number=job_number)
tpl = 'post_page.html'
return render_to_response(tpl, { 'records': records })
The problem is, if you work for and access the system from "ACME" company, there's no logic in the view that would prevent you from viewing records for "BETAMAX" company in addition to your own. How can I modify my view so that if say, user.profile.company = "ACME" , but the request returns a record where PostPage.client = "BETAMAX", access to the record is denied? Additionally, can I have one company group, say user.profile.company = "MY_COMPANY" that has access to all records?

Write a decorator that checks the company of the request.user for the view. The code would look something like this:
def belongs_to_company(func):
def decorator(request, *args, **kwargs):
has_permissions = False
# get current company
...
# get user's list of company
...
# if company not in user's list of company
if not has_permissions:
url = reverse('no_perms')
return redirect(url)
return func(request, *args, **kwargs)
return decorator
A better long term solution is to check out Role Based Access Control libraries like django-guardian

Related

Django Admin, show inline based on slug

Have the following models
class FootballWebsite(models.Model):
"""Football service website."""
url = models.URLField, unique=True)
#football service
id = models.CharField(primary_key=True,
#is this domain blocked
blocked = models.BooleanField(default=False)
#is it online or offline
online = models.BooleanField(default=False)
updated = models.DateTimeField(auto_now=True, auto_now_add=True)
sub_categories = models.ForeignKey(SubCategory, default=1)
referral = models.TextField(blank=True)
mirror = models.ForeignKey('FootballWebsite', blank=True, null=True)
rank = models.PositiveIntegerField(default=0, blank=True, null=True)
screenshot = models.BooleanField(default=False)
class Meta:
"""Meta class."""
app_label = 'ahmia'
def __unicode__(self):
return u'%s' % (self.url)
"""The datetime when the football service was last seen online"""
try:
return self.footballwebsitestatus_set.filter(online=True).latest('time').time
except FootballWebsiteStatus.DoesNotExist:
return None
class FootballWebsiteDescription(models.Model):
"""Football service website description."""
about = models.ForeignKey(Footballebsite)
title = models.TextField(blank=True, null=True)
keywords = models.TextField(blank=True, null=True)
description = models.TextField(blank=True, null=True)
relation = models.URLField(blank=True, null=True)
subject = models.TextField(blank=True, null=True)
type = models.TextField(blank=True, null=True)
updated = models.DateTimeField(auto_now=True, auto_now_add=True)
language = models.TextField(null=True, blank=True)
contactInformation = models.TextField(null=True, blank=True)
officialInfo = models.BooleanField(default=False)
slug = AutoSlugField(populate_from=['title'], allow_duplicates=True, null=True)
class Meta:
"""Meta class."""
app_label = 'ahmia'
def __unicode__(self):
return unicode(self.about.url)
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super(FootballebsiteDescription, self).save(*args, **kwargs)
def __unicode__(self):
return u'%s' % (self.title)
I have a huge amount of links, and i would like to bulk assign them into a category or mark them as blocked based on identical title slug.
Managed to at least get a list of title_slugs with the code below, but the following step, i would like to get an inline list with all sites that have an identical title_slug and bulk assign those all in their a category
class FootballWebsiteInline(admin.TabularInline):
model = FootballWebsite
class FootballWebsiteDescriptionAdmin(admin.ModelAdmin):
list_display = ['show_slug']
def show_slug(self, obj):
return format_html("<a href='{url}'>{url}</a>", url=obj.slug)
inlines = [
FootballWebsiteInline,
]
Above code obviously doesn' t work, since the title slug which can appear many times is not a primary key.
Is it possible to get an inline list based on the title slug in this case which is not a primary key at all, or am i going about this the wrong way?
When possible at all, some further tweaking would be to group the identical title slugs

Django: Assign Route to Order Based on User Input of Location

In my create order view, I am trying to automatically assign the respective route based on the location the user inputs. Basically, every location in the system has a FK to a route. There are many locations within a route. If you select a location to send products to, the route should automatically be tied to.
Currently I am able to see the route for an order in my order_list.html page, but when I view the order in the Django admin, the route is not assigned to the order but the location is.
I want it to work similarly to how you would assign the current logged in user to an order:
form.instance.user = request.user
I tried using:
form.instance.company = request.user.company
But I am getting an attribute error:
'WSGIRequest' object has no attribute 'location'
Here is my full order_create function:
orders/views.py:
#login_required(login_url='account_login')
def order_create(request):
"""
A function that takes the users cart with products, then converts the cart into an
OrderForm. Then saves the form/order to the database.
"""
cart = Cart(request)
if request.method == 'POST':
form = OrderCreateForm(request.POST)
if form.is_valid():
form.instance.user = request.user
form.instance.company = request.user.company
form.instance.route = request.location.route
order = form.save()
for item in cart:
OrderItem.objects.create(order=order,
product=item['product'],
price=item['price'],
quantity=item['quantity'])
# clear the cart
cart.clear()
return render(request,
'orders/order/created.html',
{'order': order,
})
else:
form = OrderCreateForm()
return render(request,
'orders/order/create.html',
{'cart': cart, 'form': form})
Here are the Location, Route and Order models:
orders/models.py:
class Route(models.Model):
name = models.CharField(max_length=50)
def __str__(self):
return self.name
class Locations(models.Model):
"""
A model to represent a location or locations a company has.
"""
name = models.CharField(max_length=100)
company = models.ForeignKey(Company, on_delete=models.CASCADE)
route = models.ForeignKey(Route, on_delete=models.DO_NOTHING)
store_number = models.CharField(max_length=15, blank=True, null=True)
address = models.ForeignKey(Address, on_delete=models.DO_NOTHING, blank=True, null=True)
class Order(models.Model):
user = models.ForeignKey(User, on_delete=models.DO_NOTHING)
route = models.ForeignKey(Route, on_delete=models.DO_NOTHING, blank=True, null=True)
location = models.ForeignKey(Locations, on_delete=models.DO_NOTHING, blank=True, null=True)
company = models.ForeignKey(Company, on_delete=models.DO_NOTHING, blank=True, null=True)
delivery_date = models.DateField()
address = models.ForeignKey(Address, on_delete=models.DO_NOTHING, blank=True, null=True)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
paid = models.BooleanField(default=False)
delivered = models.BooleanField(default=False)
and then finally the company and user model in my accounts app that are used as foreign keys in the orders app.
accounts/models.py:
class Company(models.Model):
"""
A model to represent companies that can operate within the system, which multiple users are apart of.
"""
name = models.CharField(max_length=100)
address = models.CharField(max_length=100)
city = models.CharField(max_length=25)
state = USStateField()
zip = models.CharField(max_length=5)
admin = models.ForeignKey("accounts.User", on_delete=models.DO_NOTHING, related_name="company_admin", blank=True, null=True)
class User(AbstractBaseUser, PermissionsMixin):
"""
A model to represent a User of the system.
"""
ROLE_CHOICES = (
('ADMIN', "Admin"),
('MANAGER', "Manager"),
('DRIVER', "Driver"),
('PRODUCTION', "Production")
)
email = models.EmailField(max_length=254, unique=True)
phone = models.CharField(max_length=15, help_text="(123)-123-1234", blank=True, null=True)
first_name = models.CharField(max_length=254, null=True, blank=True)
last_name = models.CharField(max_length=254, null=True, blank=True)
company = models.ForeignKey(Company, on_delete=models.CASCADE, blank=True, null=True)
role = models.CharField(max_length=10, choices=ROLE_CHOICES, default=None, blank=True, null=True)
is_employee = models.BooleanField(default=False, blank=True, null=True)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
last_login = models.DateTimeField(null=True, blank=True)
date_joined = models.DateTimeField(auto_now_add=True)

Django: How to show user owned data in Admin forms

Following are the models of my app:
class Store(models.Model):
store_owner = models.ForeignKey(User, null=False, verbose_name='User')
store_name = models.CharField(max_length=200, null=False,
verbose_name='Store name')
store_address_line_1 = models.CharField(max_length=200, null=False,
verbose_name='Address line 1')
store_address_line_2 = models.CharField(max_length=200, null=False,
verbose_name='Address line 2')
store_city = models.CharField(max_length=200, null=False,
verbose_name='City')
store_state = models.CharField(max_length=200, null=False,
verbose_name='State')
store_zip_code = models.CharField(max_length=200, null=False,
verbose_name='Zip/Pin Code')
store_country = models.CharField(max_length=200, null=False,
verbose_name='Country')
store_phone = models.CharField(max_length=12, verbose_name='Phone')
store_email = models.EmailField(verbose_name='Email')
store_website = models.URLField(verbose_name='Website')
class StoreDepartment(models.Model):
store = models.ForeignKey(Store, verbose_name='Store')
department_name = models.CharField(max_length=200, null=False,
verbose_name='Department name')
department_description = models.TextField(max_length=250, null=False,
verbose_name='Description')
+++++++++
I am using only the dfault Admin provided by django framwork.
I have 2 users, For both users I have created Stores.
But when I try to create StoreDepartment, I see the list of all the stores in the Select box created for "Store" foreign-key field in StoreDepartment model.
How to customize the default form so that user can see only the Stores created by them in the selectbox.
I used the following formfield_for_foreignkey in Model admin and now its working for me. User can see only the stores owned by him.
class StoreDepartmentAdmin(admin.ModelAdmin):
list_display = ['department_name', 'store']
ordering = ['id']
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == "store":
kwargs["queryset"] =Store.objects.filter(store_owner=request.user)
return super(StoreDepartmentAdmin,self).formfield_for_foreignkey(db_field, request, **kwargs)

How to create a user profile with different views in django?

I've two type of users: Students and Institutions.
Both can login in the website and they have different profiles views.
I'd like to use "http://example.com/accounts/" for both types of users but running different logics and displaying different templates for each one.
For example, Students when they go to their profile they can view/modify their attributes as what are they studying, interested courses.. etc. On the other side, the institution users can view/modify attributes of their own model as institution information.
Institution User Type:
class InstitutionProfile(models.Model):
user = models.OneToOneField(User, related_name='client')
gender = models.CharField(max_length=40, choices=GENDERS_TYPES, blank=True)
#Contact Information
location = models.ManyToManyField(Location)
address = models.CharField(max_length=254, blank=True)
zipcode = models.CharField(max_length=56, blank=True)
phone = models.CharField(max_length=56, blank=True)
def __unicode__(self):
return '%s' % format(self.user)
Student User Type:
class StudentProfile(models.Model):
user = models.OneToOneField(User, related_name='profile')
about_me = models.TextField(null=True, blank=True)
gender = models.CharField(max_length=40, choices=GENDERS_TYPES, blank=True)
birth = models.DateTimeField(blank=True, null=True)
#Contact Information
location = models.ManyToManyField(Location, related_name='homecountry')
address = models.CharField(max_length=254, blank=True)
zipcode = models.CharField(max_length=56, blank=True)
phone = models.CharField(max_length=56, blank=True)
#Interested
countries_interested = models.ManyToManyField(Location, blank=True, related_name='countries interested')
areas_interested = models.ManyToManyField(StudyArea, blank=True)
levels_interested = models.ManyToManyField(StudyLevel, blank=True)
languages_interested= models.ManyToManyField(LanguageCourse, blank=True)
def __unicode__(self):
return '%s' % format(self.user)
view.py
class InstitutionProfileDetailView(DetailView):
model = get_user_model()
slug_field = "username"
template_name = "account/institution_profile.html"
def get_object(self, queryset=None):
user = super(InstitutionProfileDetailView, self).get_object(queryset)
InstitutionProfile.objects.get_or_create(user=user)
return user
class StudentProfileDetailView(DetailView):
model = get_user_model()
slug_field = "username"
template_name = "account/student_profile.html"
def get_object(self, queryset=None):
user = super(StudentProfileDetailView, self).get_object(queryset)
StudentProfile.objects.get_or_create(user=user)
return user
What is the best solution for having different views with 2 different type of users?
You can use an unified view and return different views from that according to your logic -
def accounts_view(request):
if request.user.is_student(): # <-- check with your logic, is_student() is a stub
return StudentProfileDetailView.as_view()
elif request.user.is_institute():
return InstitutionProfileDetailView.as_view()
And point accounts/ to accounts_view.

Overriding Django profiles' profile_detail view

I installed django profiles/registration and everything seems to be fine. When a user registers their profile is created also. Now what i want to do is query another Model which is Company based on the user id of User. I dont want to change django-profiles view but add the extra field on urls to match and query Company model. When i hardcode the url (ex:put the id number of the userprofile like so userprofile=1, it works.). So when a user is logged in and goes to profile detail page Company assigned to them is queried based on their user.id.
class UserProfile(models.Model):
user = models.OneToOneField(User)
#email = models.CharField(max_length=200, blank=True, null=True)
# Other fields here
#company = models.ForeignKey(Company,blank=True,null=True)
#office = models.CharField(max_length=200, blank=True, null=True)
def __unicode__(self):
return self.user.username
class Company(models.Model):
userprofile = models.ForeignKey(UserProfile, null=True, blank=True)
comp_name = models.CharField(max_length=200,blank=True,null=True)
comp_address = models.CharField(max_length=200,blank=True, null=True)
comp_email = models.CharField(max_length=200,blank=True, null=True)
comp_zip = models.IntegerField(blank=True, null=True)
comp_phone = models.IntegerField(blank=True, null=True)
comp_city = models.CharField(max_length=200,blank=True, null=True)
#comp_state = models.USStateField(blank=True, null=True
comp_state = models.CharField(blank=True, max_length=2)
compwebsite = models.URLField(max_length=200, blank=True, null=True)
twitterurl = models.URLField(max_length=200, blank=True, null=True)
facebookurl = models.URLField(max_length=200, blank=True, null=True)
def __unicode__(self):
return self.comp_name
url(r'^profiles/(?P<username>\w+)/$', 'profiles.views.profile_detail', {'extra_context':{'queryset':Company.objects.filter(userprofile=request.user.id)}},),
You might want to call it from inside a view
from *** import profile_detail
def my_view(request, username):
extra_context = {}
return profile_detail(request, queryset=Company.objects.filter(userprofile=request.user.id),
template_name="my_template.html",
paginate_by=20,
extra_context=extra_context)