<commit_before>#
# Copyright (c) 2013 NORDUnet A/S
# Copyright 2012 <NAME>. All rights reserved.
# All rights reserved.
#
# See the file eduid-IdP/LICENSE.txt for license statement.
#
# Author : <NAME> <<EMAIL>>
#          <NAME>
#

"""
Miscellaneous HTTP related functions.
"""

import pprint
from dataclasses import dataclass
from typing import Any, Dict, List, Mapping, Optional, Sequence, Tuple, Union

from flask import Response as FlaskResponse
from flask import make_response, redirect, request
from saml2 import BINDING_HTTP_REDIRECT
from werkzeug.exceptions import BadRequest, InternalServerError
from werkzeug.wrappers import Response as WerkzeugResponse

from eduid_common.api.sanitation import SanitationProblem, Sanitizer

from eduid_webapp.idp.app import current_idp_app as current_app
from eduid_webapp.idp.settings.common import IdPConfig


@dataclass
class HttpArgs:
    """ Dataclass to remove the ambiguities of pysaml2:s apply_binding() return value """

    method: str
    url: str
    headers: Sequence[Tuple[str, str]]
    body: Optional[str]

    @classmethod
    def from_pysaml2_dict(cls: Type[HttpArgs], http_args: Dict[str, Any]) -> HttpArgs:
        # Parse the parts of http_args we know how to parse, and then warn about any remains.
        if 'status' in http_args:
            current_app.logger.warning(f'Ignoring status in http_args: {http_args["status"]}')
        method = http_args.pop('method')
        url = http_args.pop('url')
        message = http_args.pop('data')
        status = http_args.pop('status', '200 Ok')
        headers = http_args.pop('headers', [])
        headers_lc = [x[0].lower() for x in headers]
        if 'content-type' not in headers_lc:
            _content_type = http_args.pop('content', 'text/html')
            headers.append(('Content-Type', _content_type))

        if http_args != {}:
            current_app.logger.debug(
                f'Unknown HTTP args when creating {status!r} response :\n{pprint.pformat(http_args)!s}'
            )

        return cls(method=method, url=url, headers=headers, body=message)

    @property
    def redirect_url(self) -> Optional[str]:
        """
        Get the destination URL for a redirect.

        Use the header Location first, and secondly 'url' from http_args.
        """
        for k, v in self.headers:
            if k.lower() == 'location':
                return v
        return self.url


def create_html_response(binding: str, http_args: Dict[str, Union[str, List[Tuple[str, str]]]]) -> WerkzeugResponse:
    """
    Create a HTML response based on parameters compiled by pysaml2 functions
    like apply_binding().

    :param binding: SAML binding
    :param http_args: response data
    :return: HTML response
    """
    args = HttpArgs.from_pysaml2_dict(http_args)
    if binding == BINDING_HTTP_REDIRECT:
        if args.method != 'GET':
            current_app.logger.warning(f'BINDING_HTTP_REDIRECT method is not GET ({args.method})')
        location = args.redirect_url
        current_app.logger.debug(f'Binding {binding} redirecting to {location!r}')
        if args.url:
            if not location.startswith(args.url):
                current_app.logger.warning(f'There is another "url" in args: {args.url} (location: {location})')
        if not location:
            raise InternalServerError('No redirect destination')
        return redirect(location)
<commit_msg>check that location is set before using it<commit_after>#
# Copyright (c) 2013 NORDUnet A/S
# Copyright 2012 <NAME>. All rights reserved.
# All rights reserved.
#
# See the file eduid-IdP/LICENSE.txt for license statement.
#
# Author : <NAME> <<EMAIL>>
#          <NAME>
#

"""
Miscellaneous HTTP related functions.
"""

import pprint
from dataclasses import dataclass
from typing import Any, Dict, List, Mapping, Optional, Sequence, Tuple, Union

from flask import Response as FlaskResponse
from flask import make_response, redirect, request
from saml2 import BINDING_HTTP_REDIRECT
from werkzeug.exceptions import BadRequest, InternalServerError
from werkzeug.wrappers import Response as WerkzeugResponse

from eduid_common.api.sanitation import SanitationProblem, Sanitizer

from eduid_webapp.idp.app import current_idp_app as current_app
from eduid_webapp.idp.settings.common import IdPConfig


@dataclass
class HttpArgs:
    """ Dataclass to remove the ambiguities of pysaml2:s apply_binding() return value """

    method: str
    url: str
    headers: Sequence[Tuple[str, str]]
    body: Optional[str]

    @classmethod
    def from_pysaml2_dict(cls: Type[HttpArgs], http_args: Dict[str, Any]) -> HttpArgs:
        # Parse the parts of http_args we know how to parse, and then warn about any remains.
        if 'status' in http_args:
            current_app.logger.warning(f'Ignoring status in http_args: {http_args["status"]}')
        method = http_args.pop('method')
        url = http_args.pop('url')
        message = http_args.pop('data')
        status = http_args.pop('status', '200 Ok')
        headers = http_args.pop('headers', [])
        headers_lc = [x[0].lower() for x in headers]
        if 'content-type' not in headers_lc:
            _content_type = http_args.pop('content', 'text/html')
            headers.append(('Content-Type', _content_type))

        if http_args != {}:
            current_app.logger.debug(
                f'Unknown HTTP args when creating {status!r} response :\n{pprint.pformat(http_args)!s}'
            )

        return cls(method=method, url=url, headers=headers, body=message)

    @property
    def redirect_url(self) -> Optional[str]:
        """
        Get the destination URL for a redirect.

        Use the header Location first, and secondly 'url' from http_args.
        """
        for k, v in self.headers:
            if k.lower() == 'location':
                return v
        return self.url


def create_html_response(binding: str, http_args: Dict[str, Union[str, List[Tuple[str, str]]]]) -> WerkzeugResponse:
    """
    Create a HTML response based on parameters compiled by pysaml2 functions
    like apply_binding().

    :param binding: SAML binding
    :param http_args: response data
    :return: HTML response
    """
    args = HttpArgs.from_pysaml2_dict(http_args)
    if binding == BINDING_HTTP_REDIRECT:
        if args.method != 'GET':
            current_app.logger.warning(f'BINDING_HTTP_REDIRECT method is not GET ({args.method})')
        location = args.redirect_url
        current_app.logger.debug(f'Binding {binding} redirecting to {location!r}')
        if not location:
            raise InternalServerError('No redirect destination')
        if args.url:
            if not location.startswith(args.url):
                current_app.logger.warning(f'There is another "url" in args: {args.url} (location: {location})')
        return redirect(location)

{'fraction_non_alphanumeric': 0.10103362934925025, 'fraction_numerical': 0.004949774348522347, 'mean_word_length': 3.1088516746411483, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1189268', 'n_tokens_mistral': 2072, 'n_tokens_neox': 2031, 'n_words': 603}