From b46426abe0b7e3622ab1d7bfbba407daf8a0c0bd Mon Sep 17 00:00:00 2001 From: yumoqing Date: Wed, 16 Jul 2025 14:19:12 +0800 Subject: [PATCH] first commit --- README.md | 2 + json/build.sh | 3 + json/organization.json | 51 +++++ json/orgtypes.json | 18 ++ json/permission.json | 21 ++ json/role.json | 31 +++ json/rolepermission.json | 26 +++ json/userapp.json | 16 ++ json/userdepartment.json | 17 ++ json/userrole.json | 14 ++ json/users.json | 28 +++ models/audit_log.xlsx | Bin 0 -> 18421 bytes models/organization.xlsx | Bin 0 -> 16526 bytes models/orgtypes.xlsx | Bin 0 -> 16581 bytes models/permission.xlsx | Bin 0 -> 16614 bytes models/role.xlsx | Bin 0 -> 16448 bytes models/rolepermission.xlsx | Bin 0 -> 16512 bytes models/userapp.xlsx | Bin 0 -> 16578 bytes models/userdepartment.xlsx | Bin 0 -> 18256 bytes models/userrole.xlsx | Bin 0 -> 16458 bytes models/users.xlsx | Bin 0 -> 17888 bytes pyproject.toml | 4 + rbac/README.md | 0 rbac/__init__.py | 0 rbac/audit_log.py | 9 + rbac/check_perm.py | 176 ++++++++++++++++ rbac/init.py | 18 ++ rbac/set_role_perms.py | 71 +++++++ rbac/version.py | 1 + requirements.txt | 2 + script/init.py | 191 ++++++++++++++++++ script/setroleperms.sh | 7 + setup.cfg | 16 ++ wwwroot/add_adminuser.dspy | 23 +++ wwwroot/add_adminuser.ui | 36 ++++ wwwroot/add_provider.dspy | 36 ++++ wwwroot/add_reseller.dspy | 26 +++ wwwroot/admin_menu.ui | 7 + wwwroot/get_provider.dspy | 151 ++++++++++++++ wwwroot/get_reseller.dspy | 151 ++++++++++++++ wwwroot/user/login.ui | 121 +++++++++++ wwwroot/user/logout.dspy | 23 +++ wwwroot/user/myrole.ui | 27 +++ wwwroot/user/register.dspy | 10 + wwwroot/user/register.ui | 80 ++++++++ wwwroot/user/reset_password/index.ui | 49 +++++ .../user/reset_password/reset_password.dspy | 17 ++ wwwroot/user/up_login.dspy | 58 ++++++ wwwroot/user/user.ui | 80 ++++++++ wwwroot/user/user_menu.ui | 55 +++++ wwwroot/user/user_panel.ui | 15 ++ wwwroot/user/userapikey/add_userapikey.dspy | 25 +++ .../user/userapikey/delete_userapikey.dspy | 24 +++ wwwroot/user/userapikey/get_userapikey.dspy | 74 +++++++ wwwroot/user/userapikey/index.ui | 126 ++++++++++++ .../user/userapikey/update_userapikey.dspy | 22 ++ wwwroot/user/wechat_login.ui | 5 + wwwroot/userpassword_login.dspy | 11 + wwwroot/userpassword_login.ui | 43 ++++ 59 files changed, 2017 insertions(+) create mode 100644 README.md create mode 100755 json/build.sh create mode 100644 json/organization.json create mode 100644 json/orgtypes.json create mode 100644 json/permission.json create mode 100644 json/role.json create mode 100644 json/rolepermission.json create mode 100644 json/userapp.json create mode 100644 json/userdepartment.json create mode 100644 json/userrole.json create mode 100644 json/users.json create mode 100644 models/audit_log.xlsx create mode 100644 models/organization.xlsx create mode 100644 models/orgtypes.xlsx create mode 100644 models/permission.xlsx create mode 100644 models/role.xlsx create mode 100644 models/rolepermission.xlsx create mode 100644 models/userapp.xlsx create mode 100644 models/userdepartment.xlsx create mode 100644 models/userrole.xlsx create mode 100644 models/users.xlsx create mode 100644 pyproject.toml create mode 100644 rbac/README.md create mode 100644 rbac/__init__.py create mode 100644 rbac/audit_log.py create mode 100644 rbac/check_perm.py create mode 100644 rbac/init.py create mode 100644 rbac/set_role_perms.py create mode 100644 rbac/version.py create mode 100644 requirements.txt create mode 100644 script/init.py create mode 100644 script/setroleperms.sh create mode 100644 setup.cfg create mode 100644 wwwroot/add_adminuser.dspy create mode 100644 wwwroot/add_adminuser.ui create mode 100644 wwwroot/add_provider.dspy create mode 100644 wwwroot/add_reseller.dspy create mode 100644 wwwroot/admin_menu.ui create mode 100644 wwwroot/get_provider.dspy create mode 100644 wwwroot/get_reseller.dspy create mode 100644 wwwroot/user/login.ui create mode 100644 wwwroot/user/logout.dspy create mode 100644 wwwroot/user/myrole.ui create mode 100644 wwwroot/user/register.dspy create mode 100644 wwwroot/user/register.ui create mode 100644 wwwroot/user/reset_password/index.ui create mode 100644 wwwroot/user/reset_password/reset_password.dspy create mode 100644 wwwroot/user/up_login.dspy create mode 100644 wwwroot/user/user.ui create mode 100644 wwwroot/user/user_menu.ui create mode 100644 wwwroot/user/user_panel.ui create mode 100644 wwwroot/user/userapikey/add_userapikey.dspy create mode 100644 wwwroot/user/userapikey/delete_userapikey.dspy create mode 100644 wwwroot/user/userapikey/get_userapikey.dspy create mode 100644 wwwroot/user/userapikey/index.ui create mode 100644 wwwroot/user/userapikey/update_userapikey.dspy create mode 100644 wwwroot/user/wechat_login.ui create mode 100644 wwwroot/userpassword_login.dspy create mode 100644 wwwroot/userpassword_login.ui diff --git a/README.md b/README.md new file mode 100644 index 0000000..1c04cf8 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# rbac + diff --git a/json/build.sh b/json/build.sh new file mode 100755 index 0000000..86a8ad9 --- /dev/null +++ b/json/build.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +xls2ui -m ../models -o ../wwwroot rbac *.json diff --git a/json/organization.json b/json/organization.json new file mode 100644 index 0000000..66e312a --- /dev/null +++ b/json/organization.json @@ -0,0 +1,51 @@ +{ + "models_dir": "${HOME}$/py/rbac/models", + "output_dir": "${HOME}$/py/sage/wwwroot/_a/organization", + "dbname": "sage", + "tblname": "organization", + "title":"Organization", + "params": { + "sortby":"orgname", + "editor":{ + "binds":[ + { + "wid":"province_id", + "event":"changed", + "actiontype":"script", + "target":"city_id", + "script":"this.loadData({cond:'parentid=\\''+params.province_id+'\\''})" + }, + { + "wid":"city_id", + "event":"changed", + "actiontype":"script", + "target":"distinct_id", + "script":"this.loadData({cond:'parentid=\\'' + params.city_id+ '\\''})" + } + ] + }, + "browserfields": { + "exclouded": ["id"], + "alters": { + } + }, + "editexclouded": [ + "id" + ], + "subtables":[ + { + "field":"orgid", + "title":"Org. type", + "url":"../orgtypes", + "subtable":"orgtypes" + }, + { + "field":"orgid", + "title":"Users", + "url":"../users", + "subtable":"users" + } + ], + "record_toolbar": null + } +} diff --git a/json/orgtypes.json b/json/orgtypes.json new file mode 100644 index 0000000..e6778da --- /dev/null +++ b/json/orgtypes.json @@ -0,0 +1,18 @@ +{ + "models_dir": "${HOME}$/py/rbac/models", + "output_dir": "${HOME}$/py/sage/wwwroot/_a/orgtypes", + "dbname": "sage", + "tblname": "orgtypes", + "title":"Org. type", + "params": { + "browserfields": { + "exclouded": ["id", "orgid"], + "cwidth": {} + }, + "editexclouded": [ + "id", + "orgid" + ], + "record_toolbar": null + } +} diff --git a/json/permission.json b/json/permission.json new file mode 100644 index 0000000..cb4d36b --- /dev/null +++ b/json/permission.json @@ -0,0 +1,21 @@ +{ + "tblname": "permission", + "uitype":"tree", + "title":"权限", + "params":{ + "idField":"id", + "textField":"path", + "sortby":"path", + "editable":true, + "browserfields":{ + "alters":{} + }, + "edit_exclouded_fields":[], + "parentField":"parentid", + "toolbar":{ + }, + "binds":[ + ] + } + +} diff --git a/json/role.json b/json/role.json new file mode 100644 index 0000000..3c52962 --- /dev/null +++ b/json/role.json @@ -0,0 +1,31 @@ +{ + "models_dir": "${HOME}$/py/rbac/models", + "output_dir": "${HOME}$/py/sage/wwwroot/_a/role", + "dbname": "sage", + "tblname": "role", + "title":"角色", + "params": { + "sortby":"name", + "browserfields": { + "exclouded": ["id"], + "cwidth": {} + }, + "editexclouded": [ + "id" + ], + "subtables":[ + { + "field":"roleid", + "title":"角色权限", + "url":"../rolepermission", + "subtable":"rolepermission" + }, + { + "field":"roleid", + "title":"用户", + "url":"../users", + "subtable":"users" + } + ] + } +} diff --git a/json/rolepermission.json b/json/rolepermission.json new file mode 100644 index 0000000..49bf3fd --- /dev/null +++ b/json/rolepermission.json @@ -0,0 +1,26 @@ +{ + "models_dir": "${HOME}$/py/rbac/models", + "output_dir": "${HOME}$/py/sage/wwwroot/_a/rolepermission", + "dbname": "sage", + "tblname": "rolepermission", + "title":"用户", + "params": { + "relation":{ + "outter_field":"permid", + "param_field":"roleid" + }, + "noedit":true, + "browserfields": { + "exclouded": ["id", "roleid"], + "alters":{ + "permid":{ + "cwidth":60 + } + } + }, + "editexclouded": [ + "id", "roleid" + ], + "record_toolbar": null + } +} diff --git a/json/userapp.json b/json/userapp.json new file mode 100644 index 0000000..a2da0c7 --- /dev/null +++ b/json/userapp.json @@ -0,0 +1,16 @@ +{ + "tblname": "userapp", + "title":"用户", + "params": { + "confidential_fields":["apikey"], + "sortby":"appname", + "browserfields": { + "exclouded": ["id", "userid"], + "alters": {} + }, + "editexclouded": [ + "id", "userid" + ], + "record_toolbar": null + } +} diff --git a/json/userdepartment.json b/json/userdepartment.json new file mode 100644 index 0000000..91f409e --- /dev/null +++ b/json/userdepartment.json @@ -0,0 +1,17 @@ +{ + "models_dir": "${HOME}$/py/rbac/models", + "output_dir": "${HOME}$/py/sage/wwwroot/_a/userdepartment", + "dbname": "sage", + "tblname": "userdepartment", + "title":"用户", + "params": { + "browserfields": { + "exclouded": ["id", "userid"], + "cwidth": {} + }, + "editexclouded": [ + "id", "userid" + ], + "record_toolbar": null + } +} diff --git a/json/userrole.json b/json/userrole.json new file mode 100644 index 0000000..4363c3e --- /dev/null +++ b/json/userrole.json @@ -0,0 +1,14 @@ +{ + "tblname": "userrole", + "title":"用户角色管理", + "params": { + "browserfields": { + "exclouded": ["id", "orgid"], + "alters": {} + }, + "editexclouded": [ + "id", + "userid" + ] + } +} diff --git a/json/users.json b/json/users.json new file mode 100644 index 0000000..1af9cbd --- /dev/null +++ b/json/users.json @@ -0,0 +1,28 @@ +{ + "tblname": "users", + "title":"用户", + "params": { + "sortby":"username", + "confidential_fields":["password"], + "logined_userorgid":"orgid", + "browserfields": { + "exclouded": ["id", "password", "orgid", "nick_name" ], + "cwidth": {} + }, + "editexclouded": [ + "id", "nick_name", "orgid" + ], + "subtables": [ + { + "field":"userid", + "title":"用户角色", + "subtable":"userrole" + }, + { + "field":"userid", + "title":"APIKEY", + "subtable":"userapp" + } + ] + } +} diff --git a/models/audit_log.xlsx b/models/audit_log.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..144bc7a359cef64fd782b352e458b996c05a4d9c GIT binary patch literal 18421 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 UnDy756%hyq5cHVi`_=mY0Ji}dRR910 literal 0 HcmV?d00001 diff --git a/models/organization.xlsx b/models/organization.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..b43bd8b2c7c2e41d65bb22bea5544160a7c34790 GIT binary patch literal 16526 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 literal 0 HcmV?d00001 diff --git a/models/orgtypes.xlsx b/models/orgtypes.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..d77a47a274a3781a5564eb05d0cbe2cfc46ac8aa GIT binary patch literal 16581 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 AWdHyG literal 0 HcmV?d00001 diff --git a/models/permission.xlsx b/models/permission.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..4dcb8390ddd4f4f94a03cb056dd281be1468142a GIT binary patch literal 16614 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 T6#)W*0{rO*s&^BfUq}BBF1lp% literal 0 HcmV?d00001 diff --git a/models/rolepermission.xlsx b/models/rolepermission.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..ee2ff14d03afce5120c98bf6ddd075ee636d20f1 GIT binary patch literal 16512 zcmeHuWmFy8mNo7c+@0X=65QRL;O_431PJaf!QDN$TX2Wq?rtB+?e5&iyKju|e|Isc zLs7NoIj3sQwbx!ED*+6G3;+fI0RR9%0ANxo_Bjd=0D%1M5D5SRNJGHd%E8deL0i$) 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 m_ru>?q$}nFBz4`{{i%@A;g+j{YBEX_VUl literal 0 HcmV?d00001 diff --git a/models/userapp.xlsx b/models/userapp.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..c03a1bb9129d456b5f78e8fa14e21eaf9876b98b GIT binary patch literal 16578 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 literal 0 HcmV?d00001 diff --git a/models/userdepartment.xlsx b/models/userdepartment.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..0e4df52b8f66f1a9749493fff04f08124f8e2c31 GIT binary patch literal 18256 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 S0t5sV_!$5aCr$!??EQaOnH0?c literal 0 HcmV?d00001 diff --git a/models/userrole.xlsx b/models/userrole.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..4d984588a7dd8c19740a6ac9ca7fd0de7d3b7101 GIT binary patch literal 16458 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 literal 0 HcmV?d00001 diff --git a/models/users.xlsx b/models/users.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..5b5cc0e4538f704c7db60f611d8cb80298551369 GIT binary patch literal 17888 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 literal 0 HcmV?d00001 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..59514a1 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,4 @@ +[build-system] +requires = ["setuptools>=61", "wheel"] +build-backend = "setuptools.build_meta" + diff --git a/rbac/README.md b/rbac/README.md new file mode 100644 index 0000000..e69de29 diff --git a/rbac/__init__.py b/rbac/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/rbac/audit_log.py b/rbac/audit_log.py new file mode 100644 index 0000000..7c20d03 --- /dev/null +++ b/rbac/audit_log.py @@ -0,0 +1,9 @@ +from sqlor.dbpools import DBPools +from appPublic.dictObject import DictObject +from appPublic.uniqueID import getID +import json + +def write_audit_log(sor, request): + id = getID() + params_kw = get_params_kw(request) + diff --git a/rbac/check_perm.py b/rbac/check_perm.py new file mode 100644 index 0000000..c0d9ab0 --- /dev/null +++ b/rbac/check_perm.py @@ -0,0 +1,176 @@ +import time + +from aiohttp import BasicAuth +from sqlor.dbpools import DBPools +from appPublic.registerfunction import RegisterFunction +from appPublic.rc4 import password, unpassword +from appPublic.jsonConfig import getConfig +from appPublic.log import debug, exception +from appPublic.dictObject import DictObject +from appPublic.timeUtils import curDateString +from appPublic.uniqueID import getID +from ahserver.auth_api import AuthAPI, user_login +from ahserver.globalEnv import password_encode +from ahserver.serverenv import ServerEnv, get_serverenv, set_serverenv + +async def get_user_roles(userid): + sql = "select concat(b.orgtypeid, '.', b.name) as name from userrole a, role b where a.userid=${userid}$ and a.roleid = b.id" + db = DBPools() + roles = [] + dbname = await get_dbname() + async with db.sqlorContext(dbname) as sor: + recs = await sor.sqlExe(sql, {'userid':userid}) + if len(recs) < 1: + return roles + for r in recs: + roles.append(r.name) + return roles + +async def create_org(sor, ns, orgtypes=[]): + await sor.C('organization', ns) + if orgtypes == []: + orgtypes = ['customer'] + if 'customer' not in orgtypes: + orgtypes.append('customer') + for ot in orgtypes: + otns = { + 'id':getID(), + 'orgid':ns.id, + 'orgtypeid':ot + } + await sor.C('orgtypes', otns) + +async def create_user(sor, ns, roles=[]): + """ + role format: + { + orgtypeid: rr, + roles: ['ee', 'bb'] + } + """ + await sor.C('users', ns) + if roles == []: + roles = [ + { + 'orgtypeid': 'customer', + 'roles': [ 'customer'] + } + ] + for rt in roles: + sql = "select * from role where orgtypeid = ${otid}$ and name in ${roles}$)" + recs = await sor.sqlExe(sql, { + 'otid': rt['orgtypeid'], + 'roles': rt['roles'] + }) + for r in recs: + await sor.C('userrole', { + 'id':getID(), + 'userid':ns.id, + 'roleid':r.id + }) + +async def register_user(sor, ns): + if ns.password != ns.cfm_password: + debug('password not match') + return False + ns.password = password_encode(ns.password) + id = getID() + ns.id = id + ns.orgid = id + ns1 = DictObject(id=id, orgname=ns.username) + await create_org(sor, ns1) + await create_user(sor, ns) + return id + +async def get_dbname(): + rf = RegisterFunction() + dbname = await rf.exe('get_module_dbname', 'rbac') + return dbname + +async def checkUserPassword(request, username, password): + db = DBPools() + dbname = await get_dbname() + async with db.sqlorContext(dbname) as sor: + sql = "select * from users where username=${username}$ and password=${password}$" + recs = await sor.sqlExe(sql, {'username':username, 'password':password}) + if len(recs) < 1: + return False + await user_login(request, recs[0].id, + username=recs[0].username, + userorgid=recs[0].orgid) + return True + return False + +async def basic_auth(sor, auth): + auther = BasicAuth('x') + m = auther.decode(auth) + username = m.login + password = password_encode(m.password) + sql = "select * from users where username=${username}$ and password=${password}$" + recs = await sor.sqlExe(sql, {'username':username,'password':password}) + if len(recs) < 1: + return None + return recs[0].id + +async def bearer_auth(sor, auth): + # apikey = get_apikey_from_token(auth[7:]) + apikey = auth[7:] + if apikey is None: + return None + sql = "select * from userapp where apikey=${apikey}$ and expired_date > ${today}$" + recs = await sor.sqlExe(sql, {"apikey":apikey, 'today': curDateString()}) + if len(recs) < 1: + return None + return recs[0].userid + +async def getAuthenticationUserid(sor, request): + auth = request.headers.get('Authentication') + if auth is None: + return None + for h,f in registered_auth_methods.items(): + if auth.startswith(h): + return await f(auth) + return None + +async def objcheckperm(obj, request, userid, path): + debug(f'check permission: {userid=}, {path=}') + sql = """select distinct a.*, c.userid from +(select id, path from permission where path=${path}$) a +right join + rolepermission b on a.id = b.permid +right join userrole c on b.roleid = c.roleid +where c.userid = ${userid}$ +""" + + dbname = await get_dbname() + db = DBPools() + async with db.sqlorContext(dbname) as sor: + if userid is None: + userid = await getAuthenticationUserid(sor, request) + perms = await sor.R('permission', {'path':path}) + if len(perms) == 0: + debug(f'{path=} not found in permission, can access') + return True + if userid is None: + debug(f'{userid=} is None, can not access {path=}') + return False + + recs = await sor.sqlExe(sql, {'path':path, 'userid':userid}) + for r in recs: + id = r['id'] + if id is not None: + debug(f'{userid=} can access {path=}') + return True + debug(f'{userid=} has not permission to call {path=}') + return False + debug(f'error happened {userid}, {path}') + return False + +registered_auth_methods = { + "Basic ": basic_auth, + "Bearer ": bearer_auth +} + +def register_auth_method(heading, func): + registered_auth_methods[heading] = func + diff --git a/rbac/init.py b/rbac/init.py new file mode 100644 index 0000000..0b9a4ff --- /dev/null +++ b/rbac/init.py @@ -0,0 +1,18 @@ +from ahserver.auth_api import AuthAPI +from ahserver.serverenv import ServerEnv +from rbac.check_perm import objcheckperm, get_user_roles, checkUserPassword, register_user, register_auth_method, create_org, create_user +from rbac.set_role_perms import set_role_perm, set_role_perms + +def load_rbac(): + AuthAPI.checkUserPermission = objcheckperm + env = ServerEnv() + env.create_org = create_org + env.create_user = create_user + env.get_user_roles = get_user_roles + env.check_user_password = checkUserPassword + env.register_user = register_user + env.set_role_perm = set_role_perm + env.set_role_perms = set_role_perms + env.register_auth_method = register_auth_method + + diff --git a/rbac/set_role_perms.py b/rbac/set_role_perms.py new file mode 100644 index 0000000..48cacac --- /dev/null +++ b/rbac/set_role_perms.py @@ -0,0 +1,71 @@ +import sys +import os +import asyncio +from sqlor.dbpools import DBPools +from appPublic.jsonConfig import getConfig +from appPublic.uniqueID import getID +from appPublic.asynciorun import run + +async def set_role_perm(dbname, module, orgtype, role, tblname): + db = DBPools() + async with db.sqlorContext(dbname) as sor: + if '/' in dbname: + path = [f'/{module}/{dbname}'] + else: + paths = [ + f'/{module}/{tblname}', + f'/{module}/{tblname}/index.ui', + f'/{module}/{tblname}/get_{tblname}.dspy', + f'/{module}/{tblname}/add_{tblname}.dspy', + f'/{module}/{tblname}/delete_{tblname}.dspy', + f'/{module}/{tblname}/update_{tblname}.dspy' + ] + for pat in paths: + recs = await sor.R('permission', {'path': pat}) + if len(recs) == 0: + permid = getID() + await sor.C('permission', {'id':permid, 'path':pat}) + else: + permid = recs[0].id + recs = await sor.R('role', {'orgtypeid':orgtype, 'name':role}) + if len(recs) == 0: + roleid = getID() + await sor.C('role', { + 'id':roleid, + 'name':role, + 'orgtypeid':orgtype + }) + else: + roleid = recs[0].id + await sor.C('rolepermission', { + 'id':getID(), + 'roleid':roleid, + 'permid':permid + }) + print(f'{orgtype=}, {role=}, {tblname=} permission configured') + +async def set_role_perms(dbname, module, orgtype, role, items): + for tblname in items: + await set_role_perm(dbname, module, orgtype, role, tblname) + +if __name__ == '__main__': + async def main(): + if len(sys.argv) < 6: + print(f'{sys.argv[0]} dbname module orgtype role tblname ...\n') + sys.exit(1) + dbname = sys.argv[1] + module = sys.argv[2] + orgtype = sys.argv[3] + role = sys.argv[4] + await set_role_perms(dbname, module, orgtype, role, sys.argv[5:]) + + def run(coro): + p = '.' + config = getConfig(p, {'woridir':p}) + DBPools(config.databases) + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + loop.run_until_complete(coro()) + + run(main) + diff --git a/rbac/version.py b/rbac/version.py new file mode 100644 index 0000000..b794fd4 --- /dev/null +++ b/rbac/version.py @@ -0,0 +1 @@ +__version__ = '0.1.0' diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..34df19f --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +aiohttp +PyJWT diff --git a/script/init.py b/script/init.py new file mode 100644 index 0000000..17c2340 --- /dev/null +++ b/script/init.py @@ -0,0 +1,191 @@ +import os +import sys +import argparse + +from traceback import format_exc +import asyncio +from appPublic.uniqueID import getID +from appPublic.timeUtils import days_between, strdate_add +from appPublic.jsonConfig import getConfig +from random import randint, random +from appPublic.folderUtils import listFile +from appPublic.registerfunction import RegisterFunction + +from sqlor.dbpools import DBPools +from rbac.check_perm import mypassword +databases = { + "sage":{ + "driver":"aiomysql", + "async_mode":True, + "coding":"utf8", + "maxconn":100, + "dbname":"sage", + "kwargs":{ + "user":"test", + "db":"sage", + "password":"QUZVcXg5V1p1STMybG5Ia6mX9D0v7+g=", + "host":"localhost" + } + } +} + +async def insert_perm(path): + db = DBPools() + async with db.sqlorContext('sage') as sor: + ns = { + 'id':getID(), + 'path':path + } + await sor.C('permission', ns) + +av_folders = [ + '/index.ui', + '/user.ui', + '/menu.ui', + '/get_code.dspy', + '/top.ui', + '/center', + '/bottom.ui', + '/app_panel.ui', + '/bricks', + '/imgs', + '/login.ui', + '/gen_code.dspy', + '/code_login.dspy', + '/up_login.dspy', + '/wechat_login.ui', + '/public', + '/debug/index.dspy', + '/rbac/userpassword_login.ui', + '/rbac/userpassword_login.dspy', + '/tmp' +] + + +orgtypes = ['owner', 'reseller'] +type_roles = { + 'owner':['superuser', 'customer'], + 'reseller':['admin','operator', 'sale', 'maintainer', 'accountant'], + 'customer':['admin', 'customer'] +} + +rbac_tables = [ + "organization", "orgtypes", "role", "users", "userrole", "rolepermission", "permission", + "userapp", "userdepartment" ] + +role_perms = { + 'superuser': [ + '/get_code.dspy', + '/rbac/add_adminuser.ui', + '/rbac/add_adminuser.dspy', + '/rbac/role/index.ui', + '/rbac/role/get_role.dspy', + '/rbac/role/update_role.dspy', + '/rbac/role/delete_role.dspy', + '/rbac/role/add_role.dspy', + '/rbac/permission/add_permission.dspy', + '/rbac/permission/delete_permission.dspy', + '/rbac/permission/update_permission.dspy', + '/rbac/permission/get_permission.dspy', + '/rbac/permission/index.ui', + '/rbac/rolepermission/add_rolepermission.dspy', + '/rbac/rolepermission/delete_rolepermission.dspy', + '/rbac/rolepermission/update_rolepermission.dspy', + '/rbac/rolepermission/get_rolepermission.dspy', + '/rbac/rolepermission/index.ui' + ], + 'admin':[ + '/rbac/users/add_users.dspy', + '/rbac/users/delete_users.dspy', + '/rbac/users/update_users.dspy', + '/rbac/users/get_users.dspy', + '/rbac/users/index.ui', + '/rbac/userrole/add_userrole.dspy', + '/rbac/userrole/delete_userrole.dspy', + '/rbac/userrole/update_userrole.dspy', + '/rbac/userrole/get_userrole.dspy', + '/rbac/userrole/index.ui' + ] +} + +async def init_perms(): + db = DBPools() + async with db.sqlorContext('sage') as sor: + for rn, pths in role_perms.items(): + roles = await sor.sqlExe('select * from role where name=${rn}$', {'rn':rn}) + if len(roles) == 0: + print(f'{rn=} not exists') + continue + for p in pths: + print(f'{rn=} set {p=}') + perms = await sor.sqlExe('select * from permission where path=${p}$', {'p':p}) + if len(perms): + await sor.C('rolepermission', {'id':getID(), + 'roleid':roles[0].id, + 'permid':perms[0].id}) + else: + print(f'{p} is not exists') + +async def init_rbac(passwd): + db = DBPools() + async with db.sqlorContext('sage') as sor: + recs = await sor.R('organization', {'id':'0'}) + if len(recs) > 0: + return + await sor.C('organization',{'id':'0'}) + await sor.C('orgtypes', {'id':getID(), 'orgid':'0', 'orgtypeid':'owner'}) + await sor.C('orgtypes', {'id':getID(), 'orgid':'0', 'orgtypeid':'reseller'}) + roleid = '' + for orgtype, roles in type_roles.items(): + for r in roles: + id = getID() + await sor.C('role', {'id':id,'orgtypeid':orgtype,'name':r}) + if orgtype == 'owner' and r == 'superuser': + roleid = id + ns = { + 'id':getID(), + 'username':'superuser', + 'orgid':'0', + 'password':passwd + } + await sor.C('users', ns) + ns1 = { + 'id':getID(), + 'userid':ns['id'], + 'roleid':roleid + } + await sor.C('userrole', ns1) + +async def add_permissions(workdir): + root = os.path.join(workdir, 'wwwroot') + for f in listFile(root, rescursive=True): + cnt = len(root) + pth = f[cnt:] + print(f'{f=},{root=},{pth=}, {cnt=}') + act = True + for f in av_folders: + if pth.startswith(f): + act = False + break + if act: + await insert_perm(pth) + +async def main(workdir, passwd): + await add_permissions(workdir) + await init_rbac(passwd) + await init_perms() + +if __name__ == '__main__': + parser = argparse.ArgumentParser(prog='RBAC init') + parser.add_argument('-w', '--workdir') + parser.add_argument('password') + args = parser.parse_args() + if args.password is None: + parser.usage() + sys.exit(1) + print(f'{args=}') + config = getConfig(args.workdir, {'workdir':args.workdir}) + supassword = mypassword(args.password) + DBPools(config.databases) + asyncio.get_event_loop().run_until_complete(main(args.workdir, supassword)) + diff --git a/script/setroleperms.sh b/script/setroleperms.sh new file mode 100644 index 0000000..5e435d3 --- /dev/null +++ b/script/setroleperms.sh @@ -0,0 +1,7 @@ +#!/usr/bin/bash + +python -m rbac.set_role_perms sage rbac owner superuser role permission rolepermission user user role organizationorgtypes +python -m rbac.set_role_perms sage rbac owner admin user userrole +python -m rbac.set_role_perms sage rbac customer admin user userrole +python -m rbac.set_role_perms sage rbac reseller admin user userrole + diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..db6fe8b --- /dev/null +++ b/setup.cfg @@ -0,0 +1,16 @@ +[metadata] +name=rbac +version = 1.0.0 +description = a RBAC user authenticate module +author = "yu moqing" +author_email = "yumoqing@gmail.com" +readme = "README.md" +license = "MIT" +[options] +packages = find: +requires_python = ">=3.8" +install_requires = + apppublic + sqlor + ahserver + diff --git a/wwwroot/add_adminuser.dspy b/wwwroot/add_adminuser.dspy new file mode 100644 index 0000000..61af0de --- /dev/null +++ b/wwwroot/add_adminuser.dspy @@ -0,0 +1,23 @@ +if params_kw.get('password') != params_kw.get('chkpassword'): + return Error(title='add user error', message='password not match') + +ns = params_kw.copy() +ns['id'] = uuid() +await rfexe('passowrd', ns) +user_orgid = await get_userorgid() +ns['orgid'] = user_orgid +dbname = await rfexe('get_module_dbname','rbac') +db = DBPools() +debug(f'{dbname=}') +async with db.sqlorContext(dbname) as sor: + await sor.C('users',ns.copy()) + uid = ns['id'] + ns = { + 'id':uuid(), + 'userid':uid, + 'roleid':'admin' + } + await sor.C('userrole', ns.copy()) + return UiMesage(title='Success', message='admin user added') +return UiError(title='Error', message='Error happened when add admin user') + diff --git a/wwwroot/add_adminuser.ui b/wwwroot/add_adminuser.ui new file mode 100644 index 0000000..7e58462 --- /dev/null +++ b/wwwroot/add_adminuser.ui @@ -0,0 +1,36 @@ +{ + "widgettype":"ModalForm", + "options":{ + "cwidth":20, + "cheight":30, + "title":"增加管理员", + "fields":[ + { + "name":"username", + "label":"用户名", + "uitype":"str" + }, + { + "name":"password", + "label":"密码", + "uitype":"password" + }, + { + "name":"chkpassword", + "label":"确认密码", + "uitype":"password" + } + ] + }, + "binds":[ + { + "wid":"self", + "event":"submit", + "actiontype":"urlwidget", + "target":"self", + "options":{ + "url":"{{entire_url('./add_adminuser.dspy')}}" + } + } + ] +} diff --git a/wwwroot/add_provider.dspy b/wwwroot/add_provider.dspy new file mode 100644 index 0000000..7a44acc --- /dev/null +++ b/wwwroot/add_provider.dspy @@ -0,0 +1,36 @@ +ns = params_kw.copy() +id = params_kw.id +if not id or len(id) > 32: + id = uuid() +ns['id'] = id +db = DBPools() +dbname = get_module_dbname('rbac') +async with db.sqlorContext(dbname) as sor: + ownerid = await get_userorgid() + await create_org(sor, ns, orgtypes=['customer', 'provider']) + if openCustomerAccounts: + await openCustomerAccounts(sor, ownerid, orgid) + if openProviderAccounts: + await openProviderAccounts(sor, ownerid, id) + return { + "widgettype":"Message", + "options":{ + "user_data":ns, + "cwidth":16, + "cheight":9, + "title":"Add Success", + "timeout":3, + "message":"ok" + } + } + +return { + "widgettype":"Error", + "options":{ + "title":"Add Error", + "cwidth":16, + "cheight":9, + "timeout":3, + "message":"failed" + } +} diff --git a/wwwroot/add_reseller.dspy b/wwwroot/add_reseller.dspy new file mode 100644 index 0000000..cd8de33 --- /dev/null +++ b/wwwroot/add_reseller.dspy @@ -0,0 +1,26 @@ +ns = params_kw.copy() +id = params_kw.id +if not id or len(id) > 32: + id = uuid() +ns['id'] = id +db = DBPools() +dbname = get_module_dbname('rbac') +async with db.sqlorContext(dbname) as sor: + ownerid = await get_userorgid() + await create_org(sor, ns, orgtypes=['customer', 'reseller']) + if openCustomerAccounts: + await openCustomerAccounts(sor, ownerid, orgid) + if openResellerAccounts: + await openResellerAccounts(sor, ownerid, id) + return UiMessage(title="Success", message="add reseller success") + +return { + "widgettype":"Error", + "options":{ + "title":"Add reseller Error", + "cwidth":16, + "cheight":9, + "timeout":3, + "message":"failed" + } +} diff --git a/wwwroot/admin_menu.ui b/wwwroot/admin_menu.ui new file mode 100644 index 0000000..029a5f6 --- /dev/null +++ b/wwwroot/admin_menu.ui @@ -0,0 +1,7 @@ +[ + { + "name":"users", + "label":"用户管理" + "url":"{{entire_url('/rbac/users')}}" + } +] diff --git a/wwwroot/get_provider.dspy b/wwwroot/get_provider.dspy new file mode 100644 index 0000000..3d61294 --- /dev/null +++ b/wwwroot/get_provider.dspy @@ -0,0 +1,151 @@ +ns = params_kw.copy() +debug(f'get_organization.dspy:{ns=}') +if not ns.get('page'): + ns['page'] = 1 +if not ns.get('sort'): + ns['sort'] = 'orgname' + +sql = '''select a.*, b.province_id_text, c.city_id_text, d.distinct_id_text +from (select x.* from (select * from organization where 1=1 [[filterstr]]) x, orgtypes y where x.id = y.orgid and y.orgtypeid='provider') a left join (select k as province_id, + v as province_id_text from appcodes_kv where parentid='chnaddr') b on a.province_id = b.province_id left join (select k as city_id, + v as city_id_text from appcodes_kv where parentid='city') c on a.city_id = c.city_id left join (select k as distinct_id, + v as distinct_id_text from appcodes_kv where parentid='distinct') d on a.distinct_id = d.distinct_id''' + +filterjson = params_kw.get('data_filter') +if not filterjson: + fields = [ f['name'] for f in [ + { + "name": "id", + "title": "机构编码", + "type": "str", + "length": 32 + }, + { + "name": "orgname", + "title": "机构名称", + "type": "str", + "length": 100 + }, + { + "name": "alias_name", + "title": "机构别名", + "type": "str", + "length": 100 + }, + { + "name": "contactor", + "title": "联系人", + "type": "str", + "length": 32 + }, + { + "name": "contactor_phone", + "title": "联系人电话", + "type": "str", + "length": 100 + }, + { + "name": "province_id", + "title": "所在省id", + "type": "str", + "length": 32 + }, + { + "name": "city_id", + "title": "所在城市id", + "type": "str", + "length": 32 + }, + { + "name": "distinct_id", + "title": "所在地区id", + "type": "str", + "length": 32 + }, + { + "name": "emailaddress", + "title": "邮箱", + "type": "str", + "length": 256 + }, + { + "name": "address", + "title": "地址", + "type": "str", + "length": 400 + }, + { + "name": "main_business", + "title": "主营业务描述", + "type": "str", + "length": 1000 + }, + { + "name": "orgcode", + "title": "组织结构代码", + "type": "str", + "length": 100, + "comments": "个人客户存身份证" + }, + { + "name": "license_img", + "title": "营业执照", + "type": "str", + "length": 400, + "comments": "个人客户存身份证照片" + }, + { + "name": "id_img", + "title": "身份证", + "type": "str", + "length": 400, + "comments": "个人客户存身份证背面照片" + }, + { + "name": "parentid", + "title": "父机构id", + "type": "str", + "length": 32 + }, + { + "name": "org_type", + "title": "机构类型", + "type": "str", + "length": 32, + "comments": "0:业主机构;1:分销商;2:公司客户;3:个人客户;4:供应商" + }, + { + "name": "accountid", + "title": "账号", + "type": "str", + "length": 32 + } +] ] + filterjson = default_filterjson(fields, ns) +filterdic = ns.copy() +filterdic['filterstr'] = '' +filterdic['userorgid'] = '${userorgid}$' +filterdic['userid'] = '${userid}$' +if filterjson: + dbf = DBFilter(filterjson) + conds = dbf.gen(ns) + if conds: + ns.update(dbf.consts) + conds = f' and {conds}' + filterdic['filterstr'] = conds +ac = ArgsConvert('[[', ']]') +vars = ac.findAllVariables(sql) +NameSpace = {v:'${' + v + '}$' for v in vars if v != 'filterstr' } +filterdic.update(NameSpace) +sql = ac.convert(sql, filterdic) + +debug(f'{sql=}') +db = DBPools() +dbname = get_module_dbname('rbac') +async with db.sqlorContext(dbname) as sor: + r = await sor.sqlPaging(sql, ns) + return r +return { + "total":0, + "rows":[] +} diff --git a/wwwroot/get_reseller.dspy b/wwwroot/get_reseller.dspy new file mode 100644 index 0000000..898ec19 --- /dev/null +++ b/wwwroot/get_reseller.dspy @@ -0,0 +1,151 @@ +ns = params_kw.copy() +debug(f'get_organization.dspy:{ns=}') +if not ns.get('page'): + ns['page'] = 1 +if not ns.get('sort'): + ns['sort'] = 'orgname' + +sql = '''select a.*, b.province_id_text, c.city_id_text, d.distinct_id_text +from (select x.* from (select * from organization where 1=1 [[filterstr]]) x, orgtypes y where x.id = y.orgid and y.orgtypeid='reseller') a left join (select k as province_id, + v as province_id_text from appcodes_kv where parentid='chnaddr') b on a.province_id = b.province_id left join (select k as city_id, + v as city_id_text from appcodes_kv where parentid='city') c on a.city_id = c.city_id left join (select k as distinct_id, + v as distinct_id_text from appcodes_kv where parentid='distinct') d on a.distinct_id = d.distinct_id''' + +filterjson = params_kw.get('data_filter') +if not filterjson: + fields = [ f['name'] for f in [ + { + "name": "id", + "title": "机构编码", + "type": "str", + "length": 32 + }, + { + "name": "orgname", + "title": "机构名称", + "type": "str", + "length": 100 + }, + { + "name": "alias_name", + "title": "机构别名", + "type": "str", + "length": 100 + }, + { + "name": "contactor", + "title": "联系人", + "type": "str", + "length": 32 + }, + { + "name": "contactor_phone", + "title": "联系人电话", + "type": "str", + "length": 100 + }, + { + "name": "province_id", + "title": "所在省id", + "type": "str", + "length": 32 + }, + { + "name": "city_id", + "title": "所在城市id", + "type": "str", + "length": 32 + }, + { + "name": "distinct_id", + "title": "所在地区id", + "type": "str", + "length": 32 + }, + { + "name": "emailaddress", + "title": "邮箱", + "type": "str", + "length": 256 + }, + { + "name": "address", + "title": "地址", + "type": "str", + "length": 400 + }, + { + "name": "main_business", + "title": "主营业务描述", + "type": "str", + "length": 1000 + }, + { + "name": "orgcode", + "title": "组织结构代码", + "type": "str", + "length": 100, + "comments": "个人客户存身份证" + }, + { + "name": "license_img", + "title": "营业执照", + "type": "str", + "length": 400, + "comments": "个人客户存身份证照片" + }, + { + "name": "id_img", + "title": "身份证", + "type": "str", + "length": 400, + "comments": "个人客户存身份证背面照片" + }, + { + "name": "parentid", + "title": "父机构id", + "type": "str", + "length": 32 + }, + { + "name": "org_type", + "title": "机构类型", + "type": "str", + "length": 32, + "comments": "0:业主机构;1:分销商;2:公司客户;3:个人客户;4:供应商" + }, + { + "name": "accountid", + "title": "账号", + "type": "str", + "length": 32 + } +] ] + filterjson = default_filterjson(fields, ns) +filterdic = ns.copy() +filterdic['filterstr'] = '' +filterdic['userorgid'] = '${userorgid}$' +filterdic['userid'] = '${userid}$' +if filterjson: + dbf = DBFilter(filterjson) + conds = dbf.gen(ns) + if conds: + ns.update(dbf.consts) + conds = f' and {conds}' + filterdic['filterstr'] = conds +ac = ArgsConvert('[[', ']]') +vars = ac.findAllVariables(sql) +NameSpace = {v:'${' + v + '}$' for v in vars if v != 'filterstr' } +filterdic.update(NameSpace) +sql = ac.convert(sql, filterdic) + +debug(f'{sql=}') +db = DBPools() +dbname = get_module_dbname('rbac') +async with db.sqlorContext(dbname) as sor: + r = await sor.sqlPaging(sql, ns) + return r +return { + "total":0, + "rows":[] +} diff --git a/wwwroot/user/login.ui b/wwwroot/user/login.ui new file mode 100644 index 0000000..1ce2ddc --- /dev/null +++ b/wwwroot/user/login.ui @@ -0,0 +1,121 @@ +{ + "id":"login_window", + "widgettype":"PopupWindow", + "options":{ + "auto_open":true, + "anthor":"cc", + "cwidth":22, + "cheight":19 + }, + "subwidgets":[ +{ + "widgettype":"TabPanel", + "options":{ + "tab_wide":"auto", + "height":"100%", + "width":"100%", + "tab_pos":"top", + "items":[ + { + "name":"userpasswd", + "label":"用户密码", + "content":{ + "widgettype":"Form", + "options":{ + "cols":1, + "fields":[ + { + "name":"username", + "label":"用户名", + "uitype":"str" + }, + { + "name":"password", + "label":"密码", + "uitype":"password" + } + ] + }, + "binds":[ + { + "wid":"self", + "event":"submit", + "actiontype":"urlwidget", + "target":"self", + "options":{ + "method":"POST", + "url":"{{entire_url('up_login.dspy')}}" + } + } + ] + } + }, + { + "name":"checkcode", + "label":"手机验证码", + "content":{ + "widgettype":"Form", + "options":{ + "toolbar":{ + "tools":[ + { + "name":"gen_code", + "label":"发送验证码" + } + ] + }, + "description":"限中国国内手机", + "fields":[ + { + "name":"cell_no", + "label":"手机号", + "uitype":"str" + },{ + "name":"codeid", + "uitype":"hide", + "value":"{{uuid()}}" + },{ + "name":"check_code", + "uitype":"str" + } + ] + }, + "binds":[ + { + "wid":"self", + "event":"gen_code", + "actiontype":"urlwidget", + "datawidget":"self", + "datamethod":"getValue", + "target":"self", + "options":{ + "url":"{{entire_url('gen_code.dspy')}}" + } + }, + { + "wid":"self", + "event":"submit", + "actiontype":"urlwidget", + "target":"self", + "options":{ + "url":"{{entire_url('code_login.dspy')}}" + } + } + ] + } + }, + { + "name":"wechat", + "label":"微信", + "content":{ + "widgettype":"urlwidget", + "options":{ + "url":"{{entire_url('wechat_login.ui')}}" + } + } + } + ] + } +} + ] +} diff --git a/wwwroot/user/logout.dspy b/wwwroot/user/logout.dspy new file mode 100644 index 0000000..baf169d --- /dev/null +++ b/wwwroot/user/logout.dspy @@ -0,0 +1,23 @@ +await forget_user() +return { + "widgettype":"Message", + "options":{ + "title":"Message", + "message":"logout success", + "auto_open":true, + "anthor":"cc", + "timeout":3 + }, + "binds":[ + { + "wid":"self", + "event":"dismissed", + "actiontype":"urlwidget", + "target":"window", + "options":{ + "url":entire_url('/index.ui') + } + } + ] +} + diff --git a/wwwroot/user/myrole.ui b/wwwroot/user/myrole.ui new file mode 100644 index 0000000..e661030 --- /dev/null +++ b/wwwroot/user/myrole.ui @@ -0,0 +1,27 @@ +{ + "widgettype":"VBox", + "options":{ + "height":"100%" + }, + "subwidgets":[ + { + "widgettype":"Title4", + "options":{ + "otext":"我的角色", + "i18n":true + } + }, +{% set roles = get_user_roles(get_user()) %} +{% for role in roles %} + { + "widgettype":"Text", + "options":{ + "text":"{{role}}" + } + }, +{% endfor %} + { + "oops":true + } + ] +} diff --git a/wwwroot/user/register.dspy b/wwwroot/user/register.dspy new file mode 100644 index 0000000..25fd15d --- /dev/null +++ b/wwwroot/user/register.dspy @@ -0,0 +1,10 @@ +debug(f'{params_kw=}') +db = DBPools() +dbname = await rfexe('get_module_dbname', 'sage') +async with db.sqlorContext(dbname) as sor: + orgid = await register_user(sor, params_kw) + if get_owner_orgid and openCustomerAccounts: + ownerid = await get_owner_orgid(sor, orgid) + await openCustomerAccounts(sor, ownerid, orgid) + return UiMessage(title="Success", message="register success") +return UiError(title='Error', message="register failed") diff --git a/wwwroot/user/register.ui b/wwwroot/user/register.ui new file mode 100644 index 0000000..3a25c1d --- /dev/null +++ b/wwwroot/user/register.ui @@ -0,0 +1,80 @@ +{ + "widgettype":"PopupWindow", + "options":{ + "cwidth":22, + "height":"75%", + "archor":"cc", + "auto_open":true + }, + "subwidgets":[ + { + "widgettype":"Form", + "options":{ + "title":"user register", + "description":"base info we need is username, password and a cell phone number", + + "fields":[ + { + "name": "username", + "title": "\u7528\u6237\u540d", + "type": "str", + "length": 255, + "uitype": "str", + "datatype": "str", + "required":true, + "label": "\u7528\u6237\u540d" + }, + { + "name": "password", + "type": "str", + "length": 255, + "uitype": "password", + "datatype": "str", + "required":true, + "label": "\u5bc6\u7801" + }, + { + "name": "cfm_password", + "type": "str", + "length": 255, + "uitype": "password", + "datatype": "str", + "required":true, + "label": "\u5bc6\u7801" + }, + { + "name": "email", + "title": "\u90ae\u4ef6\u5730\u5740", + "type": "str", + "length": 255, + "uitype": "str", + "datatype": "str", + "required":true, + "label": "\u90ae\u4ef6\u5730\u5740" + }, + { + "name": "mobile", + "title": "\u624b\u673a", + "type": "str", + "length": 255, + "uitype": "str", + "datatype": "str", + "required":true, + "label": "\u624b\u673a" + } + ] + }, + "binds":[ + { + "wid":"self", + "event":"submit", + "actiontype":"urlwidget", + "target":"self", + "options":{ + "url":"{{entire_url('register.dspy')}}" + } + } + ] + } + ] +} diff --git a/wwwroot/user/reset_password/index.ui b/wwwroot/user/reset_password/index.ui new file mode 100644 index 0000000..8cec34a --- /dev/null +++ b/wwwroot/user/reset_password/index.ui @@ -0,0 +1,49 @@ +{ + "widgettype":"PopupWindow", + "options":{ + "cwidth":22, + "height":"75%", + "archor":"cc", + "auto_open":true + }, + "subwidgets":[ + { + "widgettype":"Form", + "options":{ + "title":"Reset Password", + "description":"reset yourself password", + "fields":[ + { + "name": "password", + "type": "str", + "length": 255, + "uitype": "password", + "datatype": "str", + "required":true, + "label": "\u5bc6\u7801" + }, + { + "name": "cfm_password", + "type": "str", + "length": 255, + "uitype": "password", + "datatype": "str", + "required":true, + "label": "\u5bc6\u7801" + } + ] + }, + "binds":[ + { + "wid":"self", + "event":"submit", + "actiontype":"urlwidget", + "target":"self", + "options":{ + "url":"{{entire_url('reset_password.dspy')}}" + } + } + ] + } + ] +} diff --git a/wwwroot/user/reset_password/reset_password.dspy b/wwwroot/user/reset_password/reset_password.dspy new file mode 100644 index 0000000..367b902 --- /dev/null +++ b/wwwroot/user/reset_password/reset_password.dspy @@ -0,0 +1,17 @@ +if params_kw.password != params_kw.cfm_password: + return UiError(title='Error', message='Password not match') + +userid = await get_user() +if userid is None: + return UiError(title='Error', message='You need login first') + +ns = { + 'id':userid, + 'password':params_kw.password +} +db = DBPools() +dbname = await rfexe('get_module_dbname', 'rbac') +async with db.sqlorContext(dbname) as sor: + await sor.U('users', ns) + return UiMessage(title='Success', message='Password reset success') +return UiError(title='Error', message='Reset password failed') diff --git a/wwwroot/user/up_login.dspy b/wwwroot/user/up_login.dspy new file mode 100644 index 0000000..0a7a8eb --- /dev/null +++ b/wwwroot/user/up_login.dspy @@ -0,0 +1,58 @@ + +debug(f'{params_kw=}, {password=}') +ns = { + "username":params_kw.username, + "password":password_encode(params_kw.password) +} + +info(f'{ns=}') +db = DBPools() +dbname = await rfexe('get_module_dbname', 'rbac') +async with db.sqlorContext(dbname) as sor: + r = await sor.sqlExe('select * from users where username=${username}$ and password=${password}$', ns.copy()) + if len(r) == 0: + return { + "widgettype":"Error", + "options":{ + "timeout":3, + "title":"Login Error", + "message":"user name or password error" + } + } + await remember_user(r[0].id, username=r[0].username, userorgid=r[0].orgid) + return { + "widgettype":"Message", + "options":{ + "timeout":3, + "auto_open":true, + "title":"Login", + "message":"Welcome back" + }, + "binds":[ + { + "wid":"self", + "event":"dismissed", + "actiontype":"urlwidget", + "target":"window", + "options":{ + "url":entire_url('/index.ui') + } + }, + { + "wid":"self", + "event":"opened", + "actiontype":"script", + "target":"window.login_window", + "script":"this.destroy()" + } + ] + } +return { + "widgettype":"Error", + "options":{ + "timeout":3, + "title":"Login Error", + "message":"system error" + } +} + diff --git a/wwwroot/user/user.ui b/wwwroot/user/user.ui new file mode 100644 index 0000000..842b715 --- /dev/null +++ b/wwwroot/user/user.ui @@ -0,0 +1,80 @@ +{% if get_user() %} +{ + "widgettype":"HBox", + "options":{ + "css":"clickable", + "tip":"用户功能", + "cwidth":6 + }, + "binds":[ + { + "wid":"self", + "event":"click", + "actiontype":"urlwidget", + "popup_options":{ + "eventpos":true, + "dismiss_events":["command"] + }, + "target":"Popup", + "options":{ + "url":"{{entire_url('user_menu.ui')}}" + } + } + ], + "subwidgets":[ + { + "widgettype":"Svg", + "options":{ + "url":"{{entire_url('/bricks/imgs/user.svg')}}", + "rate":1.5 + } + }, + { + "widgettype":"Text", + "options":{ + "cwidth": 4, + "text":"{{get_username()}}" + } + } + ] +} +{% else %} +{ + "widgettype":"IconBar", + "options":{ + "css":"filler", + "tools":[ + { + "name":"login", + "tip":"点击登录", + "icon":"{{entire_url('/bricks/imgs/login.svg')}}" + }, + { + "name":"register", + "tip":"点击注册", + "icon":"{{entire_url('/bricks/imgs/register.svg')}}" + } + ] + }, + "binds":[ + { + "wid":"self", + "event":"login", + "actiontype":"urlwidget", + "target":"self", + "options":{ + "url":"{{entire_url('login.ui')}}" + } + }, + { + "wid":"self", + "event":"register", + "actiontype":"urlwidget", + "target":"self", + "options":{ + "url":"{{entire_url('register.ui')}}" + } + } + ] +} +{% endif %} diff --git a/wwwroot/user/user_menu.ui b/wwwroot/user/user_menu.ui new file mode 100644 index 0000000..8c0ad70 --- /dev/null +++ b/wwwroot/user/user_menu.ui @@ -0,0 +1,55 @@ +{ + "widgettype":"Menu", + "options":{ + "cwidth":10, + "target":"PopupWindow", + "popup_options":{ + "height":"70%", + "width":"70%" + }, + "items":[ +{% if 'customer.customer' in get_user_roles(get_user()) %} + { + "name":"recharge", + "label":"充值", + "url":"{{entire_url('/platformbiz/recharge.ui')}}" + }, + { + "name":"myacc", + "label":"我的账户", + "url":"{{entire_url('myaccount')}}" + }, + { + "name":"mybill", + "label":"我的账单", + "url":"{{entire_url('mybill')}}" + }, + { + "name":"myorder", + "label":"我的订单", + "url":"{{entire_url('myorder')}}" + }, + { + "name":"myresource", + "label":"我的资源", + "url":"{{entire_url('myresource')}}" + }, +{% endif %} + { + "name":"resetpwd", + "label":"重置密码", + "url":"{{entire_url('reset_password')}}" + }, + { + "name":"myrole", + "label":"我的角色", + "url":"{{entire_url('myrole.ui')}}" + }, + { + "name":"logout", + "label":"签退", + "url":"{{entire_url('logout.dspy')}}" + } + ] + } +} diff --git a/wwwroot/user/user_panel.ui b/wwwroot/user/user_panel.ui new file mode 100644 index 0000000..e80acaa --- /dev/null +++ b/wwwroot/user/user_panel.ui @@ -0,0 +1,15 @@ +{ + "id":"user_panel", + "widgettype":"VBox", + "options":{ + "width":"auto" + }, + "subwidgets":[ + { + "widgettype":"urlwidget", + "options":{ + "url":"{{entire_url('user.ui')}}" + } + } + ] +} diff --git a/wwwroot/user/userapikey/add_userapikey.dspy b/wwwroot/user/userapikey/add_userapikey.dspy new file mode 100644 index 0000000..143f5a2 --- /dev/null +++ b/wwwroot/user/userapikey/add_userapikey.dspy @@ -0,0 +1,25 @@ + +ns = params_kw.copy() +id = params_kw.id +if not id or len(id) > 32: + id = uuid() +ns['id'] = id +db = DBPools() +async with db.sqlorContext('sage') as sor: + r = await sor.C('userapikey', ns.copy()) + return { + "widgettype":"Message", + "options":{ + "user_data":ns, + "title":"Add Success", + "message":"ok" + } + } + +return { + "widgettype":"Error", + "options":{ + "title":"Add Error", + "message":"failed" + } +} \ No newline at end of file diff --git a/wwwroot/user/userapikey/delete_userapikey.dspy b/wwwroot/user/userapikey/delete_userapikey.dspy new file mode 100644 index 0000000..ec1039b --- /dev/null +++ b/wwwroot/user/userapikey/delete_userapikey.dspy @@ -0,0 +1,24 @@ + +ns = { + 'id':params_kw['id'], +} +db = DBPools() +async with db.sqlorContext('sage') as sor: + r = await sor.D('userapikey', ns) + print('delete success'); + return { + "widgettype":"Message", + "options":{ + "title":"Delete Success", + "message":"ok" + } + } + +print('Delete failed'); +return { + "widgettype":"Error", + "options":{ + "title":"Delete Error", + "message":"failed" + } +} \ No newline at end of file diff --git a/wwwroot/user/userapikey/get_userapikey.dspy b/wwwroot/user/userapikey/get_userapikey.dspy new file mode 100644 index 0000000..085fa59 --- /dev/null +++ b/wwwroot/user/userapikey/get_userapikey.dspy @@ -0,0 +1,74 @@ + +ns = params_kw.copy() +print(f'get_userapikey.dspy:{ns=}') +if not ns.get('page'): + ns['page'] = 1 +if not ns.get('sort'): + + ns['sort'] = 'id' + +filterjson = params_kw.get('data_filter') +userid = await get_user() +ns['userid'] = userid +if not filterjson: + fields = [ f['name'] for f in [ + { + "name": "id", + "title": "id", + "type": "str", + "length": 32 + }, + { + "name": "providerid", + "title": "\u4f9b\u5e94\u5546id", + "type": "str", + "length": 200 + }, + { + "name": "customerid", + "title": "\u7528\u6237id", + "type": "str", + "length": 32, + "default": "0" + }, + { + "name": "apikey", + "title": "api\u5bc6\u94a5", + "type": "str", + "length": 4000, + "default": "0" + }, + { + "name": "secretkey", + "title": "\u9644\u5c5e\u5bc6\u94a5", + "type": "str", + "length": 4000 + }, + { + "name": "rfname", + "title": "\u51fd\u6570\u540d", + "type": "str", + "length": 400 + } +] ] + filterjson = default_filterjson(fields, ns) +sql = '''select a.*, b.providerid_text, c.customerid_text +from (select y.* from users x, userapikey y where x.id=${userid}$ and x.orgid = y.customerid) a left join (select id as providerid, + orgname as providerid_text from organization where 1 = 1) b on a.providerid = b.providerid left join (select id as customerid, + orgname as customerid_text from organization where 1 = 1) c on a.customerid = c.customerid''' + +if filterjson: + dbf = DBFilter(filterjson) + conds = dbf.gen(ns) + if conds: + ns.update(dbf.consts) + sql += ' and ' + conds +info(f'{ns=},{sql=}') +db = DBPools() +async with db.sqlorContext('sage') as sor: + r = await sor.sqlPaging(sql, ns) + return r +return { + "total":0, + "rows":[] +} diff --git a/wwwroot/user/userapikey/index.ui b/wwwroot/user/userapikey/index.ui new file mode 100644 index 0000000..fa784c8 --- /dev/null +++ b/wwwroot/user/userapikey/index.ui @@ -0,0 +1,126 @@ + +{ + "widgettype":"Tabular", + "options":{ + + + "editable":{ + "new_data_url":"{{entire_url('add_userapikey.dspy')}}", + "delete_data_url":"{{entire_url('delete_userapikey.dspy')}}", + "update_data_url":"{{entire_url('update_userapikey.dspy')}}" + }, + + "data_url":"{{entire_url('./get_userapikey.dspy')}}", + "data_method":"GET", + "data_params":{{json.dumps(params_kw, indent=4)}}, + "row_options":{ + + + + + "browserfields":{ + "excloud": [], + "cwidth": {} +}, + + + "editexclouded":[ + "id" +], + + "fields":[ + { + "name": "id", + "title": "id", + "type": "str", + "length": 32, + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "id" + }, + { + "name": "providerid", + "title": "\u4f9b\u5e94\u5546id", + "type": "str", + "length": 200, + "label": "\u4f9b\u5e94\u5546id", + "cwidth":16, + "uitype": "code", + "cwidth":18, + "valueField": "providerid", + "textField": "providerid_text", + "params": { + "dbname": "sage", + "table": "organization", + "tblvalue": "id", + "tbltext": "orgname", + "valueField": "providerid", + "textField": "providerid_text" + }, + "dataurl": "\/get_code.dspy" + }, + { + "name": "customerid", + "title": "\u7528\u6237id", + "type": "str", + "length": 32, + "cwidth":18, + "default": "0", + "label": "\u7528\u6237id", + "cwidth":16, + "uitype": "code", + "valueField": "customerid", + "textField": "customerid_text", + "params": { + "dbname": "sage", + "table": "organization", + "tblvalue": "id", + "tbltext": "orgname", + "valueField": "customerid", + "textField": "customerid_text" + }, + "dataurl": "\/get_code.dspy" + }, + { + "name": "apikey", + "title": "api\u5bc6\u94a5", + "type": "str", + "length": 4000, + "default": "0", + "cwidth": 18, + "uitype": "text", + "rows": 4, + "datatype": "str", + "label": "api\u5bc6\u94a5" + }, + { + "name": "secretkey", + "title": "\u9644\u5c5e\u5bc6\u94a5", + "type": "str", + "length": 4000, + "cwidth": 18, + "uitype": "text", + "rows": 4, + "datatype": "str", + "label": "\u9644\u5c5e\u5bc6\u94a5" + }, + { + "name": "rfname", + "title": "\u51fd\u6570\u540d", + "type": "str", + "length": 400, + "cwidth": 18, + "uitype": "text", + "rows": 4, + "datatype": "str", + "label": "\u51fd\u6570\u540d" + } +] + }, + + "page_rows":160, + "cache_limit":5 + } + +} diff --git a/wwwroot/user/userapikey/update_userapikey.dspy b/wwwroot/user/userapikey/update_userapikey.dspy new file mode 100644 index 0000000..b31458e --- /dev/null +++ b/wwwroot/user/userapikey/update_userapikey.dspy @@ -0,0 +1,22 @@ + +ns = params_kw.copy() +db = DBPools() +async with db.sqlorContext('sage') as sor: + r = await sor.U('userapikey', ns) + print('update success'); + return { + "widgettype":"Message", + "options":{ + "title":"Update Success", + "message":"ok" + } + } + +print('update failed'); +return { + "widgettype":"Error", + "options":{ + "title":"Update Error", + "message":"failed" + } +} \ No newline at end of file diff --git a/wwwroot/user/wechat_login.ui b/wwwroot/user/wechat_login.ui new file mode 100644 index 0000000..9f65d1d --- /dev/null +++ b/wwwroot/user/wechat_login.ui @@ -0,0 +1,5 @@ +{ + "widgettype":"VBox", + "options":{ + } +} diff --git a/wwwroot/userpassword_login.dspy b/wwwroot/userpassword_login.dspy new file mode 100644 index 0000000..883e3f0 --- /dev/null +++ b/wwwroot/userpassword_login.dspy @@ -0,0 +1,11 @@ +username = params_kw.get('username') +passwd = params_kw.get('passwd') +if not passwd: + return UiError(title='Login failed', message='Password is required') +passwd = password(passwd) +rzt = await check_user_password(request, username, passwd) +if rzt: + return UiMessage(title='Logined', message=f'Welcome back ') +return UiError(title='login failed', message='user and password mismatch') + + diff --git a/wwwroot/userpassword_login.ui b/wwwroot/userpassword_login.ui new file mode 100644 index 0000000..e8933ad --- /dev/null +++ b/wwwroot/userpassword_login.ui @@ -0,0 +1,43 @@ +{ + "id":"login_window", + "widgettype":"PopupWindow", + "options":{ + "auto_open":true, + "anthor":"cc", + "cwidth":20, + "cheight":"14" + }, + "subwidgets":[ + { + "widgettype":"Form", + "id":"userpasswd", + "options":{ + "cols":1, + "fields":[ + { + "name":"username", + "label":"用户名", + "uitype":"str" + }, + { + "name":"passwd", + "label":"密码", + "uitype":"password" + } + ] + } + } + ], + "binds":[ + { + "wid":"userpasswd", + "event":"submit", + "actiontype":"urlwidget", + "target":"self", + "options":{ + "url":"{{entire_url('userpassword_login.dspy')}}" + } + } + ] +} +