nx_crm_acl.nx source
↩ module page · 153 lines · 8196 B
1// nx_crm_acl.nx -- USERS + ROLES + RECORD PERMISSIONS (CRM census E7). The Salesforce-verified layered model,
2// sovereign + integer-deterministic + DENY-BY-DEFAULT:
3// 1. TENANT WALL first: cross-workspace access is ALWAYS denied -- even for admins (the andelinwest isolation law).
4// 2. OWNER: full access to own records.
5// 3. ADMIN (role 4): full access within the tenant.
6// 4. ORG-WIDE DEFAULT per record: private(0) | read(1) | write(2).
7// 5. ROLE HIERARCHY: the management chain ABOVE the owner gets READ on private records (v1 conservative;
8// Salesforce grants owner-like via hierarchy -- we start read-only, stated).
9// 6. EXPLICIT SHARES: (record, user, level) grants, level >= action required.
10// 7. FIELD-LEVEL SECURITY: sensitive fields (e.g. giving amounts) need a minimum role.
11// Pure logic + selftest gate (argless): a 14-check allow/deny matrix incl. the tenant-beats-admin proof.
12// Persistence rides the relate store next. license_tier: ORIGINAL expect_exit: 0
13import "nx_syscalls.nx"
14
15func slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
16func p(s: *u8) -> i64 { sys_write(1, s, slen(s)); return 0 }
17func pn(v: i64) -> i64 {
18 var m: i64 = v; if m < 0 { p("-" as *u8); m = 0 - m }
19 let t: *u8 = sys_mmap(24); var k: i64 = 0; if m == 0 { t[0] = 48 as u8; k = 1 }
20 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
21 let o: *u8 = sys_mmap(24); var i: i64 = 0; while i < k { o[i] = t[k-1-i]; i = i + 1 } sys_write(1, o, k); return 0
22}
23func seq(a: *u8, b: *u8) -> i64 {
24 var i: i64 = 0
25 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 }
26 if b[i] != (0 as u8) { return 0 }
27 return 1
28}
29
30// ---- ctx bundle (>6-arg clobber gotcha -> bundle):
31// ctx[0]=user names *i64, [1]=role lvls *i64, [2]=manager names *i64, [3]=tenants *i64, [4]=nusers,
32// ctx[5]=share recs *i64, [6]=share users *i64, [7]=share lvls *i64, [8]=nshares
33func ac_ufind(un: *i64, n: i64, name: *u8) -> i64 {
34 var i: i64 = 0
35 while i < n { if seq(un[i] as *u8, name) == 1 { return i } i = i + 1 }
36 return 0 - 1
37}
38// is `user` strictly ABOVE `owner` in the management chain (bounded walk, no cycles trusted)
39func ac_above(un: *i64, um: *i64, n: i64, owner: *u8, user: *u8) -> i64 {
40 var cur: i64 = ac_ufind(un, n, owner)
41 var hops: i64 = 0
42 while hops < 8 {
43 if cur < 0 { return 0 }
44 let mgr: *u8 = um[cur] as *u8
45 if mgr[0] == (0 as u8) { return 0 }
46 if seq(mgr, user) == 1 { return 1 }
47 cur = ac_ufind(un, n, mgr)
48 hops = hops + 1
49 }
50 return 0
51}
52// record rec: [0]=name *u8, [1]=owner *u8, [2]=owd i64 (0 private/1 read/2 write), [3]=tenant *u8
53// action: 1=read 2=write. returns 1 ALLOW / 0 DENY. deny-by-default.
54func ac_can(ctx: *i64, user: *u8, action: i64, rec: *i64) -> i64 {
55 let un: *i64 = ctx[0] as *i64
56 let n: i64 = ctx[4]
57 let ui: i64 = ac_ufind(un, n, user)
58 if ui < 0 { return 0 }
59 let ut: *i64 = ctx[3] as *i64
60 // 1. the tenant wall beats EVERYTHING (even admin)
61 if seq(ut[ui] as *u8, rec[3] as *u8) == 0 { return 0 }
62 // 2. owner
63 if seq(user, rec[1] as *u8) == 1 { return 1 }
64 // 3. admin within tenant
65 let ur: *i64 = ctx[1] as *i64
66 if ur[ui] >= 4 { return 1 }
67 // 4. org-wide default
68 if rec[2] == 2 { return 1 }
69 if rec[2] == 1 { if action == 1 { return 1 } }
70 // 5. role hierarchy: chain above the owner reads private records
71 if action == 1 { if ac_above(un, ctx[2] as *i64, n, rec[1] as *u8, user) == 1 { return 1 } }
72 // 6. explicit shares
73 let sr: *i64 = ctx[5] as *i64
74 let su: *i64 = ctx[6] as *i64
75 let sl: *i64 = ctx[7] as *i64
76 let sn: i64 = ctx[8]
77 var i: i64 = 0
78 while i < sn {
79 if seq(sr[i] as *u8, rec[0] as *u8) == 1 { if seq(su[i] as *u8, user) == 1 { if sl[i] >= action { return 1 } } }
80 i = i + 1
81 }
82 // 7. deny by default
83 return 0
84}
85// field-level security: user's role must reach minrole
86func ac_field(ctx: *i64, user: *u8, minrole: i64) -> i64 {
87 let ui: i64 = ac_ufind(ctx[0] as *i64, ctx[4], user)
88 if ui < 0 { return 0 }
89 let ur: *i64 = ctx[1] as *i64
90 if ur[ui] >= minrole { return 1 }
91 return 0
92}
93
94func ac_check(label: *u8, got: i64, want: i64) -> i64 {
95 p(" ACL-ROLE " as *u8); p(label)
96 if got == 1 { p(" -> ALLOW" as *u8) } else { p(" -> DENY" as *u8) }
97 if got == want { p("\n" as *u8); return 1 }
98 p(" (WRONG)\n" as *u8)
99 return 0
100}
101
102func main() -> i64 {
103 p("=== NX-CRM-ACL SELFTEST (ACL-ROLE: tenant wall > owner > admin > OWD > hierarchy > shares; deny-by-default) ===\n" as *u8)
104 var ok: i64 = 1
105 let mt: *u8 = sys_mmap(4); mt[0] = 0 as u8
106 // users: name / role / manager / tenant (roles: 4 admin, 3 manager, 2 member)
107 let un: *i64 = sys_mmap(8 * 8) as *i64
108 let ur: *i64 = sys_mmap(8 * 8) as *i64
109 let um: *i64 = sys_mmap(8 * 8) as *i64
110 let ut: *i64 = sys_mmap(8 * 8) as *i64
111 un[0]="alice" as *u8 as i64; ur[0]=2; um[0]="maria" as *u8 as i64; ut[0]="andelinwest" as *u8 as i64
112 un[1]="bob" as *u8 as i64; ur[1]=2; um[1]="maria" as *u8 as i64; ut[1]="andelinwest" as *u8 as i64
113 un[2]="maria" as *u8 as i64; ur[2]=3; um[2]="dana" as *u8 as i64; ut[2]="andelinwest" as *u8 as i64
114 un[3]="dana" as *u8 as i64; ur[3]=4; um[3]=mt as i64; ut[3]="andelinwest" as *u8 as i64
115 un[4]="eve" as *u8 as i64; ur[4]=4; um[4]=mt as i64; ut[4]="firstward" as *u8 as i64
116 // shares (empty to start)
117 let sr: *i64 = sys_mmap(8 * 8) as *i64
118 let su: *i64 = sys_mmap(8 * 8) as *i64
119 let sl: *i64 = sys_mmap(8 * 8) as *i64
120 let ctx: *i64 = sys_mmap(8 * 12) as *i64
121 ctx[0]=un as i64; ctx[1]=ur as i64; ctx[2]=um as i64; ctx[3]=ut as i64; ctx[4]=5
122 ctx[5]=sr as i64; ctx[6]=su as i64; ctx[7]=sl as i64; ctx[8]=0
123 // records: name / owner / owd / tenant
124 let deal1: *i64 = sys_mmap(8 * 4) as *i64
125 deal1[0]="deal1" as *u8 as i64; deal1[1]="alice" as *u8 as i64; deal1[2]=0; deal1[3]="andelinwest" as *u8 as i64
126 let note1: *i64 = sys_mmap(8 * 4) as *i64
127 note1[0]="note1" as *u8 as i64; note1[1]="bob" as *u8 as i64; note1[2]=1; note1[3]="andelinwest" as *u8 as i64
128 let doc1: *i64 = sys_mmap(8 * 4) as *i64
129 doc1[0]="doc1" as *u8 as i64; doc1[1]="alice" as *u8 as i64; doc1[2]=2; doc1[3]="andelinwest" as *u8 as i64
130
131 if ac_check("alice write deal1 (owner)" as *u8, ac_can(ctx, "alice" as *u8, 2, deal1), 1) == 0 { ok = 0 }
132 if ac_check("bob read deal1 (peer, private)" as *u8, ac_can(ctx, "bob" as *u8, 1, deal1), 0) == 0 { ok = 0 }
133 if ac_check("maria read deal1 (manager chain)" as *u8, ac_can(ctx, "maria" as *u8, 1, deal1), 1) == 0 { ok = 0 }
134 if ac_check("maria write deal1 (hierarchy = read-only v1)" as *u8, ac_can(ctx, "maria" as *u8, 2, deal1), 0) == 0 { ok = 0 }
135 if ac_check("dana read deal1 (admin in tenant)" as *u8, ac_can(ctx, "dana" as *u8, 1, deal1), 1) == 0 { ok = 0 }
136 if ac_check("eve read deal1 (ADMIN of OTHER tenant -- wall beats admin)" as *u8, ac_can(ctx, "eve" as *u8, 1, deal1), 0) == 0 { ok = 0 }
137 if ac_check("bob read note1 (OWD read)" as *u8, ac_can(ctx, "bob" as *u8, 1, note1), 1) == 0 { ok = 0 }
138 if ac_check("alice write note1 (OWD read only)" as *u8, ac_can(ctx, "alice" as *u8, 2, note1), 0) == 0 { ok = 0 }
139 if ac_check("bob write doc1 (OWD write)" as *u8, ac_can(ctx, "bob" as *u8, 2, doc1), 1) == 0 { ok = 0 }
140 if ac_check("ghost read note1 (unknown user)" as *u8, ac_can(ctx, "ghost" as *u8, 1, note1), 0) == 0 { ok = 0 }
141 // explicit share flips bob's access to deal1
142 sr[0]="deal1" as *u8 as i64; su[0]="bob" as *u8 as i64; sl[0]=2; ctx[8]=1
143 if ac_check("bob write deal1 (explicit share lvl2)" as *u8, ac_can(ctx, "bob" as *u8, 2, deal1), 1) == 0 { ok = 0 }
144 // field-level security: giving amounts need manager+
145 if ac_field(ctx, "bob" as *u8, 3) != 0 { ok = 0 }
146 if ac_field(ctx, "maria" as *u8, 3) != 1 { ok = 0 }
147 p(" ACL-ROLE field-level: giving-amount minrole=3 bob=DENY maria=ALLOW\n" as *u8)
148
149 p("NX-CRM-ACL-SELFTEST checks=13 " as *u8)
150 if ok == 1 { p("verdict=GREEN\n" as *u8); return 0 }
151 p("verdict=RED\n" as *u8)
152 return 1
153}