Beispiel #1
0
    def test_get_redirect_url_defaults(self):
        """
        Ensure method uses the provided default URL or default
        login URL setting if no other options are available
        """
        request = self.factory.get("/accounts/login/", HTTP_REFERER="/home")
        result = get_redirect_url(request, use_referer=False,
                default_url="/default/")
        self.assertEqual(result, "/default/")

        request = self.factory.get("/accounts/login/", HTTP_REFERER="/home")
        result = get_redirect_url(request)
        default_login = DEFAULT_SETTING_VALUES["UNIAUTH_LOGIN_REDIRECT_URL"]
        self.assertEqual(result, default_login)
Beispiel #2
0
def signup(request):
    """
    Creates a new Uniauth profile with the provided
    primary email address and password.

    Prompts user to verify the email address before
    profile is fully created.
    """
    next_url = request.GET.get('next')
    context = _get_global_context(request)

    if not next_url:
        next_url = get_redirect_url(request)

    # If the user is already authenticated + has a Uniauth
    # profile, proceed to next page
    if request.user.is_authenticated and not is_tmp_user(request.user) \
            and not is_unlinked_account(request.user):
        return HttpResponseRedirect(next_url)

    # If it's a POST request, attempt to validate the form
    if request.method == "POST":
        form = SignupForm(request.POST)

        # Validation successful: setup temporary user
        if form.is_valid():
            form_email = form.cleaned_data['email']
            user = request.user

            # If the user is not already authenticated, create a User
            if not user or not user.is_authenticated:
                tmp_username = get_random_username()
                user_model = get_user_model()
                user = user_model.objects.create(username=tmp_username)

            # Set user's password + create linked email
            user.set_password(form.cleaned_data["password1"])
            user.save()
            email, _ = LinkedEmail.objects.get_or_create(
                profile=user.uniauth_profile,
                address=form_email,
                is_verified=False)

            # Send verification email + render waiting template
            _send_verification_email(request, email.address, email)
            return render(request, 'uniauth/verification-waiting.html', {
                'email': email.address,
                'is_signup': True
            })

        # Validation failed: render form errors
        else:
            context['form'] = form
            return render(request, 'uniauth/signup.html', context)

    # Otherwise, render a blank Signup form
    else:
        form = SignupForm()
        context['form'] = form
        return render(request, 'uniauth/signup.html', context)
 def test_get_redirect_url_query_parameter(self):
     """
     Ensure method returns URL provided as query parameter
     over everything else if present
     """
     request = self.factory.get("/accounts/login/",
                                data={"next": "/next-page/"})
     result = get_redirect_url(request)
     self.assertEqual(result, "/next-page/")
Beispiel #4
0
 def test_get_redirect_url_referer(self):
     """
     Ensure method returns the referring page if use_referer
     is True, and no relevant query parameter was provided
     """
     request = self.factory.get("/accounts/cas-login/test-inst/",
             HTTP_REFERER="/home/")
     result = get_redirect_url(request, use_referer=True,
             default_url="/default/")
     self.assertEqual(result, "/home/")
Beispiel #5
0
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())
Beispiel #6
0
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())
Beispiel #7
0
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')
Beispiel #8
0
def link_to_profile(request):
    """
    If the user is a temporary one who was logged in via
    an institution (not through a Uniauth profile), offers
    them the choice between logging to an existing Uniauth
    account or creating a new one.

    The institution account is (eventually) linked to the
    Uniauth profile the user logged into / created.
    """
    next_url = request.GET.get('next')
    context = _get_global_context(request)

    if not next_url:
        next_url = get_redirect_url(request)

    params = urlencode({'next': next_url})
    context['next_url'] = next_url

    # If the user is not authenticated at all, redirect to login page
    if not request.user.is_authenticated:
        return HttpResponseRedirect(reverse('uniauth:login') + '?' + params)

    # If the user is already authenticated + verified, proceed to next page
    if not is_tmp_user(request.user) and not is_unlinked_account(request.user):
        return HttpResponseRedirect(next_url)

    # If the user is temporary, but was not logged in via an institution
    # (e.g. created through Uniauth, but not verified), redirect to signup
    if not is_unlinked_account(request.user):
        return HttpResponseRedirect(reverse('uniauth:signup') + '?' + params)

    # At this point, we've ensured the user is temporary and was
    # logged in via an institution. We just need to handle the
    # Login Form, if the user chooses to link to an existing account.

    # If it's a POST request, attempt to validate the form
    if request.method == "POST":
        form = LoginForm(request, request.POST)

        # Authentication successful
        if form.is_valid():
            unlinked_user = request.user
            username_split = get_account_username_split(request.user.username)

            # Log in as the authenticated Uniauth user
            user = form.get_user()
            auth_login(request, user)

            # Merge the unlinked account into the logged in profile,
            # then add the institution account described by the username
            merge_model_instances(user, [unlinked_user])
            _add_institution_account(user.profile, username_split[1],
                                     username_split[2])

            slug = username_split[1]
            context['institution'] = Institution.objects.get(slug=slug)
            return render(request, 'uniauth/link-success.html', context)

        # Authentication failed: render form errors
        else:
            context['form'] = form
            return render(request, 'uniauth/link-to-profile.html', context)

    # Otherwise, render a blank Login form
    else:
        form = LoginForm(request)
        context['form'] = form
        return render(request, 'uniauth/link-to-profile.html', context)
Beispiel #9
0
def login(request):
    """
    Authenticates the user, then redirects them to the
    next page, defaulting to the URL specified by the
    UNIAUTH_LOGIN_REDIRECT_URL setting.

    Offers users the choice between logging in with their
    Uniauth credentials, or through the CAS interface for
    any supported institution.
    """
    next_url = request.GET.get('next')
    context = _get_global_context(request)

    if not next_url:
        next_url = get_redirect_url(request)

    # If the user is already authenticated, proceed to next page
    if request.user.is_authenticated:
        return _login_success(request, request.user, next_url)

    display_standard = get_setting('UNIAUTH_LOGIN_DISPLAY_STANDARD')
    display_cas = get_setting('UNIAUTH_LOGIN_DISPLAY_CAS')
    num_institutions = len(context['institutions'])

    # Ensure the login settings are configured correctly
    if not display_standard and not display_cas:
        err_msg = "At least one of '%s' and '%s' must be True." % \
                ('UNIAUTH_LOGIN_DISPLAY_STANDARD', 'UNIAUTH_LOGIN_DISPLAY_CAS')
        raise ImproperlyConfigured(err_msg)
    if display_cas and num_institutions == 0:
        err_msg = ("'%s' is True, but there are no Institutions in the "
                   "database to log into!") % 'UNIAUTH_LOGIN_DISPLAY_CAS'
        raise ImproperlyConfigured(err_msg)

    context['display_standard'] = display_standard
    context['display_cas'] = display_cas
    context['num_institutions'] = num_institutions

    # If we aren't displaying the standard login form,
    # we're just displaying the CAS login options
    if not display_standard:
        institutions = context['institutions']
        query_params = context['query_params']

        # If there's only one possible institution to log
        # into, redirect to its CAS login page immediately
        if num_institutions == 1:
            return HttpResponseRedirect(institutions[0][2] + query_params)

        # Otherwise, render the page (without the Login form)
        else:
            return render(request, 'uniauth/login.html', context)

    # If we are displaying the login form, and it's
    # a POST request, attempt to validate the form
    elif request.method == "POST":
        form = LoginForm(request, request.POST)

        # Authentication successful: setup session + proceed
        if form.is_valid():
            user = form.get_user()
            auth_login(request, user)
            request.session['auth-method'] = "uniauth"
            return _login_success(request, user, next_url)

        # Authentication failed: render form errors
        else:
            context['form'] = form
            return render(request, 'uniauth/login.html', context)

    # Otherwise, render a blank Login form
    else:
        form = LoginForm(request)
        context['form'] = form
        return render(request, 'uniauth/login.html', context)