def test_get_service_url_correct(self): """ Ensure method behaves properly when redirect_url is or is not provided """ request = self.factory.get("/accounts/login/", data={"next": "/next-page/"}) result = get_service_url(request) expected = "http://testserver/accounts/login/?next=%2Fnext-page%2F" self.assertEqual(result, expected) result = get_service_url(request, "/alt/target/") expected = "http://testserver/accounts/login/?next=%2Falt%2Ftarget%2F" self.assertEqual(result, expected)
def test_get_service_url_query_parameters(self): """ Ensure method preserves query parameters """ request = self.factory.get("/origin/?foo=bar", secure=True) result = get_service_url(request, "/target/") expected = "https://testserver/origin/?foo=bar&next=%2Ftarget%2F" assert_urls_equivalent(result, expected, self.assertEqual) request = self.factory.get("/origin/", secure=True, data={"foo": "bar", "next": "/next-page/?cat=dog"}) result = get_service_url(request) expected = ("https://testserver/origin/?foo=bar" "&next=%2Fnext-page%2F%3Fcat%3Ddog") assert_urls_equivalent(result, expected, self.assertEqual)
def link_from_profile(request, institution): """ Attempts to authenticate a CAS account for the provided institution, and links it to the current Uniauth profile if successful. """ next_url = request.GET.get('next') ticket = request.GET.get('ticket') # Ensure there is an institution with the provided slug try: institution = Institution.objects.get(slug=institution) except Institution.DoesNotExist: raise Http404 if not next_url: next_url = get_redirect_url(request, use_referer=True) # If the user is not already logged into a verified # Uniauth account, raise permission denied if not request.user.is_authenticated or is_tmp_user(request.user) \ or is_unlinked_account(request.user): raise PermissionDenied("Must be logged in as verified Uniauth user.") service_url = get_service_url(request, next_url) client = CASClient(version=2, service_url=service_url, server_url=institution.cas_server_url) # If a ticket was provided, attempt to authenticate with it if ticket: user = authenticate(request=request, institution=institution, ticket=ticket, service=service_url) # Authentication successful: link to Uniauth profile if # the institution account has not been linked yet + proceed if user: if is_unlinked_account(user): merge_model_instances(request.user, [user]) username_split = get_account_username_split(user.username) _add_institution_account(request.user.profile, username_split[1], username_split[2]) return HttpResponseRedirect(next_url) # Authentication failed: raise permission denied else: raise PermissionDenied("Verification of CAS ticket failed") # If no ticket was provided, redirect to the # login URL for the institution's CAS server else: return HttpResponseRedirect(client.get_login_url())
def test_get_service_url_ignore_ticket(self): """ Ensures ticket is ignored if present as query parameter """ request = self.factory.get("/origin/", secure=True, data={"foo": "bar", "next": "/next-page/?cat=dog", "ticket": "FAKE-ticket-456"}) result = get_service_url(request) expected = ("https://testserver/origin/?foo=bar" "&next=%2Fnext-page%2F%3Fcat%3Ddog") assert_urls_equivalent(result, expected, self.assertEqual)
def cas_login(request, institution): """ Redirects to the CAS login URL, or verifies the CAS ticket, if provided. Accepts the slug of the institution to log in to. """ next_url = request.GET.get('next') ticket = request.GET.get('ticket') # Ensure there is an institution with the provided slug try: institution = Institution.objects.get(slug=institution) except Institution.DoesNotExist: raise Http404 if not next_url: next_url = get_redirect_url(request, use_referer=True) # If the user is already authenticated, proceed to next page if request.user.is_authenticated: return _login_success(request, request.user, next_url) service_url = get_service_url(request, next_url) client = CASClient(version=2, service_url=service_url, server_url=institution.cas_server_url) # If a ticket was provided, attempt to authenticate with it if ticket: user = authenticate(request=request, institution=institution, ticket=ticket, service=service_url) # Authentication successful: setup session + proceed if user: if not request.session.exists(request.session.session_key): request.session.create() auth_login(request, user) request.session['auth-method'] = "cas-" + institution.slug return _login_success(request, user, next_url, ["ticket"]) # Authentication failed: raise permission denied else: raise PermissionDenied("Verification of CAS ticket failed.") # If no ticket was provided, redirect to the # login URL for the institution's CAS server else: return HttpResponseRedirect(client.get_login_url())
def logout(request): """ Logs the user out of their Uniauth account, and redirects to the next page, defaulting to the URL specified by the UNIAUTH_LOGOUT_REDIRECT_URL setting. If no redirect page is set (URL parameter not given and UNIAUTH_LOGOUT_REDIRECT_URL is None), renders the logout template. Also logs the user out of CAS if they logged in via CAS, and the UNIAUTH_LOGOUT_CAS_COMPLETELY setting is true. """ next_page = request.GET.get('next') auth_method = request.session.get('auth-method') if not next_page and get_setting('UNIAUTH_LOGOUT_REDIRECT_URL'): next_page = get_redirect_url( request, get_setting('UNIAUTH_LOGOUT_REDIRECT_URL')) # Formally log out user auth_logout(request) # Determine whether the user logged in through an institution's CAS institution = None if auth_method and auth_method.startswith("cas-"): try: institution = Institution.objects.get(slug=auth_method[4:]) except Institution.DoesNotExist: pass # If we need to logout an institution's CAS, # redirect to that CAS server's logout URL if institution and get_setting('UNIAUTH_LOGOUT_CAS_COMPLETELY'): redirect_url = urlunparse( (get_protocol(request), request.get_host(), next_page or reverse('uniauth:logout'), '', '', '')) client = CASClient(version=2, service_url=get_service_url(request), server_url=institution.cas_server_url) return HttpResponseRedirect(client.get_logout_url(redirect_url)) # If next page is set, proceed to it elif next_page: return HttpResponseRedirect(next_page) # Otherwise, render the logout view else: return render(request, 'uniauth/logout.html')