diff --git a/course/auth.py b/course/auth.py index dd264f00c..5ebfeaff6 100644 --- a/course/auth.py +++ b/course/auth.py @@ -59,8 +59,9 @@ user_status, participation_status, participation_permission as pperm, + COURSE_ID_REGEX, ) -from course.models import Participation, ParticipationRole, AuthenticationToken # noqa +from course.models import Participation, ParticipationRole, AuthenticationToken, Course # noqa from accounts.models import User from course.utils import render_course_page, course_view @@ -82,14 +83,13 @@ def get_pre_impersonation_user(request): return None -def get_impersonable_user_qset(impersonator): +def get_impersonable_user_qset(impersonator, course_identifier): # type: (User) -> query.QuerySet - if impersonator.is_superuser: - return User.objects.exclude(pk=impersonator.pk) my_participations = Participation.objects.filter( user=impersonator, - status=participation_status.active) + status=participation_status.active, + course__identifier=course_identifier) impersonable_user_qset = User.objects.none() for part in my_participations: @@ -120,6 +120,14 @@ def get_impersonable_user_qset(impersonator): return impersonable_user_qset +def _get_current_course_from_request(request): + course_match = re.match("^/course/"+COURSE_ID_REGEX+"/", request.get_full_path()) + if course_match is None: + return None + else: + return course_match.group("course_identifier") + + class ImpersonateMiddleware(object): def __init__(self, get_response): self.get_response = get_response @@ -127,6 +135,7 @@ def __init__(self, get_response): def __call__(self, request): if 'impersonate_id' in request.session: imp_id = request.session['impersonate_id'] + cur_course_identifier = _get_current_course_from_request(request) impersonee = None try: @@ -136,17 +145,34 @@ def __call__(self, request): pass may_impersonate = False + permission_error = False if impersonee is not None: if request.user.is_superuser: may_impersonate = True else: - qset = get_impersonable_user_qset(cast(User, request.user)) - if qset.filter(pk=cast(User, impersonee).pk).count(): - may_impersonate = True + imp_course_identifier = request.session['impersonate_course_identifier'] + if cur_course_identifier is not None: + if cur_course_identifier == imp_course_identifier: + qset = get_impersonable_user_qset(cast(User, request.user), + course_identifier=imp_course_identifier) + if qset.filter(pk=cast(User, impersonee).pk).count(): + may_impersonate = True + else: + permission_error = True + else: + permission_error = True if may_impersonate: request.relate_impersonate_original_user = request.user request.user = impersonee + elif request.get_full_path() == "/user/stop_impersonating/": + pass + elif request.get_full_path().startswith("/logout/"): + pass + elif permission_error: + messages.add_message(request, messages.WARNING, + _("Permission denied to impersonate this page. Falling back to " + \ + cast(User, request.user).username)) else: messages.add_message(request, messages.ERROR, _("Error while impersonating.")) @@ -209,12 +235,17 @@ def __init__(self, *args, **kwargs): self.helper.add_input(Submit("submit", _("Impersonate"))) -def impersonate(request): +def impersonate(request, course_identifier): # type: (http.HttpRequest) -> http.HttpResponse if not request.user.is_authenticated: raise PermissionDenied() - impersonable_user_qset = get_impersonable_user_qset(cast(User, request.user)) + impersonator = cast(User, request.user) + if impersonator.is_superuser: + impersonable_user_qset = User.objects.exclude(pk=impersonator.pk) + else: + impersonable_user_qset = get_impersonable_user_qset(impersonator, + course_identifier=course_identifier) if not impersonable_user_qset.count(): raise PermissionDenied() @@ -236,6 +267,9 @@ def impersonate(request): request.session['impersonate_id'] = impersonee.id request.session['relate_impersonation_header'] = form.cleaned_data[ "add_impersonation_header"] + if course_identifier is not None: + request.session['impersonate_course_identifier'] = course_identifier + return redirect("/course/{}".format(course_identifier)) # Because we'll likely no longer have access to this page. return redirect("relate-home") @@ -260,7 +294,7 @@ def stop_impersonating(request): if "stop_impersonating" not in request.POST: raise SuspiciousOperation(_("odd POST parameters")) - if not hasattr(request, "relate_impersonate_original_user"): + if not 'impersonate_id' in request.session: # prevent user without pperm to stop_impersonating my_participations = Participation.objects.filter( user=request.user, diff --git a/relate/templates/base.html b/relate/templates/base.html index f58bb5b36..412eca4c9 100644 --- a/relate/templates/base.html +++ b/relate/templates/base.html @@ -108,7 +108,7 @@ {% if currently_impersonating %}
  • {% trans "Stop impersonating" %}
  • {% else %} -
  • {% trans "Impersonate user" %}
  • +
  • {% trans "Impersonate user" %}
  • {% endif %} {% if pperm.set_fake_time or request.relate_impersonate_original_user|may_set_fake_time %}
  • {% trans "Set fake time" %}
  • diff --git a/relate/urls.py b/relate/urls.py index 0e084ced3..f4396b303 100644 --- a/relate/urls.py +++ b/relate/urls.py @@ -99,11 +99,12 @@ name="relate-monitor_task"), # {{{ troubleshooting - - url(r'^user/impersonate/$', + url(r"^course" + "/" + COURSE_ID_REGEX + + "/impersonate/$", course.auth.impersonate, name="relate-impersonate"), - url(r'^user/stop_impersonating/$', + url(r"^user/stop_impersonating/$", course.auth.stop_impersonating, name="relate-stop_impersonating"),