I used a custom auth backend in my django project to connect users, my problem is that i'm not able to connect using the admin interface anymore.
this is my custom auth files :
auth_backends.py:
from django.conf import settings
from django.contrib.auth.backends import ModelBackend
from django.core.exceptions import ImproperlyConfigured
from django.db.models import get_model
class StudentModelBackend(ModelBackend):
def authenticate(self, username=None, password=None):
try:
user = self.user_class_s.objects.get(username=username)
if user.check_password(password):
return user
except self.user_class_s.DoesNotExist:
return None
def get_user(self, user_id):
try:
return self.user_class_s.objects.get(pk=user_id)
except self.user_class_s.DoesNotExist:
return None
#property
def user_class_s(self):
if not hasattr(self, '_user_class'):
self._user_class = get_model(*settings.CUSTOM_USER_MODEL.split('.', 2))
if not self._user_class:
raise ImproperlyConfigured('Could not get student model')
return self._user_class
class ProfessorModelBackend(ModelBackend):
def authenticate(self, username=None, password=None):
try:
user = self.user_class_p.objects.get(username=username)
if user.check_password(password):
return user
except self.user_class_p.DoesNotExist:
return None
def get_user(self, user_id):
try:
return self.user_class_p.objects.get(pk=user_id)
except self.user_class_p.DoesNotExist:
return None
#property
def user_class_p(self):
if not hasattr(self, '_user_class'):
self._user_class = get_model(*settings.CUSTOM_USER_MODEL_P.split('.', 2))
if not self._user_class:
raise ImproperlyConfigured('Could not get student model')
return self._user_class
settings.py:
AUTHENTICATION_BACKENDS = (
'authentification.auth_backends.StudentModelBackend',
'authentification.auth_backends.ProfessorModelBackend',
)
CUSTOM_USER_MODEL = 'forum.Student'
CUSTOM_USER_MODEL_P = 'forum.Professor'
I tried this solution :
from django.contrib.auth.decorators import login_required
admin.autodiscover()
admin.site.login = login_required(admin.site.login)
but it redirect me to the user auth interface instead of admin interface.
Could someone help me please ?
I Found the solution,
i just have to tell django to try connecting using the original backend if the user isnt a student or professor.
just add 'django.contrib.auth.backends.ModelBackend' in settings.py
settings.py :
AUTHENTICATION_BACKENDS = (
'authentification.auth_backends.StudentModelBackend',
'authentification.auth_backends.ProfessorModelBackend',
'django.contrib.auth.backends.ModelBackend',
)
The default login_required decorator will default to the settings.py LOGIN_REDIRECT_URL. However, you can also explicitly tell it which login view to use by passing the login_url keyword argument.
https://github.com/django/django/blob/stable/1.8.x/django/contrib/auth/decorators.py#L39
This may be used with the generic login_required view and my samples below show more precise conditionals by using the user_passes_test decorator.
For instance:
from django.contrib.auth.decorators import user_passes_test
def superuser_required(*args, **kwargs):
return user_passes_test(lambda u: u.is_superuser, login_url='admin:login')(*args, **kwargs)
def custom_login_required(*args, **kwargs):
return user_passes_test(lambda u: getattr(u, 'is_custom_user', False), login_url='custom-url-name-from-urls-py')(*args, **kwargs)
Then you should be able to use this new decorate as you have above with the generic login_required:
admin.site.login = custom_login_required(admin.site.login)
Related
I use cache_page decorator for caching views in Django.
How can I disable cache for superuser or authorized users ?
You can create a custom decorator with the help of CacheMiddleware class as
from django.middleware.cache import CacheMiddleware
from django.utils.decorators import decorator_from_middleware_with_args
class CustomCacheMiddleware(CacheMiddleware):
def process_request(self, request):
if request.user.is_superuser:
request._cache_update_cache = False
return None
return super().process_request(request)
def custom_cache_page(timeout, *, cache=None, key_prefix=None):
return decorator_from_middleware_with_args(CustomCacheMiddleware)(
cache_timeout=timeout, cache_alias=cache, key_prefix=key_prefix
)
#custom_cache_page(60 * 15)
def my_view(request):
...
I have a new app:
newApp
with 3 users
[admin, user1, user2]
and a login page (not from /admin/). Is it possible to restrict/redirect access to django's Admin login? i want to just get into the admin/ path if the user logged IS and admin, That means it should not work if im not logged nor logged as normal user.
You could write a custom view decorator to check if user is admin, and if not raise a Http404, for example:
def user_is_admin(function):
def wrap(request, *args, **kwargs):
if request.user.is_authenticated and request.user.is_admin:
return function(request, *args, **kwargs)
else:
raise Http404
return wrap
user.is_admin would be a custom implementation, just an example. Now you just need to wrap your views
#user_is_admin
def my_view(request):
...
Im adding this answer because the one provided by Pedro couldnt answer the part that i was trying to figure out, not letting anyone access to the /admin/ path.
I used Django-Decorator-Include to mix it with Pedro's custom decorator
and the result on my urls.py is:
from decorator_include import decorator_include
from django.core.exceptions import PermissionDenied
def user_is_admin(function):
def wrap(request, *args, **kwargs):
if request.user.is_authenticated and request.user.is_staff:
return function(request, *args, **kwargs)
else:
raise PermissionDenied
return wrap
urlpatterns = [
.....
path('admin/', decorator_include([user_is_admin], admin.site.urls)),
.....
]
I am using Django-allauth for my login/signup related stuff, so when a user signs up(first time) into my site, I am redirecting him to /thanks/ page by defining below setting in settings.py file
LOGIN_REDIRECT_URL = '/thanks/'
But when the user tried to log in for the next time(if already registered) I should redirect him to '/dashboard/' URL
So tried to alter that with Django-allauth signals like below which is not working at all
#receiver(allauth.account.signals.user_logged_in)
def registered_user_login(sender, **kwargs):
instance = User.objects.get_by_natural_key(kwargs['user'])
print instance.last_login==instance.date_joined,"??????????????????????????????"
if not instance.last_login==instance.date_joined:
return HttpResponseRedirect(reverse('dashboard'))
So can anyone please let me know how to redirect a user to /dashboard/ for the normal login, am I doing anything wrong in the above signal code?
Edit
After some modification according to the below answer by pennersr, my AccountAdapter class looks like below
from allauth.account.adapter import DefaultAccountAdapter
# from django.contrib.auth.models import User
class AccountAdapter(DefaultAccountAdapter):
def get_login_redirect_url(self, request):
if request.user.last_login == request.user.date_joined:
return '/registration/success/'
else:
return '/dashboard/'
But still, it is redirecting the user to /dashboard/, my logic in determining the first time user is wrong?
In general, you should not try to put such logic in a signal handler. What if there are multiple handlers that want to steer in different directions?
Instead, do this:
# settings.py:
ACCOUNT_ADAPTER = 'project.users.allauth.AccountAdapter'
# project/users/allauth.py:
class AccountAdapter(DefaultAccountAdapter):
def get_login_redirect_url(self, request):
return '/some/url/'
The two datetimes last_login and date_joined will always be different, although it might only be a few milliseconds. This snippet works:
# settings.py:
ACCOUNT_ADAPTER = 'yourapp.adapter.AccountAdapter'
# yourapp/adapter.py:
from allauth.account.adapter import DefaultAccountAdapter
from django.conf import settings
from django.shortcuts import resolve_url
from datetime import datetime, timedelta
class AccountAdapter(DefaultAccountAdapter):
def get_login_redirect_url(self, request):
threshold = 90 #seconds
assert request.user.is_authenticated()
if (request.user.last_login - request.user.date_joined).seconds < threshold:
url = '/registration/success'
else:
url = settings.LOGIN_REDIRECT_URL
return resolve_url(url)
One important remark to pennersr answer: AVOID using files named allauth.py as it will confuse Django and lead to import errors.
the answer here is very simple, you do not need any signals or overriding the DefaultAccountAdapter
in settings.py just add a signup redirect_url
ACCOUNT_SIGNUP_REDIRECT_URL = "/thanks/"
LOGIN_REDIRECT_URL = "/dashboard/"
You can simply define those two other signals using user_logged_in signal as base. A good place to put it is on a signals.py inside a accounts app, in case you have one, or in you core app. Just remember to import signals.py in you __init__.py.
from django.dispatch import receiver, Signal
pre_user_first_login = Signal(providing_args=['request', 'user'])
post_user_first_login = Signal(providing_args=['request', 'user'])
#receiver(user_logged_in)
def handle_user_login(sender, user, request, **kwargs):
first_login = user.last_login is None
if first_login:
pre_user_first_login.send(sender, user=user, request=request)
print 'user_logged_in'
if first_login:
post_user_first_login.send(sender, user=user, request=request)
#receiver(pre_user_first_login)
def handle_pre_user_first_login(sender, user, request, **kwargs):
print 'pre_user_first_login'
#receiver(post_user_first_login)
def handle_post_user_first_login(sender, user, request, **kwargs):
print 'post_user_first_login'
I want to check if a user is allowed to register for a specific event. I thought in order to save code I could do it like the login_required decorator right between the url() and MyClass.as_view(). But how do I get that slug? Or is this solution totally wrong? (I unfortunatelly can't use the user_passes_test because I don't want to test someting on the user but on the url.)
So I tryed this:
views.py
from registration.models import Event
from django.utils import timezone
from django.contrib.auth.decorators import login_required
from django.shortcuts import get_object_or_404
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
def reg_is_open(event_slug):
"""
Return True if registration is open.
"""
event = get_object_or_404(Event, slug=event_slug)
if event.open_date <= timezone.now() and event.cut_off >= timezone.now():
return True
def allow_view(cls, **initkwargs):
"""
Check weather registration is open and user is logged in.
Returns to registration start page if registration is closed.
"""
slug = initkwargs.get('event') # Does not work!
if not reg_is_open(slug):
return HttpResponseRedirect(reverse('registration:event_index', args=slug))
return login_required(cls.as_view(**initkwargs))
# Also works when I remove **initkwargs. That means that what I'm looking for just passes...
urls.py
from django.conf.urls import patterns, url, include
from registration import views
event_patterns = patterns('',
url(r'^person/$', views.allow_view(views.PersonList), name='person_list'),
# instead of
# url(r'^person/$', login_required(views.PersonList.as_view()), name='person_list'),
# ...
urlpatterns = patterns('',
url(r'^(?P<event>[-a-zA-Z0-9_]+)/$', views.EventDetails.as_view(), name='event_index'),
url(r'^(?P<event>[-a-zA-Z0-9_]+)/', include(event_patterns)),
# ...
If I understand correctly, your view is a DetailView of the event, correct? In that case, use something along the lines of:
class EventDetailView(DetailView):
model = Event
def dispatch(self, request, *args, **kwargs):
if self.request.user not in self.object.registered_users.all():
return HttpResponseForbidden()
else:
return super(EventDetailView, self).dispatch(request, *args, **kwargs)
This assumes that you have a M2M key between User and Event models, called registered_users on the Event side; change the code to fit your situation.
I'm working on a platform for online labs registration for my university.
Login View [project views.py]
from django.http import HttpResponse, HttpResponseRedirect, Http404
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.contrib import auth
def index(request):
return render_to_response('index.html', {}, context_instance = RequestContext(request))
def login(request):
if request.method == "POST":
post = request.POST.copy()
if post.has_key('username') and post.has_key('password'):
usr = post['username']
pwd = post['password']
user = auth.authenticate(username=usr, password=pwd)
if user is not None and user.is_active:
auth.login(request, user)
if user.get_profile().is_teacher:
return HttpResponseRedirect('/teachers/'+user.username+'/')
else:
return HttpResponseRedirect('/students/'+user.username+'/')
else:
return render_to_response('index.html', {'msg': 'You don\'t belong here.'}, context_instance = RequestContext(request)
return render_to_response('login.html', {}, context_instance = RequestContext(request))
def logout(request):
auth.logout(request)
return render_to_response('index.html', {}, context_instance = RequestContext(request))
URLS
#========== PROJECT URLS ==========#
urlpatterns = patterns('',
(r'^media/(?P<path>.*)$', 'django.views.static.serve', {'document_root': settings.MEDIA_ROOT }),
(r'^admin/', include(admin.site.urls)),
(r'^teachers/', include('diogenis.teachers.urls')),
(r'^students/', include('diogenis.students.urls')),
(r'^login/', login),
(r'^logout/', logout),
(r'^$', index),
)
#========== TEACHERS APP URLS ==========#
urlpatterns = patterns('',
(r'^(?P<username>\w{0,50})/', labs),
)
The login view basically checks whether the logged in user is_teacher [UserProfile attribute via get_profile()] and redirects the user to his profile.
Labs View [teachers app views.py]
from django.http import HttpResponse, HttpResponseRedirect, Http404
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.contrib.auth.decorators import user_passes_test
from django.contrib.auth.models import User
from accounts.models import *
from labs.models import *
def user_is_teacher(user):
return user.is_authenticated() and user.get_profile().is_teacher
#user_passes_test(user_is_teacher, login_url="/login/")
def labs(request, username):
q1 = User.objects.get(username=username)
q2 = u'%s %s' % (q1.last_name, q1.first_name)
q2 = Teacher.objects.get(name=q2)
results = TeacherToLab.objects.filter(teacher=q2)
return render_to_response('teachers/labs.html', {'results': results}, context_instance = RequestContext(request))
I'm using #user_passes_test decorator for checking whether the authenticated user has the permission to use this view [labs view].
The problem I'm having with the current logic is that once Django authenticates a teacher user he has access to all teachers profiles basically by typing the teachers username in the url.
Once a teacher finds a co-worker's username he has direct access to his data.
Any suggestions would be much appreciated.
A simple way would be to modify the view to add an extra check:
#user_passes_test(user_is_teacher, login_url="/login/")
def labs(request, username):
if username != request.user.username:
return HttpResponseNotAllowed()
... and so on ...
Assuming you have a variable called 'teacher' that represents the profile of the teacher whose profile you're viewing, just do something like this early in the view:
if request.user.get_profile() != teacher:
..redirect, throw 404, whatever you fancy
Just a short hint.
...
user = request.user
enrollment = get_object_or_404(Enrollment, id=enrollment_id)
profile = get_object_or_404(Profile, user=user)
if not (enrollment.profile == profile or user.is_staff):
raise Http404
...
enrollment.delete()
We used such if statements to determine, whether the actual user and the action he requested match. In the example above, only the profile who create an enrollment is allowed to delete it (or someone with staff priviledges).