0001-add-a-switch-user-feature-fixes-8142.patch
src/authentic2/backends/models_backend.py | ||
---|---|---|
61 | 61 |
return user |
62 | 62 |
else: |
63 | 63 |
user_login_failure(user.get_username()) |
64 | ||
65 | ||
66 |
class DummyModelBackend(ModelBackend): |
|
67 |
def authenticate(self, user=None): |
|
68 |
if user is not None: |
|
69 |
return user |
src/authentic2/constants.py | ||
---|---|---|
2 | 2 |
NONCE_FIELD_NAME = 'nonce' |
3 | 3 |
CANCEL_FIELD_NAME = 'cancel' |
4 | 4 |
AUTHENTICATION_EVENTS_SESSION_KEY = 'authentication-events' |
5 |
SWITCH_USER_SESSION_KEY = '_switch_user' |
|
6 |
LAST_LOGIN_SESSION_KEY = '_last_login' |
src/authentic2/context_processors.py | ||
---|---|---|
3 | 3 |
from pkg_resources import get_distribution |
4 | 4 |
from django.conf import settings |
5 | 5 | |
6 |
from . import utils, app_settings |
|
6 |
from . import utils, app_settings, constants
|
|
7 | 7 | |
8 | 8 |
class UserFederations(object): |
9 | 9 |
'''Provide access to all federations of the current user''' |
... | ... | |
41 | 41 |
__AUTHENTIC2_DISTRIBUTION = str(get_distribution('authentic2')) |
42 | 42 |
variables['AUTHENTIC2_VERSION'] = __AUTHENTIC2_DISTRIBUTION |
43 | 43 |
variables['add_to_blocks'] = defaultdict(lambda:[]) |
44 |
variables['LAST_LOGIN'] = request.session.get(constants.LAST_LOGIN_SESSION_KEY) |
|
45 |
variables['USER_SWITCHED'] = constants.SWITCH_USER_SESSION_KEY in request.session |
|
44 | 46 |
return variables |
src/authentic2/manager/user_views.py | ||
---|---|---|
8 | 8 |
from django.contrib.auth import get_user_model |
9 | 9 |
from django.contrib import messages |
10 | 10 |
from django.http import HttpResponseRedirect |
11 |
from django.views.generic.detail import SingleObjectMixin |
|
12 |
from django.views.generic import View |
|
11 | 13 | |
14 |
from authentic2.constants import SWITCH_USER_SESSION_KEY |
|
12 | 15 |
from authentic2.models import Attribute, PasswordReset |
16 |
from authentic2.utils import switch_user |
|
13 | 17 | |
14 | 18 |
from .views import BaseTableView, BaseAddView, PassRequestToFormMixin, \ |
15 | 19 |
BaseEditView, ActionMixin, OtherActionsMixin, Action, ExportMixin, \ |
... | ... | |
104 | 108 |
_('Delete'), |
105 | 109 |
_('Do you really want to delete "%s" ?') % |
106 | 110 |
self.object.username) |
111 |
if self.request.user.is_superuser: |
|
112 |
yield Action('switch_user', _('Impersonate this user')) |
|
107 | 113 | |
108 | 114 |
def action_force_password_change(self, request, *args, **kwargs): |
109 | 115 |
PasswordReset.objects.get_or_create(user=self.object) |
... | ... | |
156 | 162 |
def action_delete_password_reset(self, request, *args, **kwargs): |
157 | 163 |
PasswordReset.objects.filter(user=self.object).delete() |
158 | 164 | |
165 |
def action_switch_user(self, request, *args, **kwargs): |
|
166 |
return switch_user(request, self.object) |
|
167 | ||
159 | 168 |
# Copied from PasswordResetForm implementation |
160 | 169 |
def send_mail(self, subject_template_name, email_template_name, |
161 | 170 |
context, to_email): |
src/authentic2/profile_urls.py | ||
---|---|---|
69 | 69 |
url(r'^password/reset/done/$', |
70 | 70 |
auth_views.password_reset_done, |
71 | 71 |
name='auth_password_reset_done'), |
72 |
url(r'^switch-back/$', profile_views.switch_back, name='a2-switch-back'), |
|
72 | 73 |
) |
src/authentic2/profile_views.py | ||
---|---|---|
114 | 114 |
return utils.login(self.request, self.user, 'email') |
115 | 115 | |
116 | 116 |
password_reset_confirm = PasswordResetConfirmView.as_view() |
117 | ||
118 | ||
119 |
def switch_back(request): |
|
120 |
return utils.switch_back(request) |
src/authentic2/settings.py | ||
---|---|---|
134 | 134 |
'authentic2.backends.ldap_backend.LDAPBackend', |
135 | 135 |
'authentic2.backends.ldap_backend.LDAPBackendPasswordLost', |
136 | 136 |
'authentic2.backends.models_backend.ModelBackend', |
137 |
'authentic2.backends.models_backend.DummyModelBackend', |
|
137 | 138 |
'django_rbac.backends.DjangoRBACBackend', |
138 | 139 |
) |
139 | 140 |
AUTHENTICATION_BACKENDS = plugins.register_plugins_authentication_backends( |
src/authentic2/templates/authentic2/base.html | ||
---|---|---|
26 | 26 |
{% if request.user.is_authenticated %} |
27 | 27 |
<div id="user"> |
28 | 28 |
{% block user %} |
29 |
<p title="{% blocktrans with last_login=request.session.last_login %}Last login {{ last_login }}{% endblocktrans %}">
|
|
29 |
<p title="{% blocktrans %}Last login {{ LAST_LOGIN }}{% endblocktrans %}">
|
|
30 | 30 |
{% blocktrans with request.user.get_full_name as username %}Hello {{ username }}.{% endblocktrans %} |
31 | 31 |
<a id="logout" href="{% url 'auth_logout' %}">{% trans "Logout" %}</a> |
32 | 32 |
</p> |
src/authentic2/templates/idp/account_management.html | ||
---|---|---|
34 | 34 |
{% if allow_account_deletion %} |
35 | 35 |
<p><a href="{% url 'delete_account' %}">{% trans "Delete profile" %}</a></p> |
36 | 36 |
{% endif %} |
37 |
{% if USER_SWITCHED %} |
|
38 |
<p><a href="{% url 'a2-switch-back' %}">{% trans "Switch back" %}</a></p> |
|
39 |
{% endif %} |
|
37 | 40 |
</div> |
38 | 41 |
<h3>{% trans "Credentials" %}</h3> |
39 | 42 |
{% for html_block in frontends_block %} |
src/authentic2/utils.py | ||
---|---|---|
13 | 13 |
import django |
14 | 14 |
from django.conf import settings |
15 | 15 |
from django.http import HttpResponseRedirect |
16 |
from django.core.exceptions import ImproperlyConfigured |
|
16 |
from django.core.exceptions import ImproperlyConfigured, PermissionDenied
|
|
17 | 17 |
from django.core import urlresolvers |
18 | 18 |
from django.http.request import QueryDict |
19 |
from django.contrib.auth import REDIRECT_FIELD_NAME, login as auth_login |
|
19 |
from django.contrib.auth import (REDIRECT_FIELD_NAME, login as auth_login, |
|
20 |
SESSION_KEY, HASH_SESSION_KEY, BACKEND_SESSION_KEY, authenticate) |
|
20 | 21 |
from django import forms |
21 | 22 |
from django.forms.util import ErrorList |
22 | 23 |
from django.forms.utils import to_current_timezone |
... | ... | |
28 | 29 |
from django.core import signing |
29 | 30 |
from django.core.urlresolvers import reverse |
30 | 31 |
from django.utils.formats import localize |
32 |
from django.contrib import messages |
|
33 |
from django.utils.functional import empty |
|
31 | 34 | |
32 | 35 |
try: |
33 | 36 |
from django.core.exceptions import FieldDoesNotExist |
... | ... | |
303 | 306 |
URL or settings.LOGIN_REDIRECT_URL.''' |
304 | 307 |
last_login = user.last_login |
305 | 308 |
auth_login(request, user) |
306 |
if 'last_login' not in request.session: |
|
307 |
request.session['last_login'] = localize(to_current_timezone(last_login), True) |
|
309 |
if constants.LAST_LOGIN_SESSION_KEY not in request.session: |
|
310 |
request.session[constants.LAST_LOGIN_SESSION_KEY] = \ |
|
311 |
localize(to_current_timezone(last_login), True) |
|
308 | 312 |
record_authentication_event(request, how) |
309 | 313 |
return continue_to_next_url(request, **kwargs) |
310 | 314 | |
... | ... | |
567 | 571 |
def to_dict_of_set(d): |
568 | 572 |
'''Convert a dictionary of sequence into a dictionary of sets''' |
569 | 573 |
return dict((k, set(v)) for k, v in d.iteritems()) |
574 | ||
575 | ||
576 |
def switch_user(request, new_user): |
|
577 |
'''Switch to another user and remember currently logged in user in the |
|
578 |
session. Reserved to superusers.''' |
|
579 |
logger = logging.getLogger(__name__) |
|
580 |
if constants.SWITCH_USER_SESSION_KEY in request.session: |
|
581 |
messages.error(request, _('Your user is already switched, go to your ' |
|
582 |
'account page and come back to your original ' |
|
583 |
'user to do it again.')) |
|
584 |
else: |
|
585 |
if not request.user.is_superuser: |
|
586 |
raise PermissionDenied |
|
587 |
switched = {} |
|
588 |
for key in (SESSION_KEY, BACKEND_SESSION_KEY, HASH_SESSION_KEY, |
|
589 |
constants.LAST_LOGIN_SESSION_KEY): |
|
590 |
switched[key] = request.session[key] |
|
591 |
user = authenticate(user=new_user) |
|
592 |
auth_login(request, user) |
|
593 |
request.session[constants.SWITCH_USER_SESSION_KEY] = switched |
|
594 |
if constants.LAST_LOGIN_SESSION_KEY not in request.session: |
|
595 |
request.session[constants.LAST_LOGIN_SESSION_KEY] = \ |
|
596 |
localize(to_current_timezone(new_user.last_login), True) |
|
597 |
messages.info(request, _('Successfully switched to user %s') % |
|
598 |
new_user.get_full_name()) |
|
599 |
logging.info('switched to user %s', new_user) |
|
600 |
return continue_to_next_url(request) |
|
601 | ||
602 | ||
603 |
def switch_back(request): |
|
604 |
'''Switch back to original superuser after a user switch''' |
|
605 |
logger = logging.getLogger(__name__) |
|
606 |
if constants.SWITCH_USER_SESSION_KEY in request.session: |
|
607 |
switched = request.session[constants.SWITCH_USER_SESSION_KEY] |
|
608 |
for key in switched: |
|
609 |
request.session[key] = switched[key] |
|
610 |
del request.session[constants.SWITCH_USER_SESSION_KEY] |
|
611 |
del request._cached_user |
|
612 |
request.user._wrapped = empty |
|
613 |
messages.info(request, _('Successfully switched back to user %s') % |
|
614 |
request.user.get_full_name()) |
|
615 |
logger.info('switched back to user %s', request.user) |
|
616 |
else: |
|
617 |
messages.warning(request, _('No user to switch back to')) |
|
618 |
return continue_to_next_url(request) |
|
570 |
- |