Skip to content

Commit 2a4357c

Browse files
committed
add permission support in apijson get(one)/head/delete, 8 test cases
1 parent 39a1c38 commit 2a4357c

File tree

8 files changed

+285
-61
lines changed

8 files changed

+285
-61
lines changed

tests/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
commands to run the tests:
2+
3+
```
4+
cd tests
5+
nosetests --with-doctest
6+
```

tests/demo/apps/apijson_demo/dbinit.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
User = models.user
88
Privacy = models.privacy
99
Comment = models.comment
10+
Comment2 = models.comment2
1011
Moment = models.moment
1112
PublicNotice = models.publicnotice
1213

@@ -95,21 +96,21 @@
9596
"to_username" : "userb",
9697
"moment_id" : 1,
9798
"date" : "2018-12-1",
98-
"content" : "comment haha",
99+
"content" : "comment from usera to userb",
99100
},
100101
{
101102
"username" : "userb",
102103
"to_username" : "usera",
103104
"moment_id" : 2,
104105
"date" : "2018-12-2",
105-
"content" : "comment xixi",
106+
"content" : "comment from userb to usera",
106107
},
107108
{
108109
"username" : "userc",
109110
"to_username" : "usera",
110111
"moment_id" : 3,
111112
"date" : "2018-12-9",
112-
"content" : "comment hoho",
113+
"content" : "comment from userc to usera",
113114
},
114115
]
115116

@@ -158,6 +159,7 @@
158159
d["to_id"] = User.get(User.c.username==d["to_username"]).id
159160
print("create comment record for user '%s'"%(d["username"]))
160161
Comment(**d).save()
162+
Comment2(**d).save()
161163
else:
162164
print("error: unknown user '%s'"%(d["username"]))
163165

tests/demo/apps/apijson_demo/models.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ class Comment(Model):
2828
date = Field(datetime.datetime, auto_now_add=True)
2929
content = Field(TEXT)
3030

31+
class Comment2(Model):
32+
user_id = Reference("user")
33+
to_id = Reference("user")
34+
moment_id = Reference("moment")
35+
date = Field(datetime.datetime, auto_now_add=True)
36+
content = Field(TEXT)
37+
3138
class PublicNotice(Model):
3239
date = Field(datetime.datetime, auto_now_add=True)
3340
content = Field(TEXT)

tests/demo/apps/apijson_demo/settings.ini

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
11
[MODELS]
22
privacy = 'apijson_demo.models.Privacy'
33
comment = 'apijson_demo.models.Comment'
4+
comment2 = 'apijson_demo.models.Comment2'
45
moment = 'apijson_demo.models.Moment'
56
publicnotice = 'apijson_demo.models.PublicNotice'
67
norequesttag = 'apijson_demo.models.NoRequestTag'
78

9+
[PERMISSIONS]
10+
get_comment2 = "get comment2", ["OWNER", "ADMIN"], ""
11+
head_comment2 = "head comment2", ["OWNER", "ADMIN"], ""
12+
post_comment2 = "post comment2", ["OWNER", "ADMIN"], ""
13+
put_comment2 = "put comment2", ["OWNER", "ADMIN"], ""
14+
delete_comment2 = "delete comment2", ["OWNER", "ADMIN"], ""
15+
816
[APIJSON_MODELS]
917
user = {
1018
"user_id_field" : "id",
@@ -39,6 +47,14 @@ comment = {
3947
"PUT" : { "roles" : ["OWNER","ADMIN"] },
4048
"DELETE" : { "roles" : ["OWNER","ADMIN"] },
4149
}
50+
comment2 = {
51+
"user_id_field" : "user_id",
52+
"GET" : { "permissions":["get_comment2"] },
53+
"HEAD" : { "permissions":["head_comment2"] },
54+
"POST" : { "permissions":["post_comment2"] },
55+
"PUT" : { "permissions":["put_comment2"]},
56+
"DELETE" : {"permissions":["delete_comment2"]},
57+
}
4258
publicnotice = {
4359
"GET" : { "roles" : ["OWNER","LOGIN","ADMIN","UNKNOWN"] },
4460
"HEAD" : { "roles" : ["OWNER","LOGIN","ADMIN","UNKNOWN"] },
@@ -73,6 +89,19 @@ comment = {
7389
},
7490
}
7591

92+
comment2 = {
93+
"POST" :{
94+
"ADD" :{"@role": "OWNER"},
95+
"DISALLOW" : ["id"],
96+
"NECESSARY" : ["moment_id","content"]
97+
},
98+
"PUT" :{
99+
"ADD":{"@role": "OWNER"},
100+
"NECESSARY" : ["id","content"],
101+
"DISALLOW" : ["user_id","to_id"],
102+
},
103+
}
104+
76105
publicnotice = {
77106
"PUT" :{
78107
"NECESSARY" : ["id","content"],

tests/test.py

Lines changed: 102 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1153,7 +1153,7 @@ def test_apijson_head():
11531153
>>> r = handler.post('/apijson/head', data=data, middlewares=[])
11541154
>>> d = json_loads(r.data)
11551155
>>> print(d)
1156-
{'code': 400, 'msg': "no login user for role 'ADMIN'"}
1156+
{'code': 400, 'msg': "user doesn't have role 'ADMIN'"}
11571157
11581158
>>> #apijson head, without user and @role
11591159
>>> data ='''{
@@ -1581,7 +1581,7 @@ def test_apijson_delete():
15811581
>>> print(d)
15821582
{'code': 400, 'msg': "model 'nonexist' not found"}
15831583
1584-
>>> #apijson delete, default to OWNER and delete other's record
1584+
>>> #apijson delete, try to delete other's moment
15851585
>>> data ='''{
15861586
... "moment": {
15871587
... "id": 2
@@ -1591,7 +1591,7 @@ def test_apijson_delete():
15911591
>>> r = handler.post('/apijson/delete', data=data, pre_call=pre_call_as("usera"), middlewares=[])
15921592
>>> d = json_loads(r.data)
15931593
>>> print(d)
1594-
{'code': 400, 'msg': 'no permission'}
1594+
{'code': 400, 'msg': 'no role to access the data'}
15951595
15961596
>>> #apijson delete, without id
15971597
>>> data ='''{
@@ -1647,7 +1647,7 @@ def test_apijson_delete():
16471647
>>> r = handler.post('/apijson/delete', data=data, pre_call=pre_call_as("usera"), middlewares=[])
16481648
>>> d = json_loads(r.data)
16491649
>>> print(d)
1650-
{'code': 400, 'msg': "'moment' not accessible by role 'UNKNOWN'"}
1650+
{'code': 400, 'msg': "role 'UNKNOWN' has no permission to access the data"}
16511651
16521652
>>> #apijson delete, with OWNER but not login
16531653
>>> data ='''{
@@ -1667,7 +1667,7 @@ def test_apijson_delete():
16671667
>>> r = handler.post('/apijson/delete', data=data, middlewares=[])
16681668
>>> d = json_loads(r.data)
16691669
>>> print(d)
1670-
{'code': 400, 'msg': 'need login user'}
1670+
{'code': 400, 'msg': 'no role to access the data'}
16711671
16721672
>>> #apijson delete, with UNKNOWN role
16731673
>>> data ='''{
@@ -1701,5 +1701,101 @@ def test_apijson_delete():
17011701
>>> r = handler.post('/apijson/delete', data=data, pre_call=pre_call_as("admin"), middlewares=[])
17021702
>>> d = json_loads(r.data)
17031703
>>> print(d)
1704-
{'code': 400, 'msg': "'moment' not accessible by role 'superuser'"}
1704+
{'code': 400, 'msg': "role 'superuser' has no permission to access the data"}
1705+
"""
1706+
1707+
def test_apijson_permission():
1708+
"""
1709+
>>> application = make_simple_application(project_dir='.')
1710+
>>> handler = application.handler()
1711+
1712+
>>> #apijson get, query with id, access with owner
1713+
>>> data ='''{
1714+
... "comment2":{
1715+
... "id": 1
1716+
... }
1717+
... }'''
1718+
>>> r = handler.post('/apijson/get', data=data, pre_call=pre_call_as("admin"), middlewares=[])
1719+
>>> d = json_loads(r.data)
1720+
>>> print(d)
1721+
{'code': 200, 'msg': 'success', 'comment2': {'user_id': 1, 'to_id': 3, 'moment_id': 1, 'date': '2018-11-01 00:00:00', 'content': 'comment from admin', 'id': 1}}
1722+
1723+
>>> #apijson get, query with id, access other's comment, expect empty result
1724+
>>> data ='''{
1725+
... "comment2":{
1726+
... "id": 1
1727+
... }
1728+
... }'''
1729+
>>> r = handler.post('/apijson/get', data=data, pre_call=pre_call_as("userb"), middlewares=[])
1730+
>>> d = json_loads(r.data)
1731+
>>> print(d)
1732+
{'code': 200, 'msg': 'success', 'comment2': None}
1733+
1734+
>>> #apijson get, query array
1735+
>>> data ='''{
1736+
... "comment2":{
1737+
... }
1738+
... }'''
1739+
>>> r = handler.post('/apijson/get', data=data, pre_call=pre_call_as("usera"), middlewares=[])
1740+
>>> d = json_loads(r.data)
1741+
>>> print(d)
1742+
{'code': 200, 'msg': 'success', 'comment2': {'user_id': 2, 'to_id': 3, 'moment_id': 1, 'date': '2018-12-01 00:00:00', 'content': 'comment from usera to userb', 'id': 2}}
1743+
1744+
>>> #apijson get, query one with admin as OWNER
1745+
>>> data ='''{
1746+
... "comment2":{
1747+
... "@role":"OWNER"
1748+
... }
1749+
... }'''
1750+
>>> r = handler.post('/apijson/get', data=data, pre_call=pre_call_as("admin"), middlewares=[])
1751+
>>> d = json_loads(r.data)
1752+
>>> print(d)
1753+
{'code': 200, 'msg': 'success', 'comment2': {'user_id': 1, 'to_id': 3, 'moment_id': 1, 'date': '2018-11-01 00:00:00', 'content': 'comment from admin', 'id': 1}}
1754+
1755+
>>> #apijson get, query one with admin as ADMIN
1756+
>>> data ='''{
1757+
... "comment2":{
1758+
... "@role":"ADMIN",
1759+
... "user_id": 2
1760+
... }
1761+
... }'''
1762+
>>> r = handler.post('/apijson/get', data=data, pre_call=pre_call_as("admin"), middlewares=[])
1763+
>>> d = json_loads(r.data)
1764+
>>> print(d)
1765+
{'code': 200, 'msg': 'success', 'comment2': {'user_id': 2, 'to_id': 3, 'moment_id': 1, 'date': '2018-12-01 00:00:00', 'content': 'comment from usera to userb', 'id': 2}}
1766+
1767+
>>> #apijson head
1768+
>>> data ='''{
1769+
... "comment2": {
1770+
... "user_id": 1
1771+
... }
1772+
... }'''
1773+
>>> r = handler.post('/apijson/head', data=data, pre_call=pre_call_as("userc"), middlewares=[])
1774+
>>> d = json_loads(r.data)
1775+
>>> print(d)
1776+
{'code': 200, 'msg': 'success', 'comment2': {'code': 200, 'msg': 'success', 'count': 0}}
1777+
1778+
>>> #apijson delete with a user which have no permission
1779+
>>> data ='''{
1780+
... "comment2": {
1781+
... "id": 1
1782+
... },
1783+
... "@tag": "comment2"
1784+
... }'''
1785+
>>> r = handler.post('/apijson/delete', data=data, pre_call=pre_call_as("userc"), middlewares=[])
1786+
>>> d = json_loads(r.data)
1787+
>>> print(d)
1788+
{'code': 400, 'msg': 'no permission'}
1789+
1790+
>>> #apijson delete with permission, ADMIN
1791+
>>> data ='''{
1792+
... "comment2": {
1793+
... "id": 1
1794+
... },
1795+
... "@tag": "comment2"
1796+
... }'''
1797+
>>> r = handler.post('/apijson/delete', data=data, pre_call=pre_call_as("admin"), middlewares=[])
1798+
>>> d = json_loads(r.data)
1799+
>>> print(d)
1800+
{'code': 200, 'msg': 'success', 'comment2': {'id': 1, 'code': 200, 'message': 'success', 'count': 1}}
17051801
"""

uliweb_apijson/apijson/__init__.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,3 +260,71 @@ def associated_query_array(self):
260260
params.update(refs)
261261
q = self._get_array_q(params)
262262
item[self.name] = self._get_info(q.one())
263+
264+
def is_obj_owner(user, obj, user_id_field):
265+
if user and user_id_field:
266+
return obj.to_dict().get(user_id_field)==user.id
267+
return False
268+
269+
def has_obj_role(user, obj, user_id_field, as_role, *roles):
270+
from uliweb import functions
271+
if as_role:
272+
if as_role not in roles:
273+
return False, "role '%s' has no permission to access the data"%(as_role)
274+
if not functions.has_role(user, as_role):
275+
return False, "user has no role '%s'"%(as_role)
276+
if as_role == "OWNER":
277+
if not is_obj_owner(user, obj, user_id_field):
278+
return False, "user is not the owner of data"
279+
return True, None
280+
else:
281+
for role in roles:
282+
if functions.has_role(user, role):
283+
if isinstance(role,str):
284+
role_name = role
285+
elif hasattr(role, "name"):
286+
role_name = role.name
287+
else:
288+
continue
289+
if role_name == "OWNER":
290+
if is_obj_owner(user, obj, user_id_field):
291+
return True, None
292+
else:
293+
continue
294+
else:
295+
return True, None
296+
return False, "no role to access the data"
297+
298+
def has_obj_permission(user, obj, user_id_field, *perms):
299+
from uliweb import functions, models
300+
301+
Role = models.role
302+
Perm = models.permission
303+
304+
for name in perms:
305+
perm = Perm.get(Perm.c.name == name)
306+
if not perm:
307+
continue
308+
has, msg = functions.has_obj_role(user, obj, user_id_field, None, *list(perm.perm_roles.with_relation().all()))
309+
if has:
310+
return has, None
311+
return False, "no permission"
312+
313+
def has_permission_as_role(user, as_role, *perms):
314+
from uliweb import functions, models
315+
316+
Role = models.role
317+
Perm = models.permission
318+
319+
flag = functions.has_role(user, as_role)
320+
if not flag:
321+
return False, "user has no role '%s'"%(as_role)
322+
323+
for name in perms:
324+
perm = Perm.get(Perm.c.name==name)
325+
if not perm:
326+
continue
327+
for role in perm.perm_roles.with_relation().all():
328+
if role.name == as_role:
329+
return role, None
330+
return False, "no permission"

uliweb_apijson/apijson/settings.ini

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,7 @@ user = {
1919

2020
[FUNCTIONS]
2121
get_apijson_tables = "uliweb_apijson.apijson.get_apijson_tables"
22-
get_apijson_table = "uliweb_apijson.apijson.get_apijson_table"
22+
get_apijson_table = "uliweb_apijson.apijson.get_apijson_table"
23+
has_obj_role = "uliweb_apijson.apijson.has_obj_role"
24+
has_obj_permission = "uliweb_apijson.apijson.has_obj_permission"
25+
has_permission_as_role = "uliweb_apijson.apijson.has_permission_as_role"

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy