feat: add init_perm.py for permission initialization

This commit is contained in:
yumoqing 2026-04-29 12:57:28 +08:00
parent 469255afe7
commit dc8db76a52

View File

@ -0,0 +1,104 @@
"""Permission initialization utility for business modules.
Scans wwwroot directory for .ui and .dspy files, registers them in
the permission table, and grants access to convention roles.
Convention roles:
- any: anyone (no auth required)
- logined: any logged-in user
- customer.*: customer org arbitrary role
- owner.*: owner org arbitrary role
- owner.superuser: superuser for initialization
"""
import os
from appPublic.uniqueID import getID
from appPublic.log import debug
from sqlor.dbpools import DBPools
def collect_ui_dspy_paths(wwwroot_dir):
"""Scan wwwroot directory and return list of URL paths for .ui/.dspy files."""
paths = []
for dirpath, _, filenames in os.walk(wwwroot_dir):
for fn in sorted(filenames):
if fn.endswith(('.ui', '.dspy')):
full = os.path.join(dirpath, fn)
rel = os.path.relpath(full, wwwroot_dir)
# Convert to URL path: module_name/relative/path
url = '/' + rel.replace(os.sep, '/')
# Skip base.ui (layout template, not a standalone page)
if fn == 'base.ui':
continue
paths.append(url)
return paths
async def ensure_permission(sor, path, permtype='page'):
"""Ensure permission exists in database, return permid."""
recs = await sor.R('permission', {'path': path})
if recs:
return recs[0].id
permid = getID()
await sor.C('permission', {
'id': permid,
'name': path.split('/')[-1],
'path': path,
'permtype': permtype,
})
return permid
async def ensure_role(sor, orgtypeid, name):
"""Ensure role exists in database, return roleid."""
recs = await sor.R('role', {'orgtypeid': orgtypeid, 'name': name})
if recs:
return recs[0].id
roleid = getID()
await sor.C('role', {
'id': roleid,
'orgtypeid': orgtypeid,
'name': name,
})
return roleid
async def grant_permission(sor, roleid, permid):
"""Grant permission to role if not already granted."""
recs = await sor.R('rolepermission', {'roleid': roleid, 'permid': permid})
if not recs:
await sor.C('rolepermission', {
'id': getID(),
'roleid': roleid,
'permid': permid,
})
async def init_module_permissions(dbname, module_name, wwwroot_dir):
"""Initialize permissions for a business module.
Scans wwwroot for .ui/.dspy files, registers paths in permission table,
and grants access to convention roles (logined, customer.*, owner.superuser).
"""
paths = collect_ui_dspy_paths(wwwroot_dir)
if not paths:
debug(f'{module_name}: no UI/DSPY paths found, skipping permission init')
return
db = DBPools()
async with db.sqlorContext(dbname) as sor:
# Ensure convention roles exist
role_logined = await ensure_role(sor, '*', 'logined')
role_customer = await ensure_role(sor, 'customer', '*')
role_superuser = await ensure_role(sor, 'owner', 'superuser')
# Register paths and grant permissions
for path in paths:
permid = await ensure_permission(sor, path)
# Grant to logined users
await grant_permission(sor, role_logined, permid)
# Grant to customer org users
await grant_permission(sor, role_customer, permid)
# Grant to superuser
await grant_permission(sor, role_superuser, permid)
debug(f'{module_name}: registered {len(paths)} permissions')