forked from mmmaly/chcemvediet
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* #354 Login user from admin * #354 Remodel AdminLoginAsBackend * #354 Add review suggestions * #354 Create AdminLoginAsMixin * #354 Move AdminLoginAsMixin to poleno library * #354 Move AdminLoginAsAdminMixin to poleno directory * #354 Allow admin_login_as from UserAdmin and fix wrong pk in ProfileAdmin * #354 Create AccountAdapter * #354 Rename login_as_redirect_viewname attribute to lowercase * #354 Set authentication_backend path * #354 Remove unused import
- Loading branch information
1 parent
b5af447
commit 3343e2f
Showing
6 changed files
with
226 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
from allauth.account.adapter import DefaultAccountAdapter | ||
|
||
from poleno.invitations.adapters import InvitationsAdapter | ||
|
||
|
||
class AccountAdapter(InvitationsAdapter, DefaultAccountAdapter): | ||
def login(self, request, user): | ||
if not hasattr(user, u'backend'): | ||
user.backend = u'chcemvediet.auth_backends.AllauthAuthenticationBackendWithAdminLoginAs' | ||
return super(AccountAdapter, self).login(request, user) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
from allauth.account.auth_backends import AuthenticationBackend | ||
from django.contrib.auth.backends import ModelBackend | ||
|
||
from poleno.utils.admin_login_as import AdminLoginAsBackendMixin | ||
|
||
|
||
class DjangoModelBackendWithAdminLoginAs(AdminLoginAsBackendMixin, ModelBackend): | ||
pass | ||
|
||
class AllauthAuthenticationBackendWithAdminLoginAs(AdminLoginAsBackendMixin, AuthenticationBackend): | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
from django.conf.urls import patterns, url | ||
from django.contrib import admin | ||
from django.contrib.auth.backends import ModelBackend | ||
from django.core.urlresolvers import resolve, Resolver404 | ||
from django.http import HttpResponseRedirect | ||
|
||
from poleno.utils.http import get_request | ||
from poleno.utils.urls import reverse | ||
|
||
|
||
class AdminLoginAsBackendMixin(ModelBackend): | ||
u""" | ||
Django authentication ModelBackend that allows admin to log in as any user (without being logged | ||
off) and perform any action on his behalf, without the admin knowing the user's password. Admin | ||
can simultaneously perform actions on admin page. Must be used together with | ||
``AdminLoginAsAdminMixin``. Note that admin application namespace must be called 'admin'. | ||
Example: | ||
class MyApplicationBackend(AdminLoginAsBackendMixin, SomeAuthenticationBackend): | ||
... | ||
Settings: | ||
AUTHENTICATION_BACKENDS = ( | ||
'path.to.MyApplicationBackend', | ||
... | ||
) | ||
""" | ||
def is_admin_path(self, path): | ||
return resolve(path).namespace == u'admin' | ||
|
||
def get_user(self, user_id): | ||
request = get_request() | ||
user = super(AdminLoginAsBackendMixin, self).get_user(user_id) | ||
if request is None: | ||
return user | ||
try: | ||
resolve(request.path) | ||
except Resolver404: | ||
return user | ||
admin_login_as = request.session.get(u'admin_login_as') | ||
if user and user.is_staff and not self.is_admin_path(request.path) and admin_login_as: | ||
return super(AdminLoginAsBackendMixin, self).get_user(admin_login_as) or user | ||
return user | ||
|
||
class AdminLoginAsAdminMixin(admin.ModelAdmin): | ||
u""" | ||
Django ModelAdmin that defines view, which allows admin to be simultaneously logged in as any | ||
user. Must be used together with ``AdminLoginAsBackendMixin``. | ||
Example: | ||
class MyModelAdmin(AdminLoginAsAdminMixin, admin.ModelAdmin): | ||
list_display = [ | ||
... | ||
decorate( | ||
lambda o: admin_obj_format(o, u'Log in', link=u'login_as'), | ||
), | ||
] | ||
login_as_redirect_viewname = u'application:viewname' | ||
... | ||
""" | ||
def login_as_view(self, request, obj_pk): | ||
request.session[u'admin_login_as'] = obj_pk | ||
return HttpResponseRedirect(reverse(self.login_as_redirect_viewname)) | ||
|
||
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'^(\d+)/login-as/$', login_as_view, name=u'{}_{}_login_as'.format(*info)), | ||
) | ||
return urls + super(AdminLoginAsAdminMixin, self).get_urls() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
from django.contrib.auth.decorators import user_passes_test | ||
from django.conf.urls import patterns, url, RegexURLPattern | ||
from django.contrib.auth.models import User | ||
from django.http import HttpResponse | ||
from django.test import TestCase | ||
from django.test.utils import override_settings | ||
|
||
|
||
class AdminLoginAsBackendMixinTest(TestCase): | ||
|
||
def public_view(request): | ||
if isinstance(request.user, User): | ||
pass # force request.user to evaluate | ||
return HttpResponse() | ||
|
||
@user_passes_test(lambda u: u.is_staff, login_url=u'/login/', redirect_field_name=u'next') | ||
def admin_view(request): | ||
if isinstance(request.user, User): | ||
pass | ||
return HttpResponse() | ||
|
||
@user_passes_test(lambda u: u.is_staff, login_url=u'/login/', redirect_field_name=u'next') | ||
def set_admin_login_as_attribute_admin_view(request, obj_pk): | ||
request.session[u'admin_login_as'] = obj_pk | ||
if isinstance(request.user, User): | ||
pass | ||
return HttpResponse() | ||
|
||
urls = tuple(patterns(u'', | ||
url(r'^$', public_view), | ||
url(r'admin/', ([ | ||
RegexURLPattern(r'^$', admin_view), | ||
RegexURLPattern(r'^(\d+)/login-as/$', set_admin_login_as_attribute_admin_view), | ||
], None, u'admin')), | ||
)) | ||
|
||
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( | ||
AUTHENTICATION_BACKENDS=(u'poleno.utils.admin_login_as.AdminLoginAsBackendMixin',), | ||
PASSWORD_HASHERS=(u'django.contrib.auth.hashers.MD5PasswordHasher',), | ||
) | ||
self.settings_override.enable() | ||
self.create_users() | ||
|
||
def tearDown(self): | ||
self.settings_override.disable() | ||
|
||
|
||
def test_public_route_uses_anonymous_user_if_user_is_not_logged_in(self): | ||
response = self.client.get(u'/') | ||
self.assertEqual(response.status_code, 200) | ||
self.assertTrue(response.wsgi_request.user.is_anonymous()) | ||
|
||
def test_admin_route_uses_anonymous_user_and_fails_if_user_is_not_logged_in(self): | ||
response = self.client.get(u'/admin/') | ||
self.assertTrue(response.wsgi_request.user.is_anonymous()) | ||
self.assertEqual(response.status_code, 302) | ||
self.assertRedirects(response, u'/login/?next=/admin/', fetch_redirect_response=False) | ||
|
||
def test_public_route_uses_the_user_if_user_is_logged_in(self): | ||
self.assertTrue(self.client.login(username=self.user.username, password=u'test')) | ||
response = self.client.get(u'/') | ||
self.assertEqual(response.status_code, 200) | ||
self.assertTrue(response.wsgi_request.user, self.user) | ||
|
||
def test_admin_route_uses_the_user_and_fails_if_user_is_logged_in(self): | ||
self.assertTrue(self.client.login(username=self.user.username, password=u'test')) | ||
response = self.client.get(u'/admin/') | ||
self.assertEqual(response.status_code, 302) | ||
self.assertRedirects(response, u'/login/?next=/admin/', fetch_redirect_response=False) | ||
self.assertEqual(response.wsgi_request.user, self.user) | ||
|
||
def test_public_route_uses_the_admin_if_admin_is_logged_in(self): | ||
self.assertTrue(self.client.login(username=self.superuser.username, password=u'test')) | ||
response = self.client.get(u'/') | ||
self.assertEqual(response.status_code, 200) | ||
self.assertTrue(response.wsgi_request.user, self.superuser) | ||
|
||
def test_admin_route_uses_the_admin_if_admin_is_logged_in(self): | ||
self.assertTrue(self.client.login(username=self.superuser.username, password=u'test')) | ||
response = self.client.get(u'/admin/') | ||
self.assertEqual(response.status_code, 200) | ||
self.assertTrue(response.wsgi_request.user, self.superuser) | ||
|
||
def test_public_route_uses_the_user_if_admin_is_logged_in_as_another_user(self): | ||
self.assertTrue(self.client.login(username=self.superuser.username, password=u'test')) | ||
self.client.get(u'/admin/{}/login-as/'.format(self.user.pk)) | ||
response = self.client.get(u'/') | ||
self.assertEqual(response.status_code, 200) | ||
self.assertTrue(response.wsgi_request.user, self.user) | ||
|
||
def test_admin_route_uses_the_admin_if_admin_is_logged_in_as_another_user(self): | ||
self.assertTrue(self.client.login(username=self.superuser.username, password=u'test')) | ||
self.client.get(u'/admin/{}/login-as/'.format(self.user.pk)) | ||
response = self.client.get(u'/admin/') | ||
self.assertEqual(response.status_code, 200) | ||
self.assertTrue(response.wsgi_request.user, self.superuser) |