code wiki / _hdl_build / nx_access_lib.nx

nx_access_lib.nx source

↩ module page · 101 lines · 5534 B

1// nx_access_lib.nx -- the S-class authorization CORE. ONE rule, reusable on every site: a request is 2// allowed iff the user's GRANTED level >= the area's REQUIRED level, deny-by-default. Composes the 3// area->required-level resolver (nx_site_lock_lib / slk_resolve) with a user->granted-level resolver 4// (a roles registry). The owner (level 3) clears everything incl. exclusive areas; a family member (1) 5// can NEVER reach an owner area; no session (0) reaches nothing gated. No privilege escalation possible. 6// roles registry line: handle <TAB> level (e.g. elderwesto<TAB>3 ; aunt_jane<TAB>1) 7// Sovereign: nx_site_lock_lib only. license_tier: ORIGINAL 8import "nx_site_lock_lib.nx" 9const K_MAGIC_262144: i64 = 262144 10const K_MAGIC_32768: i64 = 32768 11 12// granted level for `handle` from the roles registry; 0 if absent (DENY-BY-DEFAULT). 13func ag_resolve_level(reg: *u8, len: i64, handle: *u8, hlen: i64) -> i64 { 14 let fs: *i64 = sys_mmap(8); let fe: *i64 = sys_mmap(8) 15 var ls: i64 = 0; var best: i64 = 0 16 while ls < len { 17 let le: i64 = slk_line_end(reg, len, ls) 18 if le > ls { if reg[ls] != (35 as u8) { 19 if slk_field(reg, ls, le, 0, fs, fe) == 1 { 20 let h0s: i64 = fs[0]; let h0e: i64 = fe[0] 21 if slk_eq(slk_at(reg, h0s), h0e - h0s, handle, hlen) == 1 { 22 if slk_field(reg, ls, le, 1, fs, fe) == 1 { best = slk_atoi(reg, fs[0], fe[0]) } 23 } 24 } 25 } } 26 ls = le + 1 27 } 28 return best 29} 30 31// THE DECISION. required = slk_resolve(site_locks, site, path); granted = ag_resolve_level(roles, handle). 32// returns 1=ALLOW, 0=DENY. fills out_req[0]/out_grant[0] (for honest 401/403 messaging) + out_realm. 33// public area (required 0) -> ALLOW (anyone, even no session) 34// gated area, granted >= required -> ALLOW 35// gated area, granted < required -> DENY (covers no-session=0 and under-privileged) 36func ag_allow(sl: *u8, sl_len: i64, roles: *u8, r_len: i64, 37 site: *u8, slen: i64, path: *u8, plen: i64, handle: *u8, hlen: i64, 38 out_req: *i64, out_grant: *i64, out_realm: *u8, realmcap: i64) -> i64 { 39 let lvlbox: *i64 = sys_mmap(8) 40 let locked: i64 = slk_resolve(sl, sl_len, site, slen, path, plen, out_realm, realmcap, lvlbox) 41 if locked == 0 { out_req[0] = 0; out_grant[0] = 0; return 1 } // public -> allow 42 let required: i64 = lvlbox[0] 43 var granted: i64 = 0 44 if hlen > 0 { granted = ag_resolve_level(roles, r_len, handle, hlen) } 45 out_req[0] = required; out_grant[0] = granted 46 if granted >= required { return 1 } 47 return 0 48} 49 50// ---- SHARED session-identity -> access-level path (the canonical uid-hash -> handle -> level chain). The mgmt API 51// AND the hub gateway compose THIS (one copy, not per-service duplicates) so every OPAQUE service grants access the 52// SAME way: a Modern-Auth session's uid hash -> the shared uid->handle index -> the roles registry -> level. ---- 53func ag_hex(dst: *u8, off: i64, src: *u8, n: i64) -> i64 { let hx: *u8="0123456789abcdef" as *u8; var o: i64=off; var i: i64=0; while i<n { let c: i64=(src[i] as i64)&0xff; dst[o]=hx[(c>>4)&15]; dst[o+1]=hx[c&15]; o=o+2; i=i+1 } return o } 54// leak-free bounded read into a REUSED caller buffer; returns bytes (>=0) or -1. 55func ag_read_file(path: *u8, out: *u8, cap: i64) -> i64 { 56 let fd: i64 = sys_openat_rd(path) 57 if fd < 0 { return 0 - 1 } 58 var total: i64 = 0; var go: i64 = 1 59 while go == 1 { 60 let tail: *u8 = (out as i64 + total) as *u8 61 let nr: i64 = sys_read(fd, tail, cap - total) 62 if nr <= 0 { go = 0 } 63 if nr > 0 { total = total + nr } 64 if total >= cap { go = 0 } 65 } 66 sys_close(fd) 67 return total 68} 69// uidhex -> handle via the shared index TSV (uidhex<TAB>handle). returns handle length (0 = not found). 70func ag_idx_lookup(idxbuf: *u8, idxlen: i64, uidhex: *u8, uxn: i64, out_h: *u8, cap: i64) -> i64 { 71 let fs: *i64 = sys_mmap(8); let fe: *i64 = sys_mmap(8) 72 var ls: i64 = 0; var found: i64 = 0 73 while ls < idxlen { 74 let le: i64 = slk_line_end(idxbuf, idxlen, ls) 75 if found == 0 { if le > ls { if idxbuf[ls] != (35 as u8) { 76 if slk_field(idxbuf, ls, le, 0, fs, fe) == 1 { 77 if slk_eq(slk_at(idxbuf, fs[0]), fe[0] - fs[0], uidhex, uxn) == 1 { 78 if slk_field(idxbuf, ls, le, 1, fs, fe) == 1 { 79 var o: i64 = 0; let hl: i64 = fe[0] - fs[0] 80 while o < hl { if o < cap - 1 { out_h[o] = idxbuf[fs[0] + o] } o = o + 1 } 81 out_h[o] = 0 as u8; found = hl 82 } 83 } 84 } 85 } } } 86 ls = le + 1 87 } 88 return found 89} 90// THE canonical session-identity -> access level: uid HASH -> (index) handle -> (roles) level. 0 = deny-by-default 91// (uid not in the index, or handle absent from roles). The caller supplies the uid from nx_sa_validate_handle. 92func ag_uid_to_level(uid: *u8, uidn: i64, idx_path: *u8, roles_path: *u8) -> i64 { 93 let ux: *u8 = sys_mmap(160); let uxn: i64 = ag_hex(ux, 0, uid, uidn) 94 let idx: *u8 = sys_mmap(K_MAGIC_262144); let il: i64 = ag_read_file(idx_path, idx, K_MAGIC_262144) 95 if il <= 0 { return 0 } 96 let h: *u8 = sys_mmap(128); let hl: i64 = ag_idx_lookup(idx, il, ux, uxn, h, 128) 97 if hl <= 0 { return 0 } 98 let r: *u8 = sys_mmap(K_MAGIC_32768); var rl: i64 = ag_read_file(roles_path, r, K_MAGIC_32768) 99 if rl < 0 { rl = 0 } 100 return ag_resolve_level(r, rl, h, hl) 101}