summaryrefslogtreecommitdiff
path: root/kosten/app/utils.py
blob: 73f2b5155c8391f8a54eb058c19d8644f80cea7d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
from functools import wraps
from flask import flash, request, render_template, url_for
from flask import redirect as _redirect

from .login import current_user

import datetime
today = datetime.date.today

def _gen_tpl(endpoint):
    return endpoint.replace('.', '/') + '.jinja'

def templated(template=None):
    """Marks a view as being rendered by a template. The view then shall
    return a dictionary holding the parameters for the template. Ig this
    is not the case, the response is returned unchanged. This is needed
    to support `redirect` and similar.

    The correct template is deducted as:
        - when passed nothing: the name of the view
        - when passed a string '.bla', the endpoint 'bla' in the current
          blueprint
        - when passed any other string: this string (VERBATIM!)

    Except for the last case, the hierarchy of blueprint and view is taken
    as directories in the template directory. And '.jinja' is appended.

    If the first argument is a function, this is taken as 'None' to allow:
        >>> @templated
        ... def foo():
        ...    ...

    (else it would have to be ``@templated()``).
    """

    fun = None
    if template is not None and callable(template):
        # a function was passed in
        fun = template
        template = None

    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            if template is None:
                template_name = _gen_tpl(request.endpoint)
            elif template[0] == '.' and request.blueprint is not None:
                template_name = _gen_tpl(request.blueprint + template)
            else:
                template_name = template

            ctx = f(*args, **kwargs)
            if ctx is None:
                ctx = {}
            elif not isinstance(ctx, dict):
                return ctx
            return render_template(template_name, **ctx)
        return decorated_function

    if fun is None:
        return decorator
    else:
        return decorator(fun)

def redirect (target, **kwargs):
    """Convenience wrapper for `flask.redirect`. It applies `url_for`
    on the target, which also gets passed all arguments.

    Special argument '_code' to set the HTTP-Code.
    """
    code = kwargs.pop('_code', None)
    url = url_for(target, **kwargs)

    if code is None:
        return _redirect(url)
    else:
        return _redirect(url, code)

def assert_authorisation(constructor, param):
    """Asserts that the current user has the right to load some specific data.

    This is done by using the argument with keyword `param` and pass it
    to `constructor`. If the resulting object has an attribute `user_id`,
    this is checked to be equal to `current_user.id`.

    Usage example::

        @route('/job/<int:id>')
        @assert_authorisation(Job, 'id')
        def show_job(id):
            # this is only executed if Job(id).user_id == current_user.id

    """
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            p = kwargs.get(param, None)

            if p is None:
                raise TypeError("Keyword %s expected but not received." % param)

            obj = constructor(p)
            if obj is None:
                flash("Eintrag existiert nicht!", 'error')
                return redirect('index')

            if not hasattr(obj, 'user_id'):
                return f(*args, **kwargs)

            # explicitly use user_id to avoid having to load the user object
            if obj.user_id != current_user.id:
                flash("Nicht erlaubte Operation!", 'error')
                return redirect('index')
            else:
                return f(*args, **kwargs)
        return decorated_function
    return decorator