Принудительная аутентификация любым пользователем
В целях отладки проекта мне понадобилось быстро просматривать сайт глазами конкретного пользователя. Для этого я решил написать отдельный view, в который передаю id пользователя. В документации Django написано, что для авторизации пользователя нужно сначала выполнить фунцию authenticate из модуля django.contrib.auth, а затем login.
authenticate занимается тем, что по очереди до первого успеха опрашивает все доступные бэкенды аутентификации, перечисленные в settings.AUTHENTICATION_BACKENDS, передавая им параметры собственного вызова. В случае успеха backend должен вернуть объект типа django.contrib.auth.User.models, в противном случае None.
login получает в параметрах request и user и синхронизирует request.user и user, в зависимости от того, что из них является None.
Трудность заключается в том, что стандартный backend аутентификации Django, принимает в качестве параметров имя пользователя и незашифрованный пароль. Незашифрованные пароли пользователей мне неизвестны. Пришлось отказаться от функции authenticate и передавать в login пользователя полученного непосредственно по запросу User.objects.get(pk=user_id).
Однако, после такого финта, Django стал возмущаться на то, что у объекта пользователя передаваемого в функцию login нет атрибута backend, в который функция authenticate (которую я не вызываю) должна была записать имя успешно сработавшего бэкенда аутентификации.
Мне помогло прописывание стандартного бэкенда. Таким образом код приобрёл следующую форму:
C помощью опции settings.LOGIN_DEBUG можно быстро отлючать/включать отладочную функцию логина.
Ну, и в завершение я чуть-чуть изменил свой inclusion tag, который использую в шаблонах для отображения ссылки на переданный объект. Теперь этот тэг выводит звёздочку рядом с именем пользователя, которая является ссылкой на вышеописанный view.
Собственно, вот сам код тэга:
и шаблон:
UPD:
А вот реализация для middleware, здесь ID пользователя, которым нужно залогиниться, передаётся просто через GET-параметр loginas
authenticate занимается тем, что по очереди до первого успеха опрашивает все доступные бэкенды аутентификации, перечисленные в settings.AUTHENTICATION_BACKENDS, передавая им параметры собственного вызова. В случае успеха backend должен вернуть объект типа django.contrib.auth.User.models, в противном случае None.
login получает в параметрах request и user и синхронизирует request.user и user, в зависимости от того, что из них является None.
Трудность заключается в том, что стандартный backend аутентификации Django, принимает в качестве параметров имя пользователя и незашифрованный пароль. Незашифрованные пароли пользователей мне неизвестны. Пришлось отказаться от функции authenticate и передавать в login пользователя полученного непосредственно по запросу User.objects.get(pk=user_id).
Однако, после такого финта, Django стал возмущаться на то, что у объекта пользователя передаваемого в функцию login нет атрибута backend, в который функция authenticate (которую я не вызываю) должна была записать имя успешно сработавшего бэкенда аутентификации.
Мне помогло прописывание стандартного бэкенда. Таким образом код приобрёл следующую форму:
def loginas(request,user_id):
if not getattr(settings,'LOGIN_DEBUG',False):
raise AccessDeniedException('/')
else:
user = User.objects.get(pk=user_id)
user.backend = 'django.contrib.auth.backends.ModelBackend'
auth.login(request, user)
return HttpResponseRedirect(user.get_profile().get_absolute_url())
e
C помощью опции settings.LOGIN_DEBUG можно быстро отлючать/включать отладочную функцию логина.
Ну, и в завершение я чуть-чуть изменил свой inclusion tag, который использую в шаблонах для отображения ссылки на переданный объект. Теперь этот тэг выводит звёздочку рядом с именем пользователя, которая является ссылкой на вышеописанный view.
Собственно, вот сам код тэга:
@register.inclusion_tag('site/link.html')
def link(object,base=u''):
user_id = None
if isinstance(object,User):
object = object.get_profile()
if isinstance(object,UserProfile):
if getattr(settings,'LOGIN_DEBUG',False):
user_id = object.user.id
url = object.get_absolute_url()
url = base + url
anchor = smart_unicode(object)
return {
'user_id': user_id,
'url': url,
'anchor': anchor}
и шаблон:
<a href="{{ url }}">{{ anchor|escape }}</a>
{% if user_id %}
<a href="/debug/loginas/{{ user_id }}/">*</a>
{% endif %}
UPD:
А вот реализация для middleware, здесь ID пользователя, которым нужно залогиниться, передаётся просто через GET-параметр loginas
from django.http import HttpResponseRedirect
from django.conf import settings
from django.contrib.auth.models import User
from django.contrib.auth import login
class DebugLoginMiddleware(object):
def process_request(self, request):
if not getattr(settings,'LOGIN_DEBUG',False):
return None
try:
id = int(request.GET.get('loginas',0))
except ValueError:
return None
try:
user = User.objects.get(pk=id)
except User.DoesNotExist:
return None
user.backend = 'django.contrib.auth.backends.ModelBackend'
login(request, user)
return None






Comments