Example #1
0
def test_create_jwt_token_no_secret_no_algorithm(settings: SettingsWrapper):
    """Test create_jwt_token function by trying to create a JWT token with no secret and algorithm
    set.

    Args:
        settings (SettingsWrapper): Fixture for django settings
    """
    settings.SAML2_AUTH = {"JWT_SECRET": None, "JWT_ALGORITHM": None}

    with pytest.raises(SAMLAuthError) as exc_info:
        create_jwt_token("*****@*****.**")

    assert str(exc_info.value
               ) == "Cannot create JWT token. Specify secret and algorithm."
Example #2
0
def test_decode_jwt_token_success():
    """Test decode_jwt_token function by verifying if the newly created JWT token using
    create_jwt_token function is valid."""
    jwt_token = create_jwt_token("*****@*****.**")
    user_id = decode_jwt_token(jwt_token)

    assert user_id == "*****@*****.**"
Example #3
0
def sp_initiated_login(request: HttpRequest) -> HttpResponseRedirect:
    # User must be created first by the IdP-initiated SSO (acs)
    if request.method == "GET":
        if request.GET.get("token"):
            user_id = decode_jwt_token(request.GET.get("token"))
            saml_client = get_saml_client(get_assertion_url(request), acs,
                                          user_id)
            jwt_token = create_jwt_token(user_id)
            _, info = saml_client.prepare_for_authenticate(
                sign=False, relay_state=jwt_token)
            redirect_url = dict(info["headers"]).get("Location", "")
            if not redirect_url:
                return HttpResponseRedirect(
                    get_reverse([denied, "denied",
                                 "django_saml2_auth:denied"]))
            return HttpResponseRedirect(redirect_url)
    else:
        raise SAMLAuthError("Request method is not supported.",
                            extra={
                                "exc_type": Exception,
                                "error_code": INVALID_REQUEST_METHOD,
                                "reason": "Request method is not supported.",
                                "status_code": 404
                            })
Example #4
0
def acs(request: HttpRequest):
    """Assertion Consumer Service is SAML terminology for the location at a ServiceProvider that
    accepts <samlp:Response> messages (or SAML artifacts) for the purpose of establishing a session
    based on an assertion. Assertion is a signed authentication request from identity provider (IdP)
    to acs endpoint.

    Args:
        request (HttpRequest): Incoming request from identity provider (IdP) for authentication

    Exceptions:
        SAMLAuthError: The target user is inactive.

    Returns:
        HttpResponseRedirect: Redirect to various endpoints: denied, welcome or next_url (e.g.
            the front-end app)

    Notes:
        https://wiki.shibboleth.net/confluence/display/CONCEPT/AssertionConsumerService
    """
    authn_response = decode_saml_response(request, acs)
    user = extract_user_identity(authn_response.get_identity())

    next_url = request.session.get("login_next_url") or get_default_next_url()

    # If RelayState params is passed, it is a JWT token that identifies the user trying to login
    # via sp_initiated_login endpoint
    relay_state = request.POST.get("RelayState")
    if relay_state:
        redirected_user_id = decode_jwt_token(relay_state)

        # This prevents users from entering an email on the SP, but use a different email on IdP
        if get_user_id(user) != redirected_user_id:
            raise SAMLAuthError("The user identifier doesn't match.",
                                extra={
                                    "exc_type": ValueError,
                                    "error_code": USER_MISMATCH,
                                    "reason": "User identifier mismatch.",
                                    "status_code": 403
                                })

    is_new_user, target_user = get_or_create_user(user)

    before_login_trigger = dictor(settings.SAML2_AUTH, "TRIGGER.BEFORE_LOGIN")
    if before_login_trigger:
        run_hook(before_login_trigger, user)

    request.session.flush()

    use_jwt = settings.SAML2_AUTH.get("USE_JWT", False)
    if use_jwt and target_user.is_active:
        # Create a new JWT token for IdP-initiated login (acs)
        jwt_token = create_jwt_token(target_user.email)
        # Use JWT auth to send token to frontend
        query = f"?token={jwt_token}"

        frontend_url = settings.SAML2_AUTH.get("FRONTEND_URL", next_url)

        return HttpResponseRedirect(frontend_url + query)

    if target_user.is_active:
        model_backend = "django.contrib.auth.backends.ModelBackend"
        login(request, target_user, model_backend)

        after_login_trigger = dictor(settings.SAML2_AUTH,
                                     "TRIGGER.AFTER_LOGIN")
        if after_login_trigger:
            run_hook(after_login_trigger, request.session, user)
    else:
        raise SAMLAuthError("The target user is inactive.",
                            extra={
                                "exc_type": Exception,
                                "error_code": INACTIVE_USER,
                                "reason": "User is inactive.",
                                "status_code": 500
                            })

    if is_new_user:
        try:
            return render(request, "django_saml2_auth/welcome.html",
                          {"user": request.user})
        except TemplateDoesNotExist:
            return HttpResponseRedirect(next_url)
    else:
        return HttpResponseRedirect(next_url)
Example #5
0
def test_create_jwt_token_success():
    """Test create_jwt_token function to verify JWT token generation."""
    jwt_token = create_jwt_token("*****@*****.**")
    assert isinstance(jwt_token, str)
    assert "." in jwt_token
    assert jwt_token.count(".") == 2