52 lines
2.4 KiB
Python
52 lines
2.4 KiB
Python
# Map every controller action: HTTP verb + route, anonymous or not, and which checks its body makes.
|
|
# Usage: python -I routemap.py <Controllers folder> <out.tsv>
|
|
import os, re, sys
|
|
|
|
src, out = sys.argv[1], sys.argv[2]
|
|
verb_re = re.compile(r'\[Http(Get|Post|Put|Delete|Patch)(?:\("([^"]*)"\))?\]')
|
|
method_re = re.compile(r'public\s+(?:async\s+)?[\w<>\[\], ]+\s+(\w+)\s*\(')
|
|
checks = {
|
|
'role': re.compile(r'Authorized\.Has\w+Role|HasAnyRole|HasReadFullRole|HasModifyRole|HasCreateRole|HasDeleteRole|HasSelectRole'),
|
|
'usertype': re.compile(r'UserTypeFromContext|UserType\.Customer|UserType\.HeadOffice|IsCustomerUser|CustomerUser'),
|
|
'biz': re.compile(r'Biz\.GetBiz|new \w+Biz\(|\w+Biz\.'),
|
|
'dltoken': re.compile(r'ValidateDownloadToken'),
|
|
}
|
|
|
|
rows = []
|
|
for fn in sorted(os.listdir(src)):
|
|
if not fn.endswith('.cs'):
|
|
continue
|
|
text = open(os.path.join(src, fn), encoding='utf-8-sig').read()
|
|
# drop // comments so commented-out actions are ignored
|
|
text = '\n'.join(l for l in text.splitlines() if not l.lstrip().startswith('//'))
|
|
m = re.search(r'\[Route\("([^"]*)"\)\]', text)
|
|
base = m.group(1).replace('api/v{version:apiVersion}/', '') if m else ''
|
|
cls_auth = bool(re.search(r'\[Authorize\][^{]*class\s+\w+Controller', text, re.S))
|
|
for vm in verb_re.finditer(text):
|
|
after = text[vm.end():]
|
|
mm = method_re.search(after)
|
|
if not mm:
|
|
continue
|
|
attrs = text[max(0, vm.start() - 300):vm.end()] + after[:mm.start()]
|
|
anon = '[AllowAnonymous]' in attrs
|
|
# body: from the method's opening brace to the matching closing brace
|
|
start = vm.end() + mm.end()
|
|
brace = text.find('{', start)
|
|
depth, i = 0, brace
|
|
while i < len(text):
|
|
if text[i] == '{': depth += 1
|
|
elif text[i] == '}':
|
|
depth -= 1
|
|
if depth == 0: break
|
|
i += 1
|
|
body = text[brace:i]
|
|
found = [k for k, r in checks.items() if r.search(body)]
|
|
rows.append((fn.replace('Controller.cs', ''), vm.group(1).upper(), (base + '/' + (vm.group(2) or '')).rstrip('/'),
|
|
mm.group(1), 'ANON' if anon else ('auth' if cls_auth or '[Authorize]' in attrs else 'NO-ATTR'), ','.join(found) or '-'))
|
|
|
|
with open(out, 'w', encoding='utf-8') as f:
|
|
f.write('controller\tverb\troute\taction\taccess\tchecks\n')
|
|
for r in rows:
|
|
f.write('\t'.join(r) + '\n')
|
|
print(len(rows), 'actions')
|