misc: use one-time tokens instead of cache (#39745)

This commit is contained in:
Benjamin Dauvergne 2020-02-11 14:45:24 +01:00
parent 3d55b1a0c5
commit 4e35e3b572
5 changed files with 45 additions and 33 deletions

View File

@ -281,7 +281,7 @@ class UserDetailView(OtherActionsMixin, BaseDetailView):
def action_su(self, request, *args, **kwargs):
return redirect(request, 'auth_logout',
params={REDIRECT_FIELD_NAME: build_su_url(self.object)})
params={REDIRECT_FIELD_NAME: switch_user.build_url(self.object)})
# Copied from PasswordResetForm implementation
def send_mail(self, subject_template_name, email_template_name,
@ -821,7 +821,7 @@ class UserSuView(MediaMixin, TitleMixin, PermissionMixin, DetailView):
ctx['su_url'] = make_url(
'auth_logout',
params={REDIRECT_FIELD_NAME: build_su_url(self.object, self.duration)},
params={REDIRECT_FIELD_NAME: switch_user.build_url(self.object, self.duration)},
request=self.request,
absolute=True)
ctx['duration'] = self.duration

View File

@ -111,7 +111,7 @@ urlpatterns = [
url(r'^$', views.homepage, name='auth_homepage'),
url(r'^login/$', views.login, name='auth_login'),
url(r'^logout/$', views.logout, name='auth_logout'),
url(r'^su/(?P<token>[a-f0-9]+)/$', views.su, name='su'),
url(r'^su/(?P<uuid>[A-Za-z0-9_-]+)/$', views.su, name='su'),
url(r'^accounts/', include(accounts_urlpatterns)),
url(r'^admin/', include(admin.site.urls)),
url(r'^idp/', include('authentic2.idp.urls')),

View File

@ -867,34 +867,6 @@ def to_dict_of_set(d):
return dict((k, set(v)) for k, v in d.items())
def build_su_url(user, duration=30):
token = get_hex_uuid()
data = {'user_pk': user.pk}
cache.set('switch-%s' % token, data, duration)
return make_url('su', kwargs={'token': token})
HEX_RE = re.compile('^[a-f0-9]+$')
def get_su_user(token):
User = get_user_model()
if not token:
return None
if not HEX_RE.match(token):
return None
key = 'switch-%s' % token
data = cache.get(key)
if not isinstance(data, dict):
return None
if not data.get('user_pk'):
return None
cache.delete(key)
try:
return User.objects.get(pk=data['user_pk'])
except User.DoesNotExist:
return None
def datetime_to_utc(dt):
if timezone.is_naive(dt):
dt = timezone.make_aware(dt, timezone.get_current_timezone())

View File

@ -0,0 +1,39 @@
# authentic2 - versatile identity manager
# Copyright (C) 2010-2020 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from authentic2.models import Token
from authentic2.custom_user.models import User
from authentic2.utils import make_url
def build_url(user, duration=30):
token = Token.create('su', {'user_pk': user.pk}, duration=duration)
return make_url('su', kwargs={'uuid': token.uuid_b64url})
def resolve_token(uuid):
try:
token = Token.use('su', uuid)
except (ValueError, TypeError, Token.DoesNotExist):
return None
try:
return User.objects.get(pk=token.content['user_pk'])
except User.DoesNotExist:
return None

View File

@ -54,6 +54,7 @@ from django.template import loader
from . import (utils, app_settings, compat, decorators, constants,
models, cbv, hooks, validators)
from .utils import switch_user
from .a2_rbac.utils import get_default_ou
from .a2_rbac.models import OrganizationalUnit as OU
from .forms import (
@ -1210,8 +1211,8 @@ def notimplemented_view(request):
class SuView(View):
def get(self, request, token):
user = utils.get_su_user(token)
def get(self, request, uuid):
user = switch_user.resolve_token(uuid)
if not user:
raise Http404
return utils.simulate_authentication(request, user, 'su')