Zine

open source content publishing system


source: zine/privileges.py @ 1279:088d2f519391

Revision 1279:088d2f519391, 7.7 KB checked in by Georg Brandl <georg@…>, 2 years ago (diff)

Update copyright notices.

Line 
1# -*- coding: utf-8 -*-
2"""
3    zine.privileges
4    ~~~~~~~~~~~~~~~
5
6    This module contains a list of builtin privileges.
7
8    :copyright: (c) 2010 by the Zine Team, see AUTHORS for more details.
9    :license: BSD, see LICENSE for more details.
10"""
11from werkzeug.exceptions import Forbidden
12
13from zine.database import db
14from zine.application import get_application, get_request
15from zine.i18n import lazy_gettext
16
17
18__all__ = ['DEFAULT_PRIVILEGES', 'Privilege']
19
20DEFAULT_PRIVILEGES = {}
21
22
23class _Expr(object):
24
25    def iter_privileges(self, cache=None):
26        raise NotImplementedError()
27
28    def __and__(self, other):
29        return _And(self, other)
30
31    def __or__(self, other):
32        return _Or(self, other)
33
34    def __call__(self, privileges):
35        return False
36
37
38class _Bin(_Expr):
39    joiner = 'BIN'
40
41    def __init__(self, a, b):
42        self.a = a
43        self.b = b
44
45    def iter_privileges(self, cache=None):
46        if cache is None:
47            cache = set()
48        for op in self.a, self.b:
49            for item in op.iter_privileges(cache):
50                yield item
51
52    def __repr__(self):
53        return '(%r %s %r)' % (self.a, self.joiner, self.b)
54
55
56class _And(_Bin):
57    joiner = '&'
58
59    def __call__(self, privileges):
60        return self.a(privileges) and self.b(privileges)
61
62
63class _Or(_Bin):
64    joiner = '|'
65
66    def __call__(self, privileges):
67        return self.a(privileges) or self.b(privileges)
68
69
70class _Privilege(object):
71    """Internal throw-away class used for the association proxy."""
72
73    def __init__(self, name):
74        self.name = name
75
76    @property
77    def privilege(self):
78        return get_application().privileges.get(self.name)
79
80
81class Privilege(_Expr):
82
83    def __init__(self, name, explanation, privilege_dependencies):
84        self.name = name
85        self.explanation = explanation
86        self.dependencies = privilege_dependencies
87
88    def iter_privileges(self, cache=None):
89        if cache is None:
90            cache = set([self])
91            yield self
92        elif self not in cache:
93            cache.add(self)
94            yield self
95
96    def __call__(self, privileges):
97        return self in privileges
98
99    def __repr__(self):
100        return self.name
101
102
103def add_admin_privilege(privilege):
104    """If privilege is none, BLOG_ADMIN is returned, otherwise BLOG_ADMIN
105    is "or"ed to the expression.
106    """
107    if privilege is None:
108        privilege = BLOG_ADMIN
109    elif privilege != BLOG_ADMIN:
110        privilege = BLOG_ADMIN | privilege
111    return privilege
112
113
114def bind_privileges(container, privileges, user=None):
115    """Binds the privileges to the container.  The privileges can be a list
116    of privilege names, the container must be a set.  This is called for
117    the HTTP round-trip in the form validation.
118    """
119    if not user:
120        user = get_request().user
121    app = get_application()
122    notification_types = app.notification_manager.notification_types
123    current_map = dict((x.name, x) for x in container)
124    currently_attached = set(x.name for x in container)
125    new_privileges = set(privileges)
126
127    # remove out-dated privileges
128    for name in currently_attached.difference(new_privileges):
129        container.remove(current_map[name])
130        # remove any privilege dependencies that are not attached to other
131        # privileges
132        if current_map[name].dependencies:
133            for privilege in current_map[name].dependencies.iter_privileges():
134                try:
135                    container.remove(privilege)
136                except KeyError:
137                    # privilege probably already removed
138                    pass
139
140        # remove notification subscriptions that required the privilege
141        # being deleted.
142        for notification in user.notification_subscriptions:
143            privs = notification_types[notification.notification_id].privileges
144            if current_map[name] in privs.iter_privileges():
145                db.session.delete(notification)
146                break
147            for privilege in current_map[name].dependencies:
148                if privilege in privs.iter_privileges():
149                    db.session.delete(notification)
150
151    # add new privileges
152    for name in new_privileges.difference(currently_attached):
153        privilege = app.privileges[name]
154        container.add(privilege)
155        # add dependable privileges
156        if privilege.dependencies:
157            for privilege in privilege.dependencies.iter_privileges():
158                container.add(privilege)
159
160
161def require_privilege(expr):
162    """Requires BLOG_ADMIN privilege or one of the given."""
163    def wrapped(f):
164        def decorated(request, *args, **kwargs):
165            if request.user.has_privilege(expr):
166                return f(request, *args, **kwargs)
167            raise Forbidden()
168        decorated.__name__ = f.__name__
169        decorated.__module__ = f.__module__
170        decorated.__doc__ = f.__doc__
171        return decorated
172    return wrapped
173
174
175def assert_privilege(expr):
176    """Like the `require_privilege` decorator but for asserting."""
177    if not get_request().user.has_privilege(expr):
178        raise Forbidden()
179
180
181def privilege_attribute(lowlevel_attribute):
182    """Returns a proxy attribute for privilege access."""
183    def creator_func(privilege):
184        if not isinstance(privilege, Privilege):
185            raise TypeError('%r is not a privilege object' %
186                            type(privilege).__name__)
187        priv = _Privilege.query.filter_by(name=privilege.name).first()
188        if priv is None:
189            priv = _Privilege(privilege.name)
190        return priv
191    return db.association_proxy(lowlevel_attribute, 'privilege',
192                                creator=creator_func)
193
194
195def _register(name, description, privilege_dependencies=None):
196    """Register a new builtin privilege."""
197    priv = Privilege(name, description, privilege_dependencies)
198    DEFAULT_PRIVILEGES[name] = priv
199    globals()[name] = priv
200    __all__.append(name)
201
202
203_register('ENTER_ADMIN_PANEL', lazy_gettext(u'can enter admin panel'))
204_register('BLOG_ADMIN', lazy_gettext(u'can administer the blog'))
205_register('CREATE_ENTRIES', lazy_gettext(u'can create new entries'),
206          ENTER_ADMIN_PANEL)
207_register('EDIT_OWN_ENTRIES', lazy_gettext(u'can edit their own entries'),
208          (ENTER_ADMIN_PANEL | CREATE_ENTRIES))
209_register('EDIT_OTHER_ENTRIES', lazy_gettext(u'can edit another person\'s entries'),
210          ENTER_ADMIN_PANEL)
211_register('CREATE_PAGES', lazy_gettext(u'can create new pages'),
212          ENTER_ADMIN_PANEL)
213_register('EDIT_OWN_PAGES', lazy_gettext(u'can edit their own pages'),
214          (ENTER_ADMIN_PANEL | CREATE_PAGES))
215_register('EDIT_OTHER_PAGES', lazy_gettext(u'can edit another person\'s pages'),
216          ENTER_ADMIN_PANEL)
217_register('VIEW_DRAFTS', lazy_gettext(u'can view drafts'))
218_register('MANAGE_CATEGORIES', lazy_gettext(u'can manage categories'),
219          ENTER_ADMIN_PANEL)
220_register('MODERATE_COMMENTS', lazy_gettext(u'can moderate comments'),
221          ENTER_ADMIN_PANEL)
222_register('MODERATE_OWN_ENTRIES', lazy_gettext(u'can moderate comments on it\'s own entries'),
223          (ENTER_ADMIN_PANEL | CREATE_ENTRIES))
224_register('MODERATE_OWN_PAGES', lazy_gettext(u'can moderate comments on it\'s own pages'),
225          (ENTER_ADMIN_PANEL | CREATE_PAGES))
226_register('VIEW_PROTECTED', lazy_gettext(u'can view protected entries'))
227
228# Regular users permissions
229_register('ENTER_ACCOUNT_PANEL', lazy_gettext(u'can enter his account panel'))
230_register('EDIT_OWN_COMMENTS', lazy_gettext(u'can edit own comments'),
231          ENTER_ACCOUNT_PANEL)
232_register('DELETE_OWN_COMMENTS', lazy_gettext(u'can delete own comments'),
233          ENTER_ACCOUNT_PANEL)
234
235
236CONTENT_TYPE_PRIVILEGES = {
237    'entry':    (CREATE_ENTRIES, EDIT_OWN_ENTRIES, EDIT_OTHER_ENTRIES),
238    'page':     (CREATE_PAGES, EDIT_OWN_PAGES, EDIT_OTHER_PAGES)
239}
Note: See TracBrowser for help on using the repository browser.