175 lines
6.6 KiB
Python
175 lines
6.6 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
# -*- coding: utf-8 -*-
|
||
|
|
"""
|
||
|
|
Role permission verification script.
|
||
|
|
Tests each role's access to critical endpoints via the PermissionInterceptor.
|
||
|
|
"""
|
||
|
|
|
||
|
|
import requests
|
||
|
|
import json
|
||
|
|
import sys
|
||
|
|
|
||
|
|
BASE = "http://101.43.95.130:8039"
|
||
|
|
|
||
|
|
# Test users: login uses user_name, not phone
|
||
|
|
# Role assignments from DB:
|
||
|
|
# admin(1): admin role -> *:*:* (from code)
|
||
|
|
# dispatcher01(112): dispatcher -> system:dispatch:assign, system:dispatch:list, system:profit:list
|
||
|
|
# service01(113): service -> system:order:list, system:profit:list
|
||
|
|
# ops01(114): operations -> system:dispatch:list, system:order:list, system:platformConfig:list, system:profit:list, system:region:list
|
||
|
|
# promoter_a(115): promoter -> system:profit:list, system:promotion:list, system:promotion:query
|
||
|
|
# caregiver01(118): NO ROLE -> no permissions
|
||
|
|
# patient01(119): NO ROLE -> no permissions
|
||
|
|
TEST_USERS = [
|
||
|
|
{"name": "admin", "username": "admin", "password": "admin123", "role": "admin"},
|
||
|
|
{"name": "dispatcher01", "username": "dispatcher01", "password": "123456", "role": "dispatcher"},
|
||
|
|
{"name": "service01", "username": "service01", "password": "123456", "role": "service"},
|
||
|
|
{"name": "ops01", "username": "ops01", "password": "123456", "role": "operations"},
|
||
|
|
{"name": "promoter_a", "username": "promoter_a", "password": "123456", "role": "promoter"},
|
||
|
|
{"name": "caregiver01", "username": "caregiver01", "password": "123456", "role": "none"},
|
||
|
|
{"name": "patient01", "username": "patient01", "password": "123456", "role": "none"},
|
||
|
|
]
|
||
|
|
|
||
|
|
# Endpoints to test
|
||
|
|
# (method, url, desc, should_work_if_role_has_this_permission)
|
||
|
|
# For "none" role users, most should be blocked except unprotected endpoints
|
||
|
|
ENDPOINTS = [
|
||
|
|
# User management - requires system:user:list
|
||
|
|
("GET", "/system/user/list", "userList", "system:user:list"),
|
||
|
|
# Promotion - requires system:promotion:query
|
||
|
|
("GET", "/system/promotion/myStats", "promoStats", "system:promotion:query"),
|
||
|
|
("GET", "/system/promotion/team", "promoTeam", "system:promotion:query"),
|
||
|
|
("GET", "/system/promotion/myCommission", "promoCommission", "system:promotion:query"),
|
||
|
|
# Dispatch - requires system:dispatch:list
|
||
|
|
("GET", "/system/dispatch/pending", "dispatchPending", "system:dispatch:list"),
|
||
|
|
("GET", "/system/dispatch/caregivers", "dispatchCaregivers", "system:dispatch:list"),
|
||
|
|
# Region - requires system:region:list
|
||
|
|
("GET", "/system/region/list", "regionList", "system:region:list"),
|
||
|
|
# Order myBalance - may not have @PreAuthorize (checking)
|
||
|
|
("GET", "/system/profit/myBalance", "myBalance", None),
|
||
|
|
# Order list - requires system:order:list
|
||
|
|
("GET", "/system/order/list", "orderList", "system:order:list"),
|
||
|
|
]
|
||
|
|
|
||
|
|
|
||
|
|
def login(username, password):
|
||
|
|
"""Login via appLogin, return (token, userId)"""
|
||
|
|
try:
|
||
|
|
r = requests.post(f"{BASE}/appLogin", data={
|
||
|
|
"username": username,
|
||
|
|
"password": password
|
||
|
|
}, timeout=10)
|
||
|
|
data = r.json()
|
||
|
|
if data.get("code") == 200:
|
||
|
|
return data["data"]["token"], data["data"]["userId"]
|
||
|
|
return None, data.get("msg", "unknown error")
|
||
|
|
except Exception as e:
|
||
|
|
return None, str(e)
|
||
|
|
|
||
|
|
|
||
|
|
def get_user_info(token):
|
||
|
|
"""Get user permissions from /getInfo"""
|
||
|
|
headers = {"Authorization": f"Bearer {token}"}
|
||
|
|
try:
|
||
|
|
r = requests.get(f"{BASE}/getInfo", headers=headers, timeout=10)
|
||
|
|
data = r.json()
|
||
|
|
if data.get("code") == 200:
|
||
|
|
return data.get("permissions", [])
|
||
|
|
return []
|
||
|
|
except:
|
||
|
|
return []
|
||
|
|
|
||
|
|
|
||
|
|
def test_endpoint(method, url, token):
|
||
|
|
"""Test an endpoint and return (http_code, response_code, msg)"""
|
||
|
|
headers = {"Authorization": f"Bearer {token}"}
|
||
|
|
try:
|
||
|
|
if method == "GET":
|
||
|
|
r = requests.get(f"{BASE}{url}", headers=headers, timeout=10)
|
||
|
|
else:
|
||
|
|
r = requests.post(f"{BASE}{url}", headers=headers, timeout=10)
|
||
|
|
data = r.json()
|
||
|
|
return r.status_code, data.get("code"), data.get("msg", "")
|
||
|
|
except Exception as e:
|
||
|
|
return 0, None, str(e)
|
||
|
|
|
||
|
|
|
||
|
|
def main():
|
||
|
|
total_ok = 0
|
||
|
|
total_bad = 0
|
||
|
|
issues = []
|
||
|
|
|
||
|
|
for user in TEST_USERS:
|
||
|
|
print(f"\n{'='*60}")
|
||
|
|
print(f"User: {user['name']} | Role: {user['role']}")
|
||
|
|
print(f"{'='*60}")
|
||
|
|
|
||
|
|
token, info = login(user["username"], user["password"])
|
||
|
|
if not token:
|
||
|
|
print(f" !! Login FAILED: {info}")
|
||
|
|
continue
|
||
|
|
|
||
|
|
# Get actual permissions
|
||
|
|
perms = get_user_info(token)
|
||
|
|
perm_set = set(perms)
|
||
|
|
print(f" Login OK (userId={info}), permissions={list(perm_set)[:5]}...")
|
||
|
|
print()
|
||
|
|
|
||
|
|
for method, url, desc, required_perm in ENDPOINTS:
|
||
|
|
http_code, resp_code, msg = test_endpoint(method, url, token)
|
||
|
|
|
||
|
|
# Determine access
|
||
|
|
if resp_code == 200:
|
||
|
|
access = "ALLOW"
|
||
|
|
elif resp_code == 401:
|
||
|
|
access = "DENY_401"
|
||
|
|
elif resp_code == 403:
|
||
|
|
access = "DENY_403"
|
||
|
|
elif resp_code == 500 and "denied" in str(msg).lower():
|
||
|
|
access = "DENY_500"
|
||
|
|
elif http_code == 200 and resp_code != 200:
|
||
|
|
access = f"BIZ_ERR({resp_code})"
|
||
|
|
else:
|
||
|
|
access = f"HTTP_{http_code}"
|
||
|
|
|
||
|
|
# Determine if this is correct
|
||
|
|
has_perm = required_perm is None or required_perm in perm_set
|
||
|
|
# admin always has *:*:*
|
||
|
|
if user["role"] == "admin":
|
||
|
|
has_perm = True
|
||
|
|
|
||
|
|
if access == "ALLOW":
|
||
|
|
if has_perm:
|
||
|
|
verdict = "OK"
|
||
|
|
total_ok += 1
|
||
|
|
else:
|
||
|
|
verdict = "LEAK"
|
||
|
|
total_bad += 1
|
||
|
|
issues.append(f"{user['name']} can access {desc} ({url}) but lacks {required_perm}")
|
||
|
|
elif access.startswith("DENY"):
|
||
|
|
if has_perm:
|
||
|
|
verdict = "BLOCK_ERR"
|
||
|
|
total_bad += 1
|
||
|
|
issues.append(f"{user['name']} DENIED {desc} ({url}) but has {required_perm}")
|
||
|
|
else:
|
||
|
|
verdict = "OK_BLOCK"
|
||
|
|
total_ok += 1
|
||
|
|
else:
|
||
|
|
verdict = f"?{access}"
|
||
|
|
total_bad += 1
|
||
|
|
issues.append(f"{user['name']} UNEXPECTED {access} on {desc} ({url})")
|
||
|
|
|
||
|
|
print(f" [{verdict:12s}] {desc:20s} ({url}) | access={access} | need={required_perm} | has={has_perm}")
|
||
|
|
|
||
|
|
print(f"\n{'='*60}")
|
||
|
|
print(f"SUMMARY: {total_ok} OK, {total_bad} PROBLEMS")
|
||
|
|
print(f"{'='*60}")
|
||
|
|
if issues:
|
||
|
|
print("\nISSUES FOUND:")
|
||
|
|
for i in issues:
|
||
|
|
print(f" * {i}")
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
main()
|