Restrict so users only view a certain part of website-Django - python

I am using Django, and require certain 'trial' users only to activate a certain part of the website-any ideas on an efficient way to do this?
I was thinking about giving a paying customer a certain ID and linking this to the URL of the sites for permission.
Thanks,
Tom

I'd use a view decorator like this:
def paying_only(view):
def _decorated(request, *args, **kwargs):
if not is_paying(request.user):
redirect('page_explaining_that_this_is_for_paying_users_only')
return view(request, *args, **kwargs)
return _decorated
#paying_only
def some_view(request):
...

I decided to post my solution, I might even get some feedback. I have a middleware blocking request/responses on certain paths defined in settings, first the middleware:
import re
from django.conf import settings
from django.shortcuts import redirect
class InvitationRequired(object):
def process_response(self, request, response):
if not settings.CLOSED_BETA_ACTIVE:
return response
if (hasattr(request, 'user')
and hasattr(request.user, 'is_authenticated')
and request.user.is_authenticated()):
return response
elif (request.path in
settings.CLOSED_BETA_INVITATION_MIDDLEWARE_EXCEPTED_URIS):
return response
elif response.status_code < 200 or response.status_code >= 300:
return response
else:
for regex in \
settings.CLOSED_BETA_INVITATION_MIDDLEWARE_EXCEPTED_PATTERNS:
if re.compile(regex).match(request.path):
return response
return redirect(settings.CLOSED_BETA_INVITATION_MIDDLEWARE_REDIRECT)
In settings.py I have something like this:
CLOSED_BETA_ACTIVE = True
CLOSED_BETA_INVITATION_URL = '/invitation/'
CLOSED_BETA_INVITATION_MIDDLEWARE_REDIRECT = CLOSED_BETA_INVITATION_URL
CLOSED_BETA_INVITATION_MIDDLEWARE_EXCEPTED_PATTERNS = (
r'^/api/v1/',
r'^/static/',
r'^/media/',
r'^/admin/',
r'^/registration/',
r'^/',
)
Hope it's clear, at least it can give you a a different approach.

This is a very wide-ranging question. One solution would be to store a trial flag on each user. On an authenticated request, check for User.trial in your controller (and probably view) and selectively allow/deny access to the endpoint or selectively render parts of the page.
If you wish to use built-in capabilities of Django, you could view 'trial' as a permission, or a user group.

Related

how i can set middleware for some views in django?

i have a django project with 50 url (in 1 application).
i would set a custom middleware for 20 urls.
when add middleware to MIDDLEWARE_CLASSES , this set for all urls .
How should this be done?
(i use last version of python and djnago)
You can utilize decorators, view's decorators behave similarly to middlewares, simple ex:
from functools import wraps
def custom_view_decorator(view_function):
#wraps(view_function)
def wrap(request, *args, **kwargs):
# Any preprocessing conditions..etc.
return view_function(request, *args, **kwargs)
return wrap
Usage in views.py:
#custom_view_decorator
def my_view(request, *args, **kwargs):
# view code, return response
you can either add a check condition to your middleware which is not recommended(since you cannot determine whether this middleware is working or not without reading it's code). or create a decorator for those views, decorator job is exactly this.
for starters you could do something like this:
class BlockUrlsMiddleware(MiddlewareMixin):
REDIRECT_URL = reverse("not_allowed")
WHITELISTED_URLS = [
"/your/custom/views/urls",
"/your/custom/apis/urls",
]
def process_request(self, request):
if request.path in self.WHITELISTED_URLS:
return None
else:
return HttpResponseRedirect(self.REDIRECT_URL)
few points to Note:
return None means everything is good and serve this request.
return HttpResponseRedirect means URLs are accessed which are not allowed and block this request by redirecting it to your default URLs (that you need to set) in this case REDIRECT_URL.

Differentiating unauthenticated users in custom decorator

Django beginner here.
I have been using the inbuilt login_required decorator. I want to override it for certain users who's referral urls match a certain pattern (e.g. all users originating from /buy_and_sell/).
My purpose is to show a special login page to just these users, and a generic one to everyone else.
I've been looking at various examples of writing custom decorators (e.g. here, here, here and here). But I find the definitions hard to grasp for a beginner. Can someone give me a layman's understanding (and preferably illustrative example) of how I can solve my problem?
There is user_passes_test decorator included in Django.You do not have to make your own decorator.
from django.contrib.auth.decorators import user_passes_test
def check_special_user(user):
return user.filter(is_special=True)
# if not the special user it will redirect to another login url , otherwise process the view
#user_passes_test(check_special_user,login_url='/login/')
def my_view(request):
pass
...
Need Request in decorator
To do that make a clone version of user_passes_test in your project or app and make change as follow,
def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
"""
Decorator for views that checks that the user passes the given test,
redirecting to the log-in page if necessary. The test should be a callable
that takes the user object and returns True if the user passes.
"""
def decorator(view_func):
#wraps(view_func, assigned=available_attrs(view_func))
def _wrapped_view(request, *args, **kwargs):
if test_func(request.user): # change this line to request instead of request.user
return view_func(request, *args, **kwargs)
path = request.build_absolute_uri()
resolved_login_url = resolve_url(login_url or settings.LOGIN_URL)
# If the login url is the same scheme and net location then just
# use the path as the "next" url.
login_scheme, login_netloc = urlparse(resolved_login_url)[:2]
current_scheme, current_netloc = urlparse(path)[:2]
if ((not login_scheme or login_scheme == current_scheme) and
(not login_netloc or login_netloc == current_netloc)):
path = request.get_full_path()
from django.contrib.auth.views import redirect_to_login
return redirect_to_login(
path, resolved_login_url, redirect_field_name)
return _wrapped_view
return decorator
change test_func(request.user) to test_func(request) and you will get
whole request in your decorator function.
Edit: In url.py ,
url (
r'^your-url$',
user_passes_test(check_special_user, login_url='/login/')(
my_view
),
name='my_view'
)
Here the best answer to understand python decorators : How to make a chain of function decorators?
You can use login_url argument of login_required :
#login_required(login_url='some_url)
Another way is to create a custom decorator, an example from the documentation of Django :
from django.contrib.auth.decorators import user_passes_test
def email_check(user):
return user.email.endswith('#example.com')
#user_passes_test(email_check)
def my_view(request):
...

Django login page with parameters

In Django application I am working on right now, user session is marked as multiple after same user login on another device, so after page reloading first user logs out and is redirected to admin/login page.
Need to show first user warning about this on admin/login page. The only thing, that came up on my mind for now is to add GET parameter to this page url, check it in view, and show this warning. But I can't find the class/method to override, where I can check if session is expired or not and add GET parameter, say multiple_sessions=1 url.
I assume, it would look something like this:
def logout(self, request, *args, **kwargs):
if request.session['multiple']:
super(CustomLogout, self).logout(request, redirect_to='/admin/login?multiple_session=1', *args, **kwargs)
else:
super(CustomLogout, self).logout(request, *args, **kwargs)
So the question is, what exactly method is responsible for redirecting user to logout?
Any other ideas of achieving this behaviour are greatly appreciated!
Not sure if I understood correctly. Your question is, how to show a message to the user after they logged out, is that correct?
Django has the messages framework to do just that, showing messages to a user.
from django.contrib import messages
def logout(self, request, *args, **kwargs):
if request.session['multiple']:
messages.info(request, 'Something multiple sessions.')
super(CustomLogout, self).logout(request, *args, **kwargs)
Used this custom middleware class to achieve needed behavior:
class LogoutMiddleware(object):
def process_response(self, request, response):
if hasattr(request, 'user'):
if request.user.is_authenticated() and request.session.exists(request.session.session_key):
current_session = Session.objects.get(pk=request.session.session_key)
if current_session.old == True:
response_redirect = HttpResponseRedirect(reverse('logout'))
response_redirect.set_cookie('multiple-sessions', True)
return response_redirect
return response
Then, if the login view finds this cookie, it renders warning in template.

Nested decorators, raising 404

Suppose I have view
def foo(request)
I'm also using custom user model as follow:
class MyUser(AbstractUser):
field = models.BooleanField()
I'd like to combine 2 django decorators: login_required and user_passed_test, so that anonymous user should be redirected to a login page and user who is not allowed to see the view (user.field == False) should see a proper message (say, something like 'you're not allowed to see this').
So I tried:
my_decor = user_passes_test(lambda user: user.field == True,
login_url='/bar')
def custom_decor(view_func):
decorated_view_func = login_required(my_decor(view_func), login_url='/login')
return decorated_view_func
And I also have to define view:
def bar(request):
return HttpResponse('you are not allowed to see this context.')
and hardcode it in my urls.
The question is: can I do this without creating an additional view and adding it to urls? Is there a way to make 'user_passed_test' decorator raising an 404 error instead of redirecting to a login view?
There is probably a very simple solution and surely I'm just missing it.
I dont remember very well the decorators syntax, but you can do:
def custom_decor(view_func):
def decorator(request, *args, **kwargs)
if request.user.field is True:
return view_func(request, *arg, **kwargs)
raise Http404
return decorator
so...
#login_required(login_url='/login')
#custom_decor
def foo(request)
# ...

I get a 400 Bad Request error while using django-piston

I am trying to use Piston to provide REST support to Django.
I have implemented my handlers as per the documentation provided .
The problem is that i can "read" and "delete" my resource but i cannot "create" or "update".
Each time i hit the relevant api i get a 400 Bad request Error.
I have extended the Resource class for csrf by using this commonly available code snippet:
class CsrfExemptResource(Resource):
"""A Custom Resource that is csrf exempt"""
def __init__(self, handler, authentication=None):
super(CsrfExemptResource, self).__init__(handler, authentication)
self.csrf_exempt = getattr(self.handler, 'csrf_exempt', True)
My class (code snippet) looks like this:
user_resource = CsrfExemptResource(User)
class User(BaseHandler):
allowed_methods = ('GET', 'POST', 'PUT', 'DELETE')
#require_extended
def create(self, request):
email = request.GET['email']
password = request.GET['password']
phoneNumber = request.GET['phoneNumber']
firstName = request.GET['firstName']
lastName = request.GET['lastName']
self.createNewUser(self, email,password,phoneNumber,firstName,lastName)
return rc.CREATED
Please let me know how can i get the create method to work using the POST operation?
This is happening because Piston doesn't like the fact that ExtJS is putting "charset=UTF-8" in the content-type of the header.
Easily fixed by adding some middleware to make the content-type a bit more Piston friendly, create a file called middleware.py in your application base directory:
class ContentTypeMiddleware(object):
def process_request(self, request):
if request.META['CONTENT_TYPE'] == 'application/x-www-form-urlencoded; charset=UTF-8':
request.META['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'
return None
Then simply include this middleware in your settings.py:
MIDDLEWARE_CLASSES = (
'appname.middleware.ContentTypeMiddleware',
)
Proposed solutions still did not work for me (django 1.2.3/piston 0.2.2) so I've tweaked joekrell solution and this finally works (I'm only using POST and PUT, but presumably you can add other verbs to the list):
class ContentTypeMiddleware(object):
def process_request(self, request):
if request.method in ('POST', 'PUT'):
# dont break the multi-part headers !
if not 'boundary=' in request.META['CONTENT_TYPE']:
del request.META['CONTENT_TYPE']
with:
MIDDLEWARE_CLASSES = (
'appname.middleware.ContentTypeMiddleware',
)
I haven't noticed any side-effect, but I can't promise it's bullet-proof.
I have combined some of what other people have said, and added support for any content type, json for instance...
class ContentTypeMiddleware(object):
def process_request(self, request):
if request.method in ('POST', 'PUT') and request.META['CONTENT_TYPE'].count(";") > 0:
request.META['CONTENT_TYPE'] = [c.strip() for c in request.META['CONTENT_TYPE'].split(";") ][0]
return None
I thought Eric's solution worked the best but then ran into problems when saving things in admin. This tweak seems to fix it if anyone else comes across it:
class ContentTypeMiddleware(object):
def process_request(self, request):
if request.method in ('POST') and not 'boundary=' in request.META['CONTENT_TYPE']:
request.META['CONTENT_TYPE'] = [c.strip() for c in request.META['CONTENT_TYPE'].split(";") ][0]
return None
In utils.py, change this.
def content_type(self):
"""
Returns the content type of the request in all cases where it is
different than a submitted form - application/x-www-form-urlencoded
"""
type_formencoded = "application/x-www-form-urlencoded"
ctype = self.request.META.get('CONTENT_TYPE', type_formencoded)
if ctype.strip().lower().find(type_formencoded) >= 0:
return None
return ctype
https://bitbucket.org/jespern/django-piston/issue/87/split-charset-encoding-form-content-type
This is solution which worked for me, after a tweak:
class ContentTypeMiddleware(object):
def process_request(self, request):
if 'charset=UTF-8' in request.META['CONTENT_TYPE']:
request.META['CONTENT_TYPE'] = request.META['CONTENT_TYPE'].replace('; charset=UTF-8','')
return None
We had a resource that was simply updating a timestamp based on the request credentials and PUT. It turns out that Piston does not like PUT without a payload. Adding an empty string payload '' fixed it.
A quick Google search indicates that other systems like Apache may not like PUT without a payload, as well.

Categories

Resources