diff --git a/demo/apps/apijson_demo/dbinit.py b/demo/apps/apijson_demo/dbinit.py
index b235176..4fdcb5f 100644
--- a/demo/apps/apijson_demo/dbinit.py
+++ b/demo/apps/apijson_demo/dbinit.py
@@ -75,6 +75,11 @@
"date" : "2018-11-6",
"content" : "test moment from c",
},
+ {
+ "username" : "admin",
+ "date" : "2018-11-7",
+ "content" : "test moment from admin",
+ },
]
comment_list = [
@@ -99,6 +104,13 @@
"date" : "2018-12-9",
"content" : "comment hoho",
},
+ {
+ "username" : "admin",
+ "to_username" : "usera",
+ "moment_id" : 4,
+ "date" : "2018-12-10",
+ "content" : "comment kaka",
+ },
]
for d in user_list:
diff --git a/demo/apps/settings.ini b/demo/apps/settings.ini
index bd4ea4f..8a4d3c9 100644
--- a/demo/apps/settings.ini
+++ b/demo/apps/settings.ini
@@ -17,6 +17,7 @@ INSTALLED_APPS = [
'uliweb_comui',
'uliweb_comapps.auth.login',
'uliweb_comapps.auth.user_admin',
+ 'uliweb_comapps.auth.rbac_admin',
'uliweb_apijson.apijson',
'apijson_demo',
'tables',
diff --git a/demo/apps/tables/config.ini b/demo/apps/tables/config.ini
new file mode 100644
index 0000000..f5dbe0e
--- /dev/null
+++ b/demo/apps/tables/config.ini
@@ -0,0 +1,4 @@
+[DEPENDS]
+REQUIRED_APPS = [
+ 'uliweb_apijson.tables',
+]
diff --git a/demo/apps/tables/settings.ini b/demo/apps/tables/settings.ini
index 4712b17..98ddebc 100644
--- a/demo/apps/tables/settings.ini
+++ b/demo/apps/tables/settings.ini
@@ -1,7 +1,11 @@
[APIJSON_TABLES]
-user = {
- "editable" : "auto",
-}
+user = {"model_name":"user", "tableui_name":"users"}
+role = {"model_name":"role", "tableui_name":"roles", "role":"ADMIN"}
+permission = {"model_name":"permission", "tableui_name":"permissions", "role":"ADMIN"}
+moment = {"role":"OWNER"}
+comment = {"role":"OWNER"}
+
+[APIJSON_TABLE_UI]
moment = {
"editable" : "auto",
"table_fields" : [
@@ -20,7 +24,6 @@ moment = {
{"title":"Content","key":"content","type":"textarea"},
],
}
-
comment = {
"editable" : "auto",
"table_fields" : [
diff --git a/demo/apps/tables/templates/Tables/list.html b/demo/apps/tables/templates/Tables/list.html
deleted file mode 100644
index f6dc9c2..0000000
--- a/demo/apps/tables/templates/Tables/list.html
+++ /dev/null
@@ -1,37 +0,0 @@
-{{extend "Tables/layout.html"}}
-
-{{block title}}uliweb-apijson demo: tables{{end title}}
-
-{{block content_wrapper}}
-{{include "vue/inc_apijson_table.html"}}
-
-
- {{if role!="ADMIN":}}
-
You should
login with user
admin to view all the tables
- {{pass #if}}
-
-
-
-{{end content_wrapper}}
-
-{{block mainapp_vue}}
-
-{{end mainapp_vue}}
diff --git a/demo/apps/tables/views.py b/demo/apps/tables/views.py
deleted file mode 100644
index 095b181..0000000
--- a/demo/apps/tables/views.py
+++ /dev/null
@@ -1,21 +0,0 @@
-#coding=utf-8
-from uliweb import expose, functions
-
-@expose('/tables')
-class Tables(object):
- @expose('')
- def list(self):
- table_keys = settings.APIJSON_MODELS.keys()
- if request.user:
- if functions.has_role(request.user,"ADMIN"):
- role = "ADMIN"
- else:
- role = "OWNER"
- else:
- role = "UNKNOWN"
- apijson_tables = functions.get_apijson_tables(role)
- return {
- "table_keys_json":json_dumps(table_keys),
- "apijson_tables_json":json_dumps(apijson_tables),
- "role":role,
- }
diff --git a/tests/README.md b/tests/README.md
new file mode 100644
index 0000000..007b525
--- /dev/null
+++ b/tests/README.md
@@ -0,0 +1,6 @@
+commands to run the tests:
+
+```
+cd tests
+nosetests --with-doctest
+```
diff --git a/tests/demo/apps/apijson_demo/dbinit.py b/tests/demo/apps/apijson_demo/dbinit.py
index f66a7b2..a679a70 100644
--- a/tests/demo/apps/apijson_demo/dbinit.py
+++ b/tests/demo/apps/apijson_demo/dbinit.py
@@ -7,6 +7,7 @@
User = models.user
Privacy = models.privacy
Comment = models.comment
+Comment2 = models.comment2
Moment = models.moment
PublicNotice = models.publicnotice
@@ -95,21 +96,21 @@
"to_username" : "userb",
"moment_id" : 1,
"date" : "2018-12-1",
- "content" : "comment haha",
+ "content" : "comment from usera to userb",
},
{
"username" : "userb",
"to_username" : "usera",
"moment_id" : 2,
"date" : "2018-12-2",
- "content" : "comment xixi",
+ "content" : "comment from userb to usera",
},
{
"username" : "userc",
"to_username" : "usera",
"moment_id" : 3,
"date" : "2018-12-9",
- "content" : "comment hoho",
+ "content" : "comment from userc to usera",
},
]
@@ -158,6 +159,7 @@
d["to_id"] = User.get(User.c.username==d["to_username"]).id
print("create comment record for user '%s'"%(d["username"]))
Comment(**d).save()
+ Comment2(**d).save()
else:
print("error: unknown user '%s'"%(d["username"]))
diff --git a/tests/demo/apps/apijson_demo/models.py b/tests/demo/apps/apijson_demo/models.py
index ef92a94..56e0b9c 100644
--- a/tests/demo/apps/apijson_demo/models.py
+++ b/tests/demo/apps/apijson_demo/models.py
@@ -28,6 +28,13 @@ class Comment(Model):
date = Field(datetime.datetime, auto_now_add=True)
content = Field(TEXT)
+class Comment2(Model):
+ user_id = Reference("user")
+ to_id = Reference("user")
+ moment_id = Reference("moment")
+ date = Field(datetime.datetime, auto_now_add=True)
+ content = Field(TEXT)
+
class PublicNotice(Model):
date = Field(datetime.datetime, auto_now_add=True)
content = Field(TEXT)
diff --git a/tests/demo/apps/apijson_demo/settings.ini b/tests/demo/apps/apijson_demo/settings.ini
index 906cd61..d2a97a8 100644
--- a/tests/demo/apps/apijson_demo/settings.ini
+++ b/tests/demo/apps/apijson_demo/settings.ini
@@ -1,10 +1,18 @@
[MODELS]
privacy = 'apijson_demo.models.Privacy'
comment = 'apijson_demo.models.Comment'
+comment2 = 'apijson_demo.models.Comment2'
moment = 'apijson_demo.models.Moment'
publicnotice = 'apijson_demo.models.PublicNotice'
norequesttag = 'apijson_demo.models.NoRequestTag'
+[PERMISSIONS]
+get_comment2 = "get comment2", ["OWNER", "ADMIN"], ""
+head_comment2 = "head comment2", ["OWNER", "ADMIN"], ""
+post_comment2 = "post comment2", ["OWNER", "ADMIN"], ""
+put_comment2 = "put comment2", ["OWNER", "ADMIN"], ""
+delete_comment2 = "delete comment2", ["OWNER", "ADMIN"], ""
+
[APIJSON_MODELS]
user = {
"user_id_field" : "id",
@@ -39,6 +47,15 @@ comment = {
"PUT" : { "roles" : ["OWNER","ADMIN"] },
"DELETE" : { "roles" : ["OWNER","ADMIN"] },
}
+# only define permissions, no roles
+comment2 = {
+ "user_id_field" : "user_id",
+ "GET" : { "permissions":["get_comment2"] },
+ "HEAD" : { "permissions":["head_comment2"] },
+ "POST" : { "permissions":["post_comment2"] },
+ "PUT" : { "permissions":["put_comment2"]},
+ "DELETE" : {"permissions":["delete_comment2"]},
+}
publicnotice = {
"GET" : { "roles" : ["OWNER","LOGIN","ADMIN","UNKNOWN"] },
"HEAD" : { "roles" : ["OWNER","LOGIN","ADMIN","UNKNOWN"] },
@@ -73,6 +90,19 @@ comment = {
},
}
+comment2 = {
+ "POST" :{
+ "ADD" :{"@role": "OWNER"},
+ "DISALLOW" : ["id"],
+ "NECESSARY" : ["moment_id","content"]
+ },
+ "PUT" :{
+ "ADD":{"@role": "OWNER"},
+ "NECESSARY" : ["id","content"],
+ "DISALLOW" : ["user_id","to_id"],
+ },
+}
+
publicnotice = {
"PUT" :{
"NECESSARY" : ["id","content"],
diff --git a/tests/test.py b/tests/test.py
index dd246b6..c6e8458 100644
--- a/tests/test.py
+++ b/tests/test.py
@@ -1153,7 +1153,7 @@ def test_apijson_head():
>>> r = handler.post('/apijson/head', data=data, middlewares=[])
>>> d = json_loads(r.data)
>>> print(d)
- {'code': 400, 'msg': "no login user for role 'ADMIN'"}
+ {'code': 400, 'msg': "user doesn't have role 'ADMIN'"}
>>> #apijson head, without user and @role
>>> data ='''{
@@ -1581,7 +1581,7 @@ def test_apijson_delete():
>>> print(d)
{'code': 400, 'msg': "model 'nonexist' not found"}
- >>> #apijson delete, default to OWNER and delete other's record
+ >>> #apijson delete, try to delete other's moment
>>> data ='''{
... "moment": {
... "id": 2
@@ -1591,7 +1591,7 @@ def test_apijson_delete():
>>> r = handler.post('/apijson/delete', data=data, pre_call=pre_call_as("usera"), middlewares=[])
>>> d = json_loads(r.data)
>>> print(d)
- {'code': 400, 'msg': 'no permission'}
+ {'code': 400, 'msg': 'no role to access the data'}
>>> #apijson delete, without id
>>> data ='''{
@@ -1647,7 +1647,7 @@ def test_apijson_delete():
>>> r = handler.post('/apijson/delete', data=data, pre_call=pre_call_as("usera"), middlewares=[])
>>> d = json_loads(r.data)
>>> print(d)
- {'code': 400, 'msg': "'moment' not accessible by role 'UNKNOWN'"}
+ {'code': 400, 'msg': "role 'UNKNOWN' has no permission to access the data"}
>>> #apijson delete, with OWNER but not login
>>> data ='''{
@@ -1667,7 +1667,7 @@ def test_apijson_delete():
>>> r = handler.post('/apijson/delete', data=data, middlewares=[])
>>> d = json_loads(r.data)
>>> print(d)
- {'code': 400, 'msg': 'need login user'}
+ {'code': 400, 'msg': 'no role to access the data'}
>>> #apijson delete, with UNKNOWN role
>>> data ='''{
@@ -1701,5 +1701,112 @@ def test_apijson_delete():
>>> r = handler.post('/apijson/delete', data=data, pre_call=pre_call_as("admin"), middlewares=[])
>>> d = json_loads(r.data)
>>> print(d)
- {'code': 400, 'msg': "'moment' not accessible by role 'superuser'"}
+ {'code': 400, 'msg': "role 'superuser' has no permission to access the data"}
+ """
+
+def test_apijson_permission():
+ """
+ >>> application = make_simple_application(project_dir='.')
+ >>> handler = application.handler()
+
+ >>> #apijson get, query with id, access with owner
+ >>> data ='''{
+ ... "comment2":{
+ ... "id": 1
+ ... }
+ ... }'''
+ >>> r = handler.post('/apijson/get', data=data, pre_call=pre_call_as("admin"), middlewares=[])
+ >>> d = json_loads(r.data)
+ >>> print(d)
+ {'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}}
+
+ >>> #apijson get, query with id, access other's comment, expect empty result
+ >>> data ='''{
+ ... "comment2":{
+ ... "id": 1
+ ... }
+ ... }'''
+ >>> r = handler.post('/apijson/get', data=data, pre_call=pre_call_as("userb"), middlewares=[])
+ >>> d = json_loads(r.data)
+ >>> print(d)
+ {'code': 200, 'msg': 'success', 'comment2': None}
+
+ >>> #apijson get, query array
+ >>> data ='''{
+ ... "comment2":{
+ ... }
+ ... }'''
+ >>> r = handler.post('/apijson/get', data=data, pre_call=pre_call_as("usera"), middlewares=[])
+ >>> d = json_loads(r.data)
+ >>> print(d)
+ {'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}}
+
+ >>> #apijson get, query one with admin as OWNER
+ >>> data ='''{
+ ... "comment2":{
+ ... "@role":"OWNER"
+ ... }
+ ... }'''
+ >>> r = handler.post('/apijson/get', data=data, pre_call=pre_call_as("admin"), middlewares=[])
+ >>> d = json_loads(r.data)
+ >>> print(d)
+ {'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}}
+
+ >>> #apijson get, query one with admin as ADMIN
+ >>> data ='''{
+ ... "comment2":{
+ ... "@role":"ADMIN",
+ ... "user_id": 2
+ ... }
+ ... }'''
+ >>> r = handler.post('/apijson/get', data=data, pre_call=pre_call_as("admin"), middlewares=[])
+ >>> d = json_loads(r.data)
+ >>> print(d)
+ {'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}}
+
+ >>> #apijson get, query array
+ >>> data ='''{
+ ... "[]":{
+ ... "comment2": {"@role":"ADMIN"}
+ ... }
+ ... }'''
+ >>> r = handler.post('/apijson/get', data=data, pre_call=pre_call_as("admin"), middlewares=[])
+ >>> d = json_loads(r.data)
+ >>> print(d)
+ {'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}}, {'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}}, {'comment2': {'user_id': 3, 'to_id': 2, 'moment_id': 2, 'date': '2018-12-02 00:00:00', 'content': 'comment from userb to usera', 'id': 3}}, {'comment2': {'user_id': 4, 'to_id': 2, 'moment_id': 3, 'date': '2018-12-09 00:00:00', 'content': 'comment from userc to usera', 'id': 4}}]}
+
+ >>> #apijson head
+ >>> data ='''{
+ ... "comment2": {
+ ... "user_id": 1
+ ... }
+ ... }'''
+ >>> r = handler.post('/apijson/head', data=data, pre_call=pre_call_as("userc"), middlewares=[])
+ >>> d = json_loads(r.data)
+ >>> print(d)
+ {'code': 200, 'msg': 'success', 'comment2': {'code': 200, 'msg': 'success', 'count': 0}}
+
+ >>> #apijson delete with a user which have no permission
+ >>> data ='''{
+ ... "comment2": {
+ ... "id": 1
+ ... },
+ ... "@tag": "comment2"
+ ... }'''
+ >>> r = handler.post('/apijson/delete', data=data, pre_call=pre_call_as("userc"), middlewares=[])
+ >>> d = json_loads(r.data)
+ >>> print(d)
+ {'code': 400, 'msg': 'no permission'}
+
+ >>> #apijson delete with permission, ADMIN
+ >>> data ='''{
+ ... "comment2": {
+ ... "id": 1
+ ... },
+ ... "@tag": "comment2"
+ ... }'''
+ >>> r = handler.post('/apijson/delete', data=data, pre_call=pre_call_as("admin"), middlewares=[])
+ >>> d = json_loads(r.data)
+ >>> print(d)
+ {'code': 200, 'msg': 'success', 'comment2': {'id': 1, 'code': 200, 'message': 'success', 'count': 1}}
"""
diff --git a/uliweb_apijson/__init__.py b/uliweb_apijson/__init__.py
index 7ea7bfe..522163a 100644
--- a/uliweb_apijson/__init__.py
+++ b/uliweb_apijson/__init__.py
@@ -1,4 +1,4 @@
-__version__ = '0.2.2'
+__version__ = '0.3.0'
__url__ = 'https://github.com/zhangchunlin/uliweb-apijson'
__author__ = 'Chunlin Zhang'
__email__ = 'zhangchunlin@gmail.com'
diff --git a/uliweb_apijson/apijson/__init__.py b/uliweb_apijson/apijson/__init__.py
index 596d9c9..9a9474f 100644
--- a/uliweb_apijson/apijson/__init__.py
+++ b/uliweb_apijson/apijson/__init__.py
@@ -2,57 +2,60 @@
from uliweb import settings, models, request, functions, UliwebError
from uliweb.orm import ModelNotFound
+from json import dumps as json_dumps
import logging
log = logging.getLogger('apijson')
-def get_apijson_tables(role="UNKNOWN"):
- from uliweb import settings
- s = settings.APIJSON_TABLES
- if s:
- apijson_tables = dict(s.iteritems())
- else:
- return {}
- for n in apijson_tables:
- c = apijson_tables[n]
- editable = c.get("editable",False)
- _model_name = c.get("@model_name") or n
- if editable=="auto":
+class ApijsonTable(object):
+ def __init__(self, model_name, request_tag=None, role=None, tableui_name=None):
+ self.model_name = model_name
+ self.request_tag = request_tag or self.model_name
+ self.role = role
+ self.tableui_name = tableui_name or self.model_name
+ self._get_tableui()
+ self._apply_auto()
+
+ def _get_tableui(self):
+ self.tableui = settings.APIJSON_TABLE_UI.get(self.tableui_name, {})
+ if not self.tableui:
+ log.warn("cannot find setting for {} in settings.APIJSON_TABLE_UI".format(self.tableui_name))
+
+ def _apply_auto(self):
+ editable = self.tableui.get("editable", False)
+ if editable == "auto":
editable = False
- POST = settings.APIJSON_MODELS.get(_model_name,{}).get("POST")
+ POST = settings.APIJSON_MODELS.get(self.model_name, {}).get("POST")
if POST:
roles = POST["roles"]
if roles:
- editable = role in roles
- c["editable"] = editable
- return apijson_tables
-
-def get_apijson_table(role="UNKNOWN",name=None):
- from uliweb import settings
-
- if not name:
- return {}
- s = settings.APIJSON_TABLES
- if s:
- apijson_tables = dict(s.iteritems())
- else:
- return {}
-
- c = apijson_tables.get(name)
- if not c:
- return {}
- editable = c.get("editable",False)
- _model_name = c.get("@model_name") or n
- if editable=="auto":
- editable = False
- POST = settings.APIJSON_MODELS.get(_model_name,{}).get("POST")
- if POST:
- roles = POST["roles"]
- if roles:
- editable = role in roles
- c["editable"] = editable
- return c
+ editable = self.role in roles
+ self.tableui["editable"] = editable
+
+ def to_dict(self):
+ return dict(model_name=self.model_name,
+ request_tag=self.request_tag,
+ role=self.role,
+ tableui_name=self.tableui_name,
+ tableui=self.tableui)
+
+
+def get_apijson_tables():
+ def iter_table():
+ apison_table_ui = dict(
+ settings.APIJSON_TABLE_UI.iteritems())
+ for tableui_name in apison_table_ui:
+ tableui = apison_table_ui[tableui_name]
+ model_name = tableui.get("@model_name") or tableui_name
+ request_tag = model_name
+ role = None
+ yield(ApijsonTable(model_name=model_name, request_tag=request_tag, role=role, tableui_name=tableui_name))
+ return list(iter_table())
+
+
+def get_apijson_table(*args, **kwargs):
+ return ApijsonTable(*args, **kwargs)
class ApiJsonModelQuery(object):
def __init__(self,name,params,parent,key):
@@ -88,23 +91,39 @@ def _check_GET_permission(self):
roles = GET.get("roles")
params_role = self.params.get("@role")
-
- if not params_role:
- if hasattr(request,"user"):
- params_role = "LOGIN"
+ user = getattr(request, "user", None)
+
+ if roles:
+ if not params_role:
+ if user:
+ params_role = "LOGIN"
+ else:
+ params_role = "UNKNOWN"
+ elif params_role != "UNKNOWN":
+ if not user:
+ raise UliwebError("no login user for role '%s'" % (params_role))
+ if params_role not in roles:
+ raise UliwebError("'%s' not accessible by role '%s'" % (self.name, params_role))
+ if params_role == "UNKNOWN":
+ self.permission_check_ok = True
+ elif functions.has_role(user, params_role):
+ self.permission_check_ok = True
else:
- params_role = "UNKNOWN"
- elif params_role != "UNKNOWN":
- if not hasattr(request,"user"):
- raise UliwebError("no login user for role '%s'"%(params_role))
- if params_role not in roles:
- raise UliwebError("'%s' not accessible by role '%s'"%(self.name,params_role))
- if params_role == "UNKNOWN":
- self.permission_check_ok = True
- elif functions.has_role(request.user,params_role):
- self.permission_check_ok = True
- else:
- raise UliwebError("user doesn't have role '%s'"%(params_role))
+ raise UliwebError("user doesn't have role '%s'" % (params_role))
+ if not self.permission_check_ok:
+ perms = GET.get("permissions")
+ if perms:
+ if params_role:
+ role, msg = functions.has_permission_as_role(user, params_role, *perms)
+ if role:
+ self.permission_check_ok = True
+ else:
+ role = functions.has_permission(user, *perms)
+ if role:
+ role_name = getattr(role, "name")
+ if role_name:
+ self.permission_check_ok = True
+ params_role = role_name
if not self.permission_check_ok:
raise UliwebError("no permission")
@@ -260,3 +279,71 @@ def associated_query_array(self):
params.update(refs)
q = self._get_array_q(params)
item[self.name] = self._get_info(q.one())
+
+def is_obj_owner(user, obj, user_id_field):
+ if user and user_id_field:
+ return obj.to_dict().get(user_id_field)==user.id
+ return False
+
+def has_obj_role(user, obj, user_id_field, as_role, *roles):
+ from uliweb import functions
+ if as_role:
+ if as_role not in roles:
+ return False, "role '%s' has no permission to access the data"%(as_role)
+ if not functions.has_role(user, as_role):
+ return False, "user has no role '%s'"%(as_role)
+ if as_role == "OWNER":
+ if not is_obj_owner(user, obj, user_id_field):
+ return False, "user is not the owner of data"
+ return True, None
+ else:
+ for role in roles:
+ if functions.has_role(user, role):
+ if isinstance(role,str):
+ role_name = role
+ elif hasattr(role, "name"):
+ role_name = role.name
+ else:
+ continue
+ if role_name == "OWNER":
+ if is_obj_owner(user, obj, user_id_field):
+ return True, None
+ else:
+ continue
+ else:
+ return True, None
+ return False, "no role to access the data"
+
+def has_obj_permission(user, obj, user_id_field, *perms):
+ from uliweb import functions, models
+
+ Role = models.role
+ Perm = models.permission
+
+ for name in perms:
+ perm = Perm.get(Perm.c.name == name)
+ if not perm:
+ continue
+ has, msg = functions.has_obj_role(user, obj, user_id_field, None, *list(perm.perm_roles.with_relation().all()))
+ if has:
+ return has, None
+ return False, "no permission"
+
+def has_permission_as_role(user, as_role, *perms):
+ from uliweb import functions, models
+
+ Role = models.role
+ Perm = models.permission
+
+ flag = functions.has_role(user, as_role)
+ if not flag:
+ return False, "user has no role '%s'"%(as_role)
+
+ for name in perms:
+ perm = Perm.get(Perm.c.name==name)
+ if not perm:
+ continue
+ for role in perm.perm_roles.with_relation().all():
+ if role.name == as_role:
+ return role, None
+ return False, "no permission"
diff --git a/uliweb_apijson/apijson/settings.ini b/uliweb_apijson/apijson/settings.ini
index c713bf3..dd6e035 100644
--- a/uliweb_apijson/apijson/settings.ini
+++ b/uliweb_apijson/apijson/settings.ini
@@ -19,4 +19,7 @@ user = {
[FUNCTIONS]
get_apijson_tables = "uliweb_apijson.apijson.get_apijson_tables"
-get_apijson_table = "uliweb_apijson.apijson.get_apijson_table"
\ No newline at end of file
+get_apijson_table = "uliweb_apijson.apijson.get_apijson_table"
+has_obj_role = "uliweb_apijson.apijson.has_obj_role"
+has_obj_permission = "uliweb_apijson.apijson.has_obj_permission"
+has_permission_as_role = "uliweb_apijson.apijson.has_permission_as_role"
diff --git a/uliweb_apijson/apijson/templates/vue/inc_apijson_table.html b/uliweb_apijson/apijson/templates/vue/inc_apijson_table.html
index 361ba21..4920866 100644
--- a/uliweb_apijson/apijson/templates/vue/inc_apijson_table.html
+++ b/uliweb_apijson/apijson/templates/vue/inc_apijson_table.html
@@ -2,9 +2,7 @@
Vue.component('apijson-table', {
delimiters: ['{', '}'],
props: [
- "model_name", //apijson model name
- "request_tag", //apijson request tag, default will be same with model name
- "config",
+ "table",
"custom_tcolumns_render_generator",
"hook_init", //hook_init(vm), will invoke in mounted()
"hook_ajax_params", //hook_ajax_params(method,action,params), will invoke before ajax action
@@ -15,7 +13,7 @@
Add
-
+
@@ -47,15 +45,12 @@
- Confirm to delete #{delete_params.row&&delete_params.row.id} in table '{model_name}'?
+ Confirm to delete #{delete_params.row&&delete_params.row.id} in table '{table.model_name}'?
`,
data: function(){
var thisp = this
return {
- l_request_tag: null,
- role: "{{=role or ''}}",
-
loading: false,
modal_view: false,
viewedit_items: [],
@@ -105,7 +100,7 @@
if (thisp.config_editable) {
buttons.push(delete_button)
}
- return h('div', buttons);
+ return h('div', buttons)
}
}
},
@@ -126,7 +121,7 @@
},
tcolumns_init: false,
tlist:[],
- query_count: thisp.config ? (thisp.config.default_page_size || 10) : 10,
+ query_count: thisp.table.tableui ? (thisp.table.tableui.default_page_size || 10) : 10,
current_page: 1,
total: 0,
sort_key: "id",
@@ -181,10 +176,10 @@
var model_params = {
"@order":thisp.sort_key+thisp.sort_order
}
- if (thisp.role!="") {
- model_params["@role"] = thisp.role
+ if (thisp.table.role!=null && thisp.table.role!="") {
+ model_params["@role"] = thisp.table.role
}
- arr_params[thisp.model_name] = model_params
+ arr_params[thisp.table.model_name] = model_params
var params = {
"[]":arr_params,
"total@":"/[]/total"
@@ -201,7 +196,9 @@
if (data.code==200) {
var arr = data["[]"]
for (var i=0;i
@@ -25,8 +25,6 @@
`,
data: function(){
return {
- l_request_tag: null,
- role: "{{=role or ''}}",
loading: false,
row: {},
row_saved: {},
@@ -43,10 +41,11 @@
var model_params = {
"id":this.id
}
- if (this.role!='') {
- model_params["@role"] = this.role
+ var role = this.table.role
+ if (role!=null && role!='') {
+ model_params["@role"] = role
}
- params[this.model_name] = model_params
+ params[this.table.model_name] = model_params
var thisp = this
$.ajax({
type: "POST",
@@ -56,7 +55,7 @@
success: function (data) {
thisp.loading = false
if (data.code==200) {
- thisp.row = data[thisp.model_name]
+ thisp.row = data[thisp.table.model_name]
thisp.row_saved = thisp.row
thisp.viewedit_items = []
if (thisp.config_viewedit_fields!=null) {
@@ -70,7 +69,7 @@
}
else {
thisp.$Notice.error({
- title: 'error when get table '+thisp.table_name,
+ title: 'error when get table '+thisp.tableui_name,
desc: data.msg
})
}
@@ -84,7 +83,7 @@
},
save: function(){
var params = {
- "@tag": this.l_request_tag
+ "@tag": this.table.request_tag
}
var record_params = {}
//only save modified fields
@@ -95,10 +94,11 @@
this.row[d.key] = d.value
}
}
- if (this.role!='') {
- record_params["@role"] = this.role
+ var role = this.table.role
+ if (role!=null && role!='') {
+ record_params["@role"] = role
}
- params[this.l_request_tag] = record_params
+ params[this.table.request_tag] = record_params
params = this.ajax_hook("apijson_put","update",params)
var thisp = this
$.ajax({
@@ -110,15 +110,22 @@
if (data.code==200){
thisp.row_saved = thisp.row
thisp.$Notice.success({
- title: 'success update #'+thisp.row.id+' in table '+thisp.model_name,
+ title: 'success update #'+thisp.row.id+' in table '+thisp.table.model_name,
desc: data.msg
})
+ if(thisp.hook_ajax_post_result){
+ thisp.hook_ajax_post_result(true, params, thisp.row_saved)
+ }
}
else {
thisp.$Notice.error({
- title: 'error when update #'+thisp.row.id+' in table '+thisp.model_name,
+ title: 'error when update #'+thisp.row.id+' in table '+thisp.table.model_name,
desc: data.msg
})
+ if(thisp.hook_ajax_post_result){
+ thisp.hook_ajax_post_result(false, params, thisp.row_saved)
+ }
+
}
}
})
@@ -137,20 +144,14 @@
}
},
mounted: function(){
- if (this.request_tag==null) {
- this.l_request_tag = this.model_name
- }
- else {
- this.l_request_tag = this.request_tag
- }
//if not do this, the first notice will hide behind the navigation bar in uliweb apps
this.$Notice.config({top: 100,duration: 8});
if (this.hook_init!=null) {
this.hook_init(this)
}
- if (this.config!=null){
- this.config_editable = this.config.editable || false
- this.config_viewedit_fields = this.config.viewedit_fields || null
+ if (this.table.tableui!=null){
+ this.config_editable = this.table.tableui.editable || false
+ this.config_viewedit_fields = this.table.tableui.viewedit_fields || null
}
this.init_viewedit()
}
diff --git a/uliweb_apijson/apijson/views.py b/uliweb_apijson/apijson/views.py
index 5dc8a7e..e10b944 100644
--- a/uliweb_apijson/apijson/views.py
+++ b/uliweb_apijson/apijson/views.py
@@ -111,26 +111,39 @@ def _get_one(self,key):
if not GET:
return json({"code":400,"msg":"'%s' not accessible"%(model_name)})
+ user = getattr(request,"user", None)
roles = GET.get("roles")
permission_check_ok = False
- if not params_role:
- if hasattr(request,"user") and request.user:
- params_role = "LOGIN"
+ if roles:
+ if not params_role:
+ params_role = "LOGIN" if user else "UNKNOWN"
+ elif params_role != "UNKNOWN":
+ if not user:
+ return json({"code":400,"msg":"no login user for role '%s'"%(params_role)})
+ if params_role not in roles:
+ return json({"code":400,"msg":"'%s' not accessible by role '%s'"%(model_name,params_role)})
+ if params_role == "UNKNOWN":
+ permission_check_ok = True
+ elif functions.has_role(user,params_role):
+ permission_check_ok = True
else:
- params_role = "UNKNOWN"
- elif params_role != "UNKNOWN":
- if not (hasattr(request,"user") and request.user):
- return json({"code":400,"msg":"no login user for role '%s'"%(params_role)})
- if params_role not in roles:
- return json({"code":400,"msg":"'%s' not accessible by role '%s'"%(model_name,params_role)})
- if params_role == "UNKNOWN":
- permission_check_ok = True
- elif functions.has_role(request.user,params_role):
- permission_check_ok = True
- else:
- return json({"code":400,"msg":"user doesn't has role '%s'"%(params_role)})
+ return json({"code":400,"msg":"user doesn't has role '%s'"%(params_role)})
if not permission_check_ok:
- return json({"code":400,"msg":"no permission"})
+ perms = GET.get("permissions")
+ if perms:
+ if params_role:
+ role, msg = functions.has_permission_as_role(user, params_role, *perms)
+ if role:
+ permission_check_ok = True
+ else:
+ role = functions.has_permission(user, *perms)
+ if role:
+ role_name = getattr(role, "name")
+ if role_name:
+ permission_check_ok = True
+ params_role = role_name
+ if not permission_check_ok:
+ return json({"code":400,"msg":"no permission to access the data"})
if params_role=="OWNER":
owner_filtered,q = self._filter_owner(model,model_setting,q)
@@ -364,25 +377,35 @@ def _head(self,key):
roles = HEAD.get("roles")
permission_check_ok = False
- if not params_role:
- if hasattr(request,"user") and request.user:
- params_role = "LOGIN"
+ user = getattr(request, "user", None)
+ if roles:
+ if not params_role:
+ params_role = "LOGIN" if user else "UNKNOWN"
+ if params_role not in roles:
+ return json({"code":400,"msg":"role '%s' not have permission HEAD for '%s'"%(params_role,model_name)})
+ if functions.has_role(user, params_role):
+ permission_check_ok = True
else:
- params_role = "UNKNOWN"
- if params_role not in roles:
- return json({"code":400,"msg":"role '%s' not have permission HEAD for '%s'"%(params_role,model_name)})
- if params_role == "UNKNOWN":
- permission_check_ok = True
- elif not (hasattr(request,"user") and request.user):
- return json({"code":400,"msg":"no login user for role '%s'"%(params_role)})
- elif functions.has_role(request.user,params_role):
- permission_check_ok = True
+ return json({"code":400,"msg":"user doesn't have role '%s'"%(params_role)})
else:
- return json({"code":400,"msg":"user doesn't have role '%s'"%(params_role)})
+ perms = HEAD.get("permissions")
+ if perms:
+ if params_role:
+ role, msg = functions.has_permission_as_role(user, params_role, *perms)
+ if role:
+ permission_check_ok = True
+ else:
+ role = functions.has_permission(user, *perms)
+ if role:
+ role_name = getattr(role, "name")
+ if role_name:
+ permission_check_ok = True
+ params_role = role_name
+
#current implementation won't run here, but keep for safe
if not permission_check_ok:
return json({"code":400,"msg":"no permission"})
-
+
if params_role=="OWNER":
owner_filtered,q = self._filter_owner(model,model_setting,q)
if not owner_filtered:
@@ -394,7 +417,7 @@ def _head(self,key):
param = params[n]
if not hasattr(model.c,n):
return json({"code":400,"msg":"'%s' don't have field '%s'"%(model_name,n)})
- q = model.filter(getattr(model.c,n)==param)
+ q = q.filter(getattr(model.c,n)==param)
rdict = {
"code":200,
"msg":"success",
@@ -695,33 +718,23 @@ def _delete_one(self,key,tag):
return json({"code":400,"msg":"cannot find record id = '%s'"%(id_)})
permission_check_ok = False
+ msg = "'%s' not accessible by user"%(model_name)
DELETE = model_setting.get("DELETE")
if DELETE:
roles = DELETE.get("roles")
- if params_role:
- if not params_role in roles:
- return json({"code":400,"msg":"'%s' not accessible by role '%s'"%(model_name,params_role)})
- roles = [params_role]
if roles:
- for role in roles:
- if role == "OWNER":
- if hasattr(request,"user") and request.user:
- if user_id_field:
- if obj.to_dict().get(user_id_field)==request.user.id:
- permission_check_ok = True
- break
- else:
- return json({"code":400,"msg":"need login user"})
- elif role == "UNKNOWN":
+ has, msg = functions.has_obj_role(getattr(request,"user",None), obj, user_id_field, params_role, *roles)
+ if has:
+ permission_check_ok = True
+ if not permission_check_ok:
+ perms = DELETE.get("permissions")
+ if perms:
+ has, msg = functions.has_obj_permission(getattr(request,"user",None), obj, user_id_field, *perms)
+ if has:
permission_check_ok = True
- break
- else:
- if functions.has_role(request.user,role):
- permission_check_ok = True
- break
if not permission_check_ok:
- return json({"code":400,"msg":"no permission"})
+ return json({"code":400,"msg":msg})
try:
obj.delete()
diff --git a/uliweb_apijson/tables/README.md b/uliweb_apijson/tables/README.md
new file mode 100644
index 0000000..e69de29
diff --git a/uliweb_apijson/tables/__init__.py b/uliweb_apijson/tables/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/uliweb_apijson/tables/settings.ini b/uliweb_apijson/tables/settings.ini
new file mode 100644
index 0000000..e69de29
diff --git a/demo/apps/tables/static/readme.txt b/uliweb_apijson/tables/static/readme.txt
similarity index 100%
rename from demo/apps/tables/static/readme.txt
rename to uliweb_apijson/tables/static/readme.txt
diff --git a/uliweb_apijson/tables/templates/Tables/inc/table_config.html b/uliweb_apijson/tables/templates/Tables/inc/table_config.html
new file mode 100644
index 0000000..1eb0526
--- /dev/null
+++ b/uliweb_apijson/tables/templates/Tables/inc/table_config.html
@@ -0,0 +1,94 @@
+
diff --git a/demo/apps/tables/templates/Tables/layout.html b/uliweb_apijson/tables/templates/Tables/layout.html
similarity index 100%
rename from demo/apps/tables/templates/Tables/layout.html
rename to uliweb_apijson/tables/templates/Tables/layout.html
diff --git a/uliweb_apijson/tables/templates/Tables/list.html b/uliweb_apijson/tables/templates/Tables/list.html
new file mode 100644
index 0000000..fa9fc65
--- /dev/null
+++ b/uliweb_apijson/tables/templates/Tables/list.html
@@ -0,0 +1,54 @@
+{{extend "Tables/layout.html"}}
+
+{{block title}}uliweb-apijson: tables{{end title}}
+
+{{block other_use}}
+{{include "vue/inc_apijson_table.html"}}
+{{include "Tables/inc/table_config.html"}}
+{{end other_use}}
+
+{{block content_wrapper}}
+
+
+ {{if role!="ADMIN":}}
+
You should
login with user
admin to view all the tables
+ {{pass #if}}
+
+
+
+{{end content_wrapper}}
+
+{{block mainapp_vue}}
+
+{{end mainapp_vue}}
diff --git a/demo/apps/tables/templates/readme.txt b/uliweb_apijson/tables/templates/readme.txt
similarity index 100%
rename from demo/apps/tables/templates/readme.txt
rename to uliweb_apijson/tables/templates/readme.txt
diff --git a/uliweb_apijson/tables/views.py b/uliweb_apijson/tables/views.py
new file mode 100644
index 0000000..a2c8d70
--- /dev/null
+++ b/uliweb_apijson/tables/views.py
@@ -0,0 +1,30 @@
+#coding=utf-8
+from uliweb import expose, functions, settings
+
+@expose('/tables')
+class Tables(object):
+ @expose('')
+ def list(self):
+ if request.user:
+ role = "ADMIN" if functions.has_role(request.user,"ADMIN") else "OWNER"
+ else:
+ role = "UNKNOWN"
+ apijson_tables = functions.get_apijson_tables()
+ def _get_model(i):
+ model_name = i.model_name
+ model = settings.APIJSON_MODELS.get(model_name,{})
+ if not i.role:
+ roles = model.get("GET",{}).get("roles")
+ i.role = roles[0] if isinstance(roles, list) else roles
+ return model
+ models = [_get_model(i) for i in apijson_tables]
+ def _get_request(i):
+ request_tag = i.request_tag or i.model_name
+ return settings.APIJSON_REQUESTS.get(request_tag,{})
+ requests = [_get_request(i) for i in apijson_tables]
+ return {
+ "apijson_tables_json":json_dumps([d.to_dict() for d in apijson_tables]),
+ "models_json": json_dumps(models),
+ "requests_json": json_dumps(requests),
+ "role":role,
+ }