Skip to content

Commit

Permalink
#354 Login user from admin
Browse files Browse the repository at this point in the history
  • Loading branch information
viliambalaz committed Nov 28, 2021
1 parent 5f4315e commit 94ec4f0
Show file tree
Hide file tree
Showing 6 changed files with 236 additions and 2 deletions.
29 changes: 28 additions & 1 deletion chcemvediet/apps/accounts/admin.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
# vim: expandtab
# -*- coding: utf-8 -*-
from django import forms
from django.conf.urls import patterns, url
from django.contrib import admin
from django.contrib.auth import login
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404

from poleno.utils.misc import decorate
from poleno.utils.admin import simple_list_filter_factory, admin_obj_format
from poleno.utils.misc import decorate
from poleno.utils.urls import reverse

from .models import Profile

Expand Down Expand Up @@ -44,6 +50,10 @@ class ProfileAdmin(admin.ModelAdmin):
short_description=u'Undecided E-mails',
admin_order_field=u'undecided_emails_count',
),
decorate(
lambda o: admin_obj_format(o, u'Log in', link=u'login_as'),
short_description=u'Admin Login',
),
]
list_filter = [
u'anonymize_inforequests',
Expand Down Expand Up @@ -79,3 +89,20 @@ def get_queryset(self, request):
queryset = queryset.select_related(u'user')
queryset = queryset.select_undecided_emails_count()
return queryset

def login_as_view(self, request, id):
admin_login_as = request.session.get(u'admin_login_as')
user = get_object_or_404(User, pk=id)
if not hasattr(user, u'backend'):
user.backend = u'chcemvediet.apps.accounts.backends.AdminLoginAsBackend'
login(request, user)
request.session[u'admin_login_as'] = admin_login_as
return HttpResponseRedirect(reverse(u'inforequests:mine'))

def get_urls(self):
info = self.model._meta.app_label, self.model._meta.model_name
login_as_view = self.admin_site.admin_view(self.login_as_view)
urls = patterns('',
url(r'^(.+)/login-as/$', login_as_view, name=u'{}_{}_login_as'.format(*info)),
)
return urls + super(ProfileAdmin, self).get_urls()
39 changes: 39 additions & 0 deletions chcemvediet/apps/accounts/backends.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from django.contrib.auth import login
from django.contrib.auth.backends import ModelBackend
from django.core.urlresolvers import resolve

from poleno.utils.http import get_request


class AdminLoginAsBackend(ModelBackend):

def is_admin_path(self, path):
return resolve(path).func.__module__ in [
u'django.contrib.admin.options',
u'django.contrib.admin.sites',
u'adminplus.sites',
]

def get_user(self, user_id):
request = get_request()
admin_login_as = request.session.get(u'admin_login_as')
user = super(AdminLoginAsBackend, self).get_user(user_id)
admin_user = super(AdminLoginAsBackend, self).get_user(admin_login_as)
if not user:
return None
if user.is_staff:
request.session[u'admin_login_as'] = user.id

# todo: - je potrebne odlisit admin-url od "specialnej" admin-url, ktora sluzi na prihlasenie ineho usera
# - aby nebolo explictne uvedene `'/login-as/'` ale nieco vseobecne
# Ak by dana if vetva neexistovala, nebolo by mozne pouzit multiple_login (vid. test).
if request.path.endswith(u'/login-as/') and admin_login_as:
return admin_user

if self.is_admin_path(request.path) and not user.is_staff and admin_login_as:
if admin_user:
if not hasattr(admin_user, u'backend'):
admin_user.backend = u'chcemvediet.apps.accounts.backends.AdminLoginAsBackend'
login(request, admin_user)
return admin_user
return user
51 changes: 51 additions & 0 deletions chcemvediet/apps/accounts/tests/test_admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from django.contrib.auth.models import User
from django.test import TestCase
from django.test.utils import override_settings

from poleno.utils.test import ViewTestCaseMixin
from poleno.utils.urls import reverse


class ProfileAdminTest(ViewTestCaseMixin, TestCase):
u"""
Tests ``login_as_view()`` view registered as "admin:accounts_profile_login_as".
"""

def create_users(self):
self.user = User.objects.create_user(
username=u'user',
email=u'[email protected]',
password=u'test',
)
self.superuser = User.objects.create_superuser(
username=u'superuser',
email=u'[email protected]',
password=u'test',
)

def setUp(self):
self.settings_override = override_settings(
PASSWORD_HASHERS=(u'django.contrib.auth.hashers.MD5PasswordHasher',),
)
self.settings_override.enable()
self.create_users()

def tearDown(self):
self.settings_override.disable()


def test_anonymous_user_is_redirected(self):
url = reverse(u'admin:accounts_profile_login_as', args=(self.user.pk,))
response = self.client.get(url, follow=True)
expected_url = reverse(u'admin:login') + u'?next=' + url
self.assertRedirects(response, expected_url)

def test_staff_user_gets_inforequest_detail(self):
self.assertTrue(self.client.login(
username=self.superuser.username, password=u'test'
))
url = reverse(u'admin:accounts_profile_login_as', args=(self.user.pk,))
response = self.client.get(url, follow=True)
expected_url = reverse(u'inforequests:mine')
self.assertRedirects(response, expected_url)
self.assertEqual(self.client.session[u'_auth_user_id'], self.user.id)
82 changes: 82 additions & 0 deletions chcemvediet/apps/accounts/tests/test_backend.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
from django.contrib.auth.models import User
from django.test import TestCase
from django.test.utils import override_settings

from poleno.utils.urls import reverse


class AdminLoginAsBackend(TestCase):

def create_users(self):
self.user1 = User.objects.create_user(
username=u'user1',
email=u'[email protected]',
password=u'test',
)
self.user2 = User.objects.create_user(
username=u'user2',
email=u'[email protected]',
password=u'test',
)
self.superuser1 = User.objects.create_superuser(
username=u'superuser1',
email=u'[email protected]',
password=u'test',
)
self.superuser2 = User.objects.create_superuser(
username=u'superuser2',
email=u'[email protected]',
password=u'test',
)

def setUp(self):
self.settings_override = override_settings(
AUTHENTICATION_BACKENDS=(u'chcemvediet.apps.accounts.backends.AdminLoginAsBackend',),
PASSWORD_HASHERS=(u'django.contrib.auth.hashers.MD5PasswordHasher',),
)
self.settings_override.enable()
self.create_users()

def tearDown(self):
self.settings_override.disable()


def test_multiple_requests(self):
self.assertTrue(self.client.login(
username=self.superuser1.username, password=u'test'
))
url1 = reverse(u'admin:accounts_profile_login_as', args=(self.user1.pk,))
url2 = reverse(u'admin:accounts_profile_login_as', args=(self.user2.pk,))
expected_url = reverse(u'inforequests:mine')
self.assertRedirects(self.client.get(url1, follow=True), expected_url)
self.assertEqual(self.client.session[u'_auth_user_id'], self.user1.pk)
self.assertRedirects(self.client.get(url2, follow=True), expected_url)
self.assertEqual(self.client.session[u'_auth_user_id'], self.user2.pk)

def test_logging_back_admin(self):
self.client.login(username=self.superuser1.username, password=u'test')
url = reverse(u'admin:accounts_profile_login_as', args=(self.user1.pk,))
self.client.get(url, follow=True)
self.assertEqual(self.client.session[u'_auth_user_id'], self.user1.pk)
response = self.client.get(reverse(u'admin:index'))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, u'adminplus/index.html')
self.assertEqual(self.client.session[u'_auth_user_id'], self.superuser1.pk)

def test_staff_user_login_as_other_staff_user(self):
self.client.login(username=self.superuser1.username, password=u'test')
self.assertEqual(self.client.session[u'_auth_user_id'], self.superuser1.pk)
url = reverse(u'admin:accounts_profile_login_as', args=(self.superuser2.pk,))
response = self.client.get(url, follow=True)
self.assertRedirects(response, reverse(u'inforequests:mine'))
self.assertEqual(self.client.session[u'_auth_user_id'], self.superuser2.pk)
response2 = self.client.get(reverse(u'admin:index'))
self.assertEqual(response2.status_code, 200)
self.assertTemplateUsed(response2, u'adminplus/index.html')
self.assertEqual(self.client.session[u'_auth_user_id'], self.superuser2.pk)

def test_login_to_non_existing_user(self):
self.client.login(username=self.superuser1, password=u'test')
url = reverse(u'admin:accounts_profile_login_as', args=(344,))
response = self.client.get(url)
self.assertEqual(response.status_code, 404)
2 changes: 1 addition & 1 deletion chcemvediet/settings/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@
)

AUTHENTICATION_BACKENDS = (
u'django.contrib.auth.backends.ModelBackend',
u'chcemvediet.apps.accounts.backends.AdminLoginAsBackend',
u'allauth.account.auth_backends.AuthenticationBackend',
)

Expand Down
35 changes: 35 additions & 0 deletions poleno/utils/backends.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from django.contrib.auth import login
from django.contrib.auth.backends import ModelBackend
from django.core.urlresolvers import resolve

from poleno.utils.http import get_request


class AdminLoginAsBackend(ModelBackend):

def is_admin_path(self, path):
return resolve(path).func.__module__ in [
u'django.contrib.admin.options',
u'django.contrib.admin.sites',
u'adminplus.sites',
]

def get_user(self, user_id):
user = super(AdminLoginAsBackend, self).get_user(user_id)
request = get_request()
admin_login_as = request.session.get(u'admin_login_as')

if not user:
return None

if user.is_staff:
request.session[u'admin_login_as'] = user.id

if self.is_admin_path(request.path) and not user.is_staff and admin_login_as:
admin_user = super(AdminLoginAsBackend, self).get_user(admin_login_as)
if admin_user:
if not hasattr(admin_user, u'backend'):
admin_user.backend = u'poleno.utils.backends.AdminLoginAsBackend'
login(request, admin_user)
return admin_user
return user

0 comments on commit 94ec4f0

Please sign in to comment.