Django- restrict users to access urls - python

i'm creating a app.it has manytomany field to store data about class and students.
urls.py
url(r'^class/(?p<title>[-\w]+)/(?p<id>[\d]+)/',views.list,name ='list'),
Basically one user(Teacher) can create many class_room .Each class_room have one title and many students following in that class.
problem is:
Each class_room have unique url. Eg (mywebsite.com/science/88/) this link is access only for following students not for anonymous user.This is a loop hole if any non following students try some random url like this they could see the page (mywebsite.com/maths/2500/).
How to restrict a student from access a page which he is not following?

the UserPassesTestMixin mixin can be used to this effect. Basically, write a View Class that implements the test_funcfunction. This function has access to self so you can read the URL and the user. if the test_func returns True, the user is allowed to go on, otherwise is passed to access control (probably redirected to the login form if configured).

Related

Eve framework: user restricted resource access

I'm using Eve framework and I'm trying to use User-Restricted resource access as described in:
http://python-eve.org/authentication.html#user-restricted-resource-access
I'm doing something like:
class CustomAuth(TokenAuth):
def check_auth(self, token, allowed_roles, resource, method):
# Get user as an instance of UserResource.
if user and hasattr(user, 'id'):
self.set_request_auth_value(user['id'])
request.authenticated_user = user
...
So, there are a few question from my side:
Is it enough for using User-Restricted Resource Access?
How this field adds into user created objects?
Is this additional field called id in my user created objects? Is it possible to rename it?
As I understand it should be named same as it's called in User resource. Is it true?
Does this field (property) applies for newly created objects only? Is it possible to fetch previously created objects by current user following this way?
Well, I want to know an answers for my questions + clarify how it may be used.
Is it an expected way to extract it somehow in my hooks?
user_id = current_app.auth.get_request_auth_value()
current_app.data.driver.session.query(resource).find({'id': user_id})
Is this block of code from hook expected?
How it behaves if my requested resource has its own id field?
P.S. I was reading a post:
https://stackoverflow.com/a/35654252/7335432
The user-restricted access feature prevents users from accessing records they didn't create. The set_request_auth_value() method does:
1) Upon making a POST request to create a record, it automatically adds a field specified as AUTH_FIELD (or auth_field if you only want to do it to a specific resource). So for example, if you declare in settings.py
AUTH_FIELD = "my_auth_field"
and then add
set_request_auth_value(user['id'])
to your authentication method, that means that your app creates a field "my_auth_field" that has its value set to whatever user["id"] is. So if you were to go into Mongo Compass or some other DBMS and manually inspect your records, you'd see a "my_auth_field" field in there.
2) On GET requests when you access those records, Eve checks the "my_auth_field" value against whatever user["id"] is, and only displays the records where "my_auth_field" is equal to user["id"]. Since this field is added automatically when you create a record using Eve, it effectively filters out everything that specific user didn't create.
So yes, it only applies to newly created objects. I'm not sure exactly what you mean by "is it enough", but it doesn't look like 'user' is declared anywhere in your authentication class. You might wanna check out this tutorial they do incorporating user restricted access into token authentication.

Django: Filter request results to only contain data related to the requesting user

I'm a Django beginner (though I do have experience in web development using Sails.js + Angular) so bear with me.
I have an existing application that uses REST API in communicating between Sails.js backend and AngularJS frontend. Now, we've found the backend to be unsuited for our purposes, and we're going to swap to using Django in near-future. Sails.js automatically creates the REST methods for the controllers while Django doesn't, so I suppose I'm going to use something like Django Rest Framework to create the API.
So yeah, I've found corresponding features for most things. The on thing I haven't found yet is a replacement for a Sails.js feature called "policies". They are functions that can be executed on queries to certain controller actions, and can be defined as model-specific, model-controller action-specific, and request type specific. For example, you can have an "authAccess" policy that checks that the user of a request is authenticated, and the policy gets executed before the actual requested controller method gets executed. You can also use these to modify request objects before passing them to the controller. Now to my actual problem:
Let's say I have a User model that has a many-to-one relation with another model, let's call it Book, meaning a user can own many books, but a book can only have one owner. Goody good. Now, we have a logged-in user that is making a query to find all of his books. He makes a GET request to /book. I want to ensure that the returned Book objects are filtered so that ONLY HIS BOOKS are returned.
So basically in Sails I was able to write a policy that altered the request parameters to be like {user: loggedInUser} so the resulting Book results were automatically filtered. This was handy, since I was able to use the same policy to filter other models, too, like DVD or Friend or whatnot. My question is - what would be the best way to implement the same functionality in Django?
Have a look at the documentation:
http://www.django-rest-framework.org/api-guide/filtering/#filtering-against-the-current-user
Most likely you are better off overwriting the get_queryset method in a model viewset. And you can make this a generic approach by creating a base class for your views, something like:
from rest_framework import generics, viewsets, mixins, generics
class OwnerModelViewSet(viewsets.ModelViewSet):
def get_queryset(self):
"""
This view should return a list of all the records
for the currently authenticated user.
"""
return self.model.objects.filter(user=self.request.user)
All your model viewset classes can inherit from that class. It would require the foreign key field to be always named "user" though. If that is not the case here is a slightly hacky way how you could find a foreign key field to the User table. Use with care.
from django.db.models.fields.related import ForeignKey
from accounts.models import User
def _get_related_user(self, obj):
'''
Search for FK to user model and return field name or False if no FK.
This can lead to wrong results when the model has more than one user FK.
'''
for f in self.model._meta.fields:
if isinstance(f, ForeignKey) and f.rel.to == User:
return f.name
return False

Update Multiple Items with Input Value in Django Admin

I would like to set the value of a field in multiple rows of Django Admin.
For example if I had database of books with shelf locations I might move several books to another shelf. I need a way, within Django Admin, to input the new shelf location and update the multiple selected items.
I have seen that you can run Admin Actions but I need an easy way to input a value into the action.
You can do this with admin actions, by providing an intermediate page with a form to input the value you want:
https://docs.djangoproject.com/en/1.8/ref/contrib/admin/actions/#actions-that-provide-intermediate-pages
Alternatively you could use some client-side scripting to collect the value from the user and append it to the querystring (or as an extra input field int he POST data) when submitting the admin action form.
Your admin action function receives the request object as an argument so has access to the extra GET/POST fields:
https://docs.djangoproject.com/en/1.8/ref/contrib/admin/actions/#adding-actions-to-the-modeladmin

Django custom admin view like history

I need to add a custom view to a model admin similar to the history view. For example if I have a model called Job I can access the history by going to /jobs/job//history/. How do I add another view that will respond to a pattern like /jobs/job//workflow/?
You can define get_urls() on your Admin to add more admin views.
don't forget the admin_view() wrapper
if you wanted to add a view for an individual object (like the change form), just add the object id to your url pattern, then in your view (try to) grab the corresponding object.
It's up to you to provide the links (for example, by overriding the base (/change_form) template and adding a new item to the "object-tools" list).

Pyramid: modifying site content based on settings

Is it possible to alter the contents of a view/template based on specific user-defined settings in development.ini or production.ini.
As an example say I am developing a pyramid web-app that lists all the students of the class. The back-end database has only one table - 'student'. Now I develop an optional script that also adds a table 'teacher' to the database. Ideally the web-app should be able to run for both the cases. If teacher table is missing, it will not query it and simply print student details. If teacher table is present, it will print name of the teacher along with the name of the student.
To my mind this can be accomplished in one of the following ways -
Keep separate routes (URLs) for teacher+student and student only
pages. The problem is that you cannot stop people from actually
calling the former when you only have student info. This will lead
to unnecessary error pages
Use a setting teacher_enabled=true/false in .ini file. The setting can be accessed in __ init __.py file through settings['teacher_enabled']. Configure just a single route (say'home','/') but map it to different views based on whether seeting variable is true/false. This will not allow for use of #view_config decorator and templates for both cases will have to be separate
Again using the setting variable, pass it to the view somehow. Make only relevant queries in the view. E.g. - If the teacher_enabled is True, query the teacher table else query the student table only. Pass this variable to templates too and there decide if some details are to be displayed (e.g. teacher name).
So my question is which of these approaches should I use? In case settings variables are to be passed to the views, how can that be done? And is there a standard way of dealing with this problem?
Keep separate routes (URLs) for teacher+student and student only pages. The problem is that you cannot stop people from actually calling the former when you only have student info.
Ah, but you can! Combine it with number 2: Add a teacher_enabled=true/false setting to your .ini file, and then you can use some code similar to this:
from pyramid.threadlocal import get_current_registry
from pyramid.httpexceptions import HTTPFound
#Define some awesome student views here
#view_config(name='student')
def student(request):
return HTTPFound('Foo!')
if get_current_registry().settings['teacher_enabled']:
#Some awesome student and teacher views here
#view_config(name='student_and_teacher')
def student_and_teacher(request):
return HTTPFound('Bar!')
Number 3 is also feasible. Just remember: It's easier to ask for forgiveness than permission. So you could do something like this: (Using SQLAlchemy as an example)
from your_models.teacher import Teacher
from sqlalchemy.exc import NoSuchTableError
try:
teacher = session.query(Teacher).filter(Teacher.id==42).first()
except NoSuchTableError:
teacher = Teacher('Unknown')

Categories

Resources