Commit 1af577d6 authored by Grant's avatar Grant
Browse files

add in SAML auth

parent 6eba0104
Loading
Loading
Loading
Loading
+81 −0
Original line number Diff line number Diff line
"""Provide authentication methods."""
from urllib.parse import urlparse, urlencode
from common.env import check_environment as ce

try:
@@ -56,3 +57,83 @@ def authenticate_ldap_user(uid, password):
        return None
    connection.search(root_dn, user_search_filter, attributes=["*"])
    return connection.entries[0]


def authenticate_saml(
    scheme,
    host,
    url,
    method,
    args,
    form,
    process_success=lambda _: None,
    process_failure=lambda _: None,
    saml_path=None,
):
    """
    Handles SAML authentication by processing the necessary components extracted from a web request.

    This function manages SAML Single Sign-On (SSO), Single Log-Out (SLO), and Assertion Consumer Service (ACS)
    endpoints. It accepts individual components typically found in a web request and utilizes them to process
    SAML requests and responses, delegating outcome handling to provided callback functions.

    Parameters:
    - scheme (str): The request scheme ('http' or 'https').
    - host (str): The request host.
    - url (str): The full URL of the request.
    - method (str): The HTTP method of the request.
    - args (dict): The query parameters of the request as a dictionary.
    - form (dict): The form data of the request as a dictionary.
    - process_success (Callable): Function to call after successful authentication or logout to handle the response.
    - process_failure (Callable): Function to call when an error occurs or no relevant SAML action is found.
    - saml_path (str, optional): Custom path for SAML configuration files. If not provided, environment variable
      'SAML_PATH' is used.

    Returns:
    - Result of the `process_success` or `process_failure` callable depending on the SAML processing outcome.

    Raises:
    - Exception: Descriptive exceptions can be raised depending on the SAML processing errors or misconfigurations.

    Usage:
    This function is designed to be framework-agnostic and should be integrated into any web application by adapting
    the request handling to extract necessary components.
    Example:
    # Extract necessary components from a Flask or Django request and pass them to this function.
    result = authenticate_saml(request.scheme, request.host, request.url, request.method, request.args, request.form,
                               handle_success, handle_failure)
    """
    # Prepare request data for SAML auth
    url_data = urlparse(url)
    saml_request = {
        "https": "on" if scheme == "https" else "off",
        "http_host": host,
        "server_port": url_data.port,
        "script_name": url_data.path,
        "get_data": args,
        "post_data": form,
        "query_string": urlencode(args) if method == "GET" else "",
    }

    # Initialize SAML auth
    saml_path = saml_path or ce("SAML_PATH")
    auth = OneLogin_Saml2_Auth(saml_request, custom_base_path=saml_path)

    # SAML Action Handling
    try:
        if "sso" in args:
            return process_success(auth.login())
        elif "slo" in args:
            return process_success(auth.logout())
        elif "acs" in args:
            auth.process_response(
                request_id=None
            )  # Assuming request carries all needed info
            if auth.is_authenticated():
                return process_success(None)  # pass user details / attributes
            else:
                return process_failure("Authentication failed or errors occurred")
        else:
            return process_failure("No SAML action found")
    except Exception as e:
        return process_failure(str(e))