/
rbac.py
140 lines (122 loc) · 4.87 KB
/
rbac.py
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# Copyright 2012 Hai Thanh Nguyen
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Role-Based Access Control system
Implements the hierarchical model of role-based access control.
"""
from google.appengine.ext import ndb
class RoleModel(ndb.Model):
parents = ndb.KeyProperty(repeated=True)
name = ndb.StringProperty()
class PermissionModel(ndb.Model):
desc = ndb.StringProperty()
class RoleAssignmentModel(ndb.Model):
user_key = ndb.KeyProperty()
role_key = ndb.KeyProperty()
class PermissionAssignmentModel(ndb.Model):
role_key = ndb.KeyProperty()
perm_id = ndb.StringProperty()
def default_role(string_id, name=None, parents=[]):
"""Helper for creating a new and/or getting an existing role that has a string id"""
_roles_valid(parents)
q = RoleModel.get_by_id(string_id)
if q == None:
if name==None: raise Exception(u"""The role does not exist. You must provide a "name" argument if you wanna create the role."""
"""\nHave you installed the RBAC system?""")
return RoleModel(id=string_id, name=name, parents=parents).put()
return q.key
def register_permission(perm_id, desc):
"""Create a new permission
NOTICE: Permission registrations (using this function) MUST be put in the register_permissions function of rbac_setup.py
You must go to /upgrade for this function to take effect
"""
q = PermissionModel.get_by_id(perm_id)
if not q:
model = PermissionModel(id=perm_id, desc=desc)
model.put()
def _roles_valid(roles):
"""Internal helper."""
for role_key in roles:
if role_key.get() == None:
raise Exception(u'The role with id "%s" does not exist.' % role_key.id())
return False
return True
def register_role(name, parents=[]):
"""Create a new role
Args:
name: A human-readable name for the role
parents: A list of keys of the roles to be used as this role's parent
"""
#Make sure that the parents are all valid
_roles_valid(parents)
return RoleModel(name=name, parents=parents).put()
def add_role(user_key, role_key):
"""Assign a new role to a user"""
q = RoleAssignmentModel.gql("WHERE user_key = :1 AND role_key = :2", user_key, role_key)
if q.count(1) == 0:
model = RoleAssignmentModel(user_key=user_key, role_key=role_key)
model.put()
def has_permission(role_key, perm_id):
q = PermissionAssignmentModel.gql("WHERE role_key = :1 AND perm_id = :2", role_key, perm_id)
return q.count(1) > 0
def has_permissions(role_key, perms):
for perm in perms:
if not has_permission(role_key, perm):
return False
return True
def check_permission_role(role_key, perms):
"""Similar to check_permission, but only applies to a specific role"""
if not isinstance(perms, list):
perms = [perms]
if has_permissions(role_key, perms):
return True
else:
parents = role_key.get().parents
for parent in parents:
if check_permission_role(parent, perms):
return True
return False
def allow(role_key, perms):
"""Assign a permission to a role
Args:
role_key: the key of the role
perms: can be a string or a list of strings - one or more permission id's
"""
if not isinstance(perms, list):
perms = [perms]
for perm_id in perms:
if not has_permission(role_key, perm_id):
model = PermissionAssignmentModel(role_key=role_key, perm_id=perm_id)
model.put()
def get_roles(user_key):
q = ndb.gql("SELECT role_key FROM RoleAssignmentModel WHERE user_key = :1", user_key)
return [role.role_key for role in q]
def check_permission(user_key, perms):
"""Check if a user is allowed to do something.
Args:
user: the key of the user to check
perms: a permission or a list of permissions to check
"""
roles = get_roles(user_key)
if not isinstance(perms, list):
perms = [perms]
for perm_id in perms:
if PermissionModel.get_by_id(perm_id) == None:
raise Exception(u'Permission "%s" does not exist' % perm_id)
super_admin_role = default_role("super_admin")
if super_admin_role in roles:
return True #If user is "super admin", everything is allowed
for role_key in roles:
if check_permission_role(role_key, perms):
return True
return False