Example #1
0
def xsrf_protected(non_xsrf_protected_methods: List[str] = config.get_setting(
    'NON_XSRF_PROTECTED_METHODS')):
    """
    Decorator to validate XSRF tokens for any methods but GET, HEAD, OPTIONS.

    :param non_xsrf_protected_methods: List of methods (lowercase)
                                        defaults to list from settings (get, head, options)
    """
    def decorated(func):
        @wraps(func)
        def decorator(*args, **kwargs):

            if flask.request.method.lower() in non_xsrf_protected_methods:
                # Generate XSRF token
                token = generate_xsrf_token()
                flask.current_app.jinja_env.globals['xsrf_token'] = token

                @flask.after_this_request
                def add_cookie(response):
                    response.set_cookie('XSRF-TOKEN', token)
                    return response

                return func(*args, **kwargs)
            else:
                # Validate XSRF token
                token = flask.request.form.get('xsrf')
                if not token:
                    raise XSRFError("XSRF token required but not given.")

                validate_xsrf_token(token)
                return func(*args, **kwargs)

        return decorator

    return decorated
Example #2
0
    def add_report_to_headers(response):
        """
        Generate the Report-To header to be added to a response.

        :param response: The response our app has generated which requires headers.
        :return: The response with the required headers.
        """

        response.headers['Report-To'] = json.dumps(
            config.get_setting('REPORT_TO_HEADER'))
        return response
Example #3
0
    def get_base_task(self) -> dict:
        """
        Generate the base task body.

        This method is provided as a convenient way of overriding the
        default body if it requires more complexity than updating a
        setting. The default assumes this is being run in App Engine.

        This body is supposed to be updated later with the task payload
        and URI.

        :return: The base task body.
        """
        return config.get_setting("CLOUD_TASKS_BODY")
Example #4
0
    def setup_app_config(self, app: Flask) -> Flask:
        """
        Setup the configuration for the Flask app.

        This method is meant to be overridden in the case
        that a Flask app needs extra configuration.

        By default it sets the app Secret Key.

        :param Flask app: The Flask app that requires configuring.
        :return: The configured Flask app.
        :rtype: Flask
        """
        app.secret_key = config.get_setting('SECRET_KEY')
        return app
Example #5
0
    def get_task_queue(self):
        """
        Generate a queue object to create tasks with.

        This is meant as an easy way to override the method for generating
        a queue object for which we make tasks with.
        """

        try:
            queue_settings = config.get_setting(self.QUEUE_SETTINGS_NAME)
        except AttributeError:
            raise AttributeError(
                f"{self.QUEUE_SETTINGS_NAME} setting not defined. "
                f"For {self.__module__}.{self.__name__} to work the "
                f"{self.QUEUE_SETTINGS_NAME} setting must be defined.")

        return TASK_CLIENT.queue_path(**queue_settings)
Example #6
0
    def add_csp_headers(response):
        """
        Generate CSP Headers to be added to a response.

        The CSP headers are generated by inspecting the CSP_CONFIG object
        in the settings module.

        :param response: The response our app has generated which requires headers.
        :return: The response with the required headers.
        """

        csp_headers = '; '.join(
            f'{key} {value}'
            for key, value in config.get_setting('CSP_CONFIG').items())
        response.headers['Content-Security-Policy'] = csp_headers

        return response
Example #7
0
def validate_xsrf_token(token):
    """
    Validate a token against the current session token

    :param token: Token to validate (should be serialized)
    :return True/False
    """
    serializer = URLSafeTimedSerializer(flask.current_app.secret_key)
    try:
        token = serializer.loads(token,
                                 max_age=config.get_setting('XSRF_TIME_LIMIT'))
    except SignatureExpired:
        raise XSRFError("XSRF token has expired.")
    except BadData:
        raise XSRFError("XSRF token is invalid.")

    if not hmac.compare_digest(flask.session['xsrf_token'], token):
        raise XSRFError("XSRF token does not match server token.")

    return True
Example #8
0
 def login():
     return render_template(template,
                            nonce=get_setting('CSP_NONCE'),
                            client_id=client_id)
Example #9
0
 def get_client_id(self) -> str:
     """
     Overrideable method to make customising the client_id
     easier to do.
     """
     return get_setting(self._CLIENT_ID_SETTING)
Example #10
0
 def get_template_folder(self) -> str:
     """
     Overrideable method to make customising the template folder
     easier to do.
     """
     return get_setting(self._AUTH_TEMPLATE_FOLDER_SETTING)
Example #11
0
import inspect
from importlib import import_module

from secure_scaffold.config import get_setting


try:
    _DATABASE_SETTINGS = get_setting('DATABASE_SETTINGS')
except AttributeError:
    raise AttributeError('DATABASE_SETTINGS setting not defined. '
                         'For the database system to work the '
                         'DATABASE_SETTINGS setting must be defined.')


_ENGINE_KEY = _DATABASE_SETTINGS.get('engine')
if not _ENGINE_KEY:

    raise KeyError('DATABASE_SETTINGS improperly defined. '
                   'Requires an engine value.')
_ENGINE_MODULE = import_module(_DATABASE_SETTINGS['engine'])


_ENGINE_SETTINGS = _DATABASE_SETTINGS.get('settings')
if not _ENGINE_SETTINGS:
    raise KeyError('DATABASE_SETTINGS improperly defined. '
                   'Requires a settings value.')
ENGINE = _ENGINE_MODULE.Engine(**_DATABASE_SETTINGS['settings'])


class NotUniqueError(Exception):
    pass