<commit_before>from app import app
from app.exceptions import ValidationException, AuthorizationException, \
    BusinessRuleException
from app.service import user_service

from flask import session, request
from flask_login import current_user

from functools import wraps
from collections import namedtuple
from urllib.parse import urlparse
from datetime import datetime as dt, timedelta

from onelogin.saml2.auth import OneLogin_Saml2_Auth
from onelogin.saml2.utils import OneLogin_Saml2_Utils


SAMLInfo = namedtuple('SAMLInfo', ['auth', 'req'])


# Session keys

SESSION_SAML_DATA = 'saml_data'
SESSION_SAML_SIGN_UP_SESSION = 'saml_sign_up_session'

# SAML data dict keys

SAML_DATA_IS_AUTHENTICATED = 'is_authenticated'
SAML_DATA_ATTRIBUTES = 'attributes'
SAML_DATA_NAMEID = 'nameid'
SAML_DATA_REDIRECT_URL = 'redirect_url'
SAML_DATA_LINKING_USER_ID = 'linking_user_id'

# SAML sign up session dict keys and timeout

SIGN_UP_SESSION_TIME_TOUCHED = 'time_touched'
SIGN_UP_SESSION_LINKING_STUDENT_ID = 'linking_student_id'
SIGN_UP_SESSION_TIMEOUT = timedelta(minutes=5)

# SAML attribute URNs.
# See this wiki page for a more detailed description of the attributes:
# https://wiki.surfnet.nl/display/surfconextdev/Attributes

# Examples: '12345678' (in case of a student), 'jdoe1' (in case of an employee)
ATTR_URN_UID = 'urn:mace:dir:attribute-def:uid'

# Example: ['student', 'member', 'affiliate']
ATTR_URN_PERSON_AFFILIATION = 'urn:mace:dir:attribute-def:eduPersonAffiliation'

# Examples: 'John', '<NAME>'
ATTR_URN_GIVEN_NAME = 'urn:mace:dir:attribute-def:givenName'

# Example: 'Doe', '<NAME>'
ATTR_URN_SURNAME = 'urn:mace:dir:attribute-def:sn'

# Examples: '<EMAIL>', '<EMAIL>'
ATTR_URN_MAIL = 'urn:mace:dir:attribute-def:mail'

# Examples: 'nl', 'en'
ATTR_URN_PREFERRED_LANGUAGE = 'urn:mace:dir:attribute-def:preferredLanguage'


def _requires_saml_data(f):
    @wraps(f)
    def wrapper(*args, **kwargs):
        if SESSION_SAML_DATA not in session:
            session[SESSION_SAML_DATA] = {}

        try:
            return f(*args, **kwargs)
        finally:
            # Explicitly tell the session that it has been modified,
            # since just adding/removing an item in the session['saml_data']
            # dict is not picked up as no items in the session dict itself
            # are changed. See:
            # http://flask.pocoo.org/docs/1.0/api/?highlight=session#flask.session
            session.modified = True

    return wrapper


def _requires_active_sign_up_session(f):
    @wraps(f)
    def wrapper(*args, **kwargs):
        if not sign_up_session_active():
            raise ValidationException('No SAML sign up session active.')

        try:
            return f(*args, **kwargs)
        finally:
            session.modified = True

    return wrapper


def ensure_data_cleared(f):
    @wraps(f)
    def wrapper(*args, **kwargs):
        try:
            return f(*args, **kwargs)
        finally:
            clear_saml_data()

    return wrapper


def build_saml_info():
    url_data = urlparse(request.url)

    saml_req = {
        'http_host': request.host,
        'server_port': url_data.port,
        'script_name': request.path,
        'get_data': request.args.copy(),
        'post_data': request.form.copy(),
        'https': 'on'
    }

    saml_path = app.config['SAML_PATH']
    saml_auth = OneLogin_Saml2_Auth(saml_req, custom_base_path=saml_path)

    return SAMLInfo(saml_auth, saml_req)


@_requires_saml_data
def process_response(saml_info):
    saml_auth = saml_info.auth

    saml_auth.process_response()
    errors = saml_auth.get_errors()

    if errors:
        raise ValidationException(<commit_msg>Add explanation of saml directory<commit_after>from app import app
from app.exceptions import ValidationException, AuthorizationException, \
    BusinessRuleException
from app.service import user_service

from flask import session, request
from flask_login import current_user

from functools import wraps
from collections import namedtuple
from urllib.parse import urlparse
from datetime import datetime as dt, timedelta

from onelogin.saml2.auth import OneLogin_Saml2_Auth
from onelogin.saml2.utils import OneLogin_Saml2_Utils


SAMLInfo = namedtuple('SAMLInfo', ['auth', 'req'])


# Session keys

SESSION_SAML_DATA = 'saml_data'
SESSION_SAML_SIGN_UP_SESSION = 'saml_sign_up_session'

# SAML data dict keys

SAML_DATA_IS_AUTHENTICATED = 'is_authenticated'
SAML_DATA_ATTRIBUTES = 'attributes'
SAML_DATA_NAMEID = 'nameid'
SAML_DATA_REDIRECT_URL = 'redirect_url'
SAML_DATA_LINKING_USER_ID = 'linking_user_id'

# SAML sign up session dict keys and timeout

SIGN_UP_SESSION_TIME_TOUCHED = 'time_touched'
SIGN_UP_SESSION_LINKING_STUDENT_ID = 'linking_student_id'
SIGN_UP_SESSION_TIMEOUT = timedelta(minutes=5)

# SAML attribute URNs.
# See this wiki page for a more detailed description of the attributes:
# https://wiki.surfnet.nl/display/surfconextdev/Attributes

# Examples: '12345678' (in case of a student), 'jdoe1' (in case of an employee)
ATTR_URN_UID = 'urn:mace:dir:attribute-def:uid'

# Example: ['student', 'member', 'affiliate']
ATTR_URN_PERSON_AFFILIATION = 'urn:mace:dir:attribute-def:eduPersonAffiliation'

# Examples: 'John', '<NAME>'
ATTR_URN_GIVEN_NAME = 'urn:mace:dir:attribute-def:givenName'

# Example: 'Doe', '<NAME>'
ATTR_URN_SURNAME = 'urn:mace:dir:attribute-def:sn'

# Examples: '<EMAIL>', '<EMAIL>'
ATTR_URN_MAIL = 'urn:mace:dir:attribute-def:mail'

# Examples: 'nl', 'en'
ATTR_URN_PREFERRED_LANGUAGE = 'urn:mace:dir:attribute-def:preferredLanguage'


def _requires_saml_data(f):
    @wraps(f)
    def wrapper(*args, **kwargs):
        if SESSION_SAML_DATA not in session:
            session[SESSION_SAML_DATA] = {}

        try:
            return f(*args, **kwargs)
        finally:
            # Explicitly tell the session that it has been modified,
            # since just adding/removing an item in the session['saml_data']
            # dict is not picked up as no items in the session dict itself
            # are changed. See:
            # http://flask.pocoo.org/docs/1.0/api/?highlight=session#flask.session
            session.modified = True

    return wrapper


def _requires_active_sign_up_session(f):
    @wraps(f)
    def wrapper(*args, **kwargs):
        if not sign_up_session_active():
            raise ValidationException('No SAML sign up session active.')

        try:
            return f(*args, **kwargs)
        finally:
            session.modified = True

    return wrapper


def ensure_data_cleared(f):
    @wraps(f)
    def wrapper(*args, **kwargs):
        try:
            return f(*args, **kwargs)
        finally:
            clear_saml_data()

    return wrapper


def build_saml_info():
    url_data = urlparse(request.url)

    saml_req = {
        'http_host': request.host,
        'server_port': url_data.port,
        'script_name': request.path,
        'get_data': request.args.copy(),
        'post_data': request.form.copy(),
        'https': 'on'
    }

    # This is the base path for all SAML config files and certificates
    # It is equal to "saml/{develop,master}" and is structured as follows:
    # saml/{develop,master}/
    #   settings.json:              General settings
    #   advanced_settings.json:     Some extra settings
    #   certs/sp.{key,crt}          Certificate and private key

    saml_path = app.config['SAML_PATH']
    saml_auth = OneLogin_Saml2_Auth(saml_req, custom_base_path=saml_path)

    return SAMLInfo(saml_auth, saml_req)


@_requires_saml_data
def process_response(saml_info):
    saml_auth = saml_info.auth

    saml_auth.process_response()
    errors = saml_auth.get_errors()

    if errors:
        raise ValidationException(
{'fraction_non_alphanumeric': 0.09869018285566074, 'fraction_numerical': 0.0044092854363895735, 'mean_word_length': 3.6967113276492083, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 11, 'https://': 2, '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': '3486591', 'n_tokens_mistral': 2642, 'n_tokens_neox': 2546, 'n_words': 590}