s3:ntlm_auth: make logs more consistent with length check
[Samba.git] / python / samba / tests / conditional_ace_claims.py
blob881f87581cffad567b7e17a9fe6b649b2efdddb8
1 # Unix SMB/CIFS implementation.
2 # Copyright © Catalyst IT 2023
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 3 of the License, or
7 # (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18 """Tests for Conditional ACEs, claims, and security tokens."""
20 import random
21 from samba.dcerpc import security
22 from samba.security import access_check
23 from samba.tests.token_factory import token as Token
24 from samba.tests.token_factory import list_to_claim
25 from samba.dcerpc.security import CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1
26 from samba.tests import TestCase, DynamicTestCase, get_env_dir
27 from samba.colour import c_RED
28 import os
29 from samba import NTSTATUSError
30 from samba.ntstatus import NT_STATUS_ACCESS_DENIED
32 DEFAULT_ACCESS = security.SEC_FILE_ALL
33 DEFAULT_ACCESS2 = (security.SEC_STD_READ_CONTROL |
34 security.SEC_ADS_LIST |
35 security.SEC_ADS_READ_PROP)
38 def write_c_test_on_failure(f):
39 """This is a function decorator that writes a function for
40 /libcli/security/tests/test_run_conditional_ace.c that runs the
41 equivalent test. Why?! Because iterating over a test to debug the
42 failure is slower in Python tests, but adding new tests is faster
43 in Python. So the flow goes like this:
45 1. add python tests, run them
46 2. if nothing fails, goto 1
47 3. copy the test_something() text into test_run_conditional_ace.c,
48 rename it, and add it to main().
49 4. `make bin/test_run_conditional_ace && rr bin/test_run_conditional_ace`
50 5. `rr replay`
52 and you're away. You can also just work from the Python, but a few
53 runs of `make -j` after touching something in libcli/security will
54 make you see why this exists.
56 You might be thinking that this surely took longer to write than
57 waiting 100 times for a 30 second compile, but that misses the
58 point that debugging needs to be ergonomic and fun.
59 """
60 from json import dumps as q # JSON quoting is C quoting, more or less
62 def wrapper(name, token, sddl, access_desired):
63 try:
64 f(name, token, sddl, access_desired)
65 except Exception:
66 print()
67 print('static void test_something(void **state)')
68 print('{')
69 print('\tINIT();')
70 for s in ('sids', 'device_sids'):
71 if s in token:
72 macro = ('user_sids' if s == 'sids' else s).upper()
73 v = ', '.join(q(x) for x in token[s])
74 print(f'\t{macro}({v});')
75 for s in ('user_claims', 'device_claims'):
76 if s in token:
77 macro = s.upper()
78 for name, values in token[s].items():
79 if isinstance(values,
80 CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1):
81 v = '...'
82 else:
83 if not isinstance(values, (list, tuple)):
84 values = [values]
85 v = ', '.join(q(x) for x in values)
86 v = q(f"{v}")
87 print(f'\t{macro}({q(name)}, {v});')
88 print(f'\tSD({q(sddl)});')
89 if 'allow' in f.__name__:
90 print(f'\tALLOW_CHECK({access_desired:#x});')
91 else:
92 print(f'\tDENY_CHECK({access_desired:#x});')
93 print('}')
94 print()
95 raise
96 return wrapper
99 class ConditionalAceClaimsBase(TestCase):
100 maxDiff = 0
102 @classmethod
103 def setUpDynamicTestCases(cls):
104 cls.domain_sid = security.dom_sid("S-1-22-333-4444")
105 seen = set()
107 for i, row in enumerate(cls.data):
108 token, sddl, access_desired = row
109 name = f'{i+1:03}-{token}-{sddl}-{access_desired}'
110 if len(name) > 150:
111 name = f"{name[:125]}+{len(name) - 125}-more-characters"
113 if name in seen:
114 print(f"seen {row} after {len(seen)}")
115 seen.add(name)
117 if cls.allow:
118 cls.generate_dynamic_test('test_allow',
119 name, token, sddl, access_desired)
120 else:
121 cls.generate_dynamic_test('test_deny',
122 name, token, sddl, access_desired)
124 fuzz_seed_dir = get_env_dir('SAMBA_WRITE_FUZZ_STRINGS_DIR')
125 if fuzz_seed_dir is not None:
126 cls._write_sddl_strings_for_fuzz_seeds(fuzz_seed_dir)
128 @classmethod
129 def _write_sddl_strings_for_fuzz_seeds(cls, fuzz_seed_dir):
130 """write all the SDDL strings we have into a directory as individual
131 files, using a naming convention beloved of fuzzing engines.
133 To run this set an environment variable; see
134 cls.setUpDynamicTestCases(), below.
136 Note this will only run in subclasses annotated with @DynamicTestCase.
138 from hashlib import md5
139 for _, sddl, _ in cls.data:
140 name = md5(sddl.encode()).hexdigest()
141 with open(os.path.join(fuzz_seed_dir, name), 'w') as f:
142 f.write(sddl)
144 @write_c_test_on_failure
145 def _test_allow_with_args(self, _token, sddl, access_desired):
146 if isinstance(_token, dict):
147 token = Token(**_token)
148 else:
149 token = _token
150 sd = security.descriptor.from_sddl(sddl, self.domain_sid)
151 try:
152 granted = access_check(sd, token, access_desired)
153 except NTSTATUSError as e:
154 print(c_RED(sddl))
155 print(c_RED(_token))
156 if e.args[0] != NT_STATUS_ACCESS_DENIED:
157 raise
158 self.fail("access was denied")
160 self.assertEqual(granted, access_desired)
162 @write_c_test_on_failure
163 def _test_deny_with_args(self, token, sddl, access_desired):
164 if isinstance(token, dict):
165 token = Token(**token)
166 sd = security.descriptor.from_sddl(sddl, self.domain_sid)
167 try:
168 granted = access_check(sd, token, access_desired)
169 except NTSTATUSError as e:
170 if e.args[0] == NT_STATUS_ACCESS_DENIED:
171 return
172 self.fail(f"failed with {e}, not access denied")
174 self.fail("access allowed")
177 @DynamicTestCase
178 class AllowTests(ConditionalAceClaimsBase):
179 name = "allow"
180 allow = True
181 data = [
182 ( # device_claims
183 {'sids': ['WD', 'AA'],
184 'device_claims': {"colour":["orange", "blue"]}},
185 ('D:(XA;;0x1f;;;AA;'
186 '(@Device.colour == {"orange", "blue"}))'),
187 0x10),
188 ( # device_claims, int >=
189 {'sids': ['WD', 'AA'],
190 'device_claims': {"legs": 4}},
191 ('D:(XA;;0x1f;;;AA;(@Device.legs >= 1))'),
192 0x10),
193 ( # device_claims, int
194 {'sids': ['WD', 'AA'],
195 'device_claims': {"legs": 1}},
196 ('D:(XA;;0x1f;;;AA;(@Device.legs == 1))'),
197 0x10),
198 ( # device_member_of && member_of
199 {'sids': ['WD', 'AA'],
200 'device_sids': ['BA', 'BG']},
201 ("D:(XA;;0x1f;;;AA;"
202 "(Device_Member_of{SID(BA)} && Member_of{SID(WD)}))"),
203 0x10),
204 ( # device_member_of || member_of, both true
205 {'sids': ['WD', 'AA'],
206 'device_sids': ['BA', 'BG']},
207 ("D:(XA;;0x1f;;;AA;"
208 "(Device_Member_of{SID(AA)} || Member_of{SID(WD)}))"),
209 0x10),
210 ( # device_member_of || member_of, second true
211 {'sids': ['WD', 'AA'],
212 'device_sids': ['BA', 'BG']},
213 ("D:(XA;;0x1f;;;AA;"
214 "(Device_Member_of{SID(AA)} || Member_of{SID(WD)}))"),
215 0x10),
216 ( # device_member_of || member_of, first true
217 {'sids': ['WD', 'AA'],
218 'device_sids': ['BA', 'BG']},
219 ("D:(XA;;0x1f;;;AA;"
220 "(Device_Member_of{SID(BG)} || Member_of{SID(WR)}))"),
221 0x10),
222 ( # single SID, Member_of_Any
223 {'sids': ['S-1-222-333']},
224 ("D:(XA;;0x1ff;;;S-1-222-333;(Member_of_Any{SID(S-1-222-333)}))"),
225 0x1),
226 ({'sids': ['S-1-1-0']}, "O:S-1-1-0D:(A;;0x1ff;;;WD)", DEFAULT_ACCESS),
227 ({'sids': ['S-1-1-0']},
228 "O:S-1-1-0D:(XA;;0x1ff;;;WD;(Member_of{SID(S-1-1-0)}))",
229 DEFAULT_ACCESS),
230 ({'sids': ['S-1-1-0', 'S-1-222-333']},
231 "O:S-1-1-0D:(XA;;0x1ff;;;WD;(Member_of{SID(S-1-1-0)}))",
232 DEFAULT_ACCESS),
233 ({'sids': ['WD', 'S-1-222-333']},
234 "O:S-1-1-0D:(XA;;0x1ff;;;WD;(Member_of{SID(S-1-1-0)}))",
235 DEFAULT_ACCESS),
236 ( # a single SID, not a composite
237 {'sids': ['S-1-1-0', 'S-1-222-333']},
238 "O:S-1-1-0D:(XA;;0x1ff;;;WD;(Member_of SID(S-1-1-0)))",
239 DEFAULT_ACCESS),
240 ( # a single SID, not a composite, without space after Member_of
241 {'sids': ['S-1-1-0', 'S-1-222-333']},
242 "O:S-1-1-0D:(XA;;0x1ff;;;WD;(Member_of\nSID(S-1-1-0)))",
243 DEFAULT_ACCESS),
244 ( # a single SID, not a composite, Member_of_Any
245 {'sids': ['S-1-1-0', 'S-1-222-333']},
246 "O:S-1-1-0D:(XA;;0x1ff;;;WD;(Member_of_Any SID(S-1-1-0)))",
247 DEFAULT_ACCESS),
248 ( # Member_of_Any
249 {'sids': ['S-1-1-0', 'S-1-222-333']},
250 "O:S-1-1-0D:(XA;;0x1;;;WD;(Member_of_Any{SID(AS),SID(WD)}))",
251 0x1),
252 ({'sids': ['S-1-1-0', 'S-1-222-333']},
253 ("O:S-1-1-0D:"
254 "(XA;;0x1ff;;;WD;(Member_of_Any{SID(S-1-1-0), SID(S-1-222-333)}))"),
255 DEFAULT_ACCESS),
256 ({'sids': ['S-1-1-0', 'S-1-222-333']},
257 ("O:S-1-1-0D:"
258 "(XA;;0x1ff;;;WD;(Member_of_Any{SID(S-1-1-334), SID(S-1-222-333)}))"),
259 DEFAULT_ACCESS),
260 ({'sids': ['S-1-1-0', 'S-1-222-333']},
261 ("D:(XA;;0x1ff;;;WD;(Member_of_Any{SID(S-1-222-333)}))"),
262 DEFAULT_ACCESS),
263 ({'sids': ['S-1-77-88-99', 'AA']},
264 "D:(XA;;0x1f;;;AA;(Member_of{SID(S-1-77-88-99)}))",
265 0x10),
266 ( # device_member_of
267 {'sids': ['WD', 'AA'],
268 'device_sids': ['BA', 'BG']},
269 "D:(XA;;0x1f;;;AA;(Device_Member_of{SID(BA)}))",
270 0x10),
271 ( # device_member_of
272 {'sids': ['WD', 'AA'],
273 'device_sids': ['BA', 'BG']},
274 "D:(XA;;0x1f;;;AA;(Device_Member_of{SID(BA)}))",
275 0x10),
276 ( # not (!) member_of
277 {'sids': ['WD', 'AA'],
278 'device_sids': ['BA', 'BG']},
279 "D:(XA;;0x1f;;;AA;(! (Member_of{SID(BA)})))",
280 0x10),
281 ( # not not (!!) member_of
282 {'sids': ['WD', 'AA'],
283 'device_sids': ['BA', 'BG']},
284 "D:(XA;;0x1f;;;AA;(!(! (Member_of{SID(AA)}))))",
285 0x10),
286 ( # not * 8 (!!!! !!!!) member_of
287 {'sids': ['WD', 'AA'],
288 'device_sids': ['BA', 'BG']},
289 "D:(XA;;0x1f;;;AA;(!(!(!(!(!(!(!(!( Member_of{SID(AA)}))))))))))",
290 0x10),
291 ( # not * 9 (!!! !!! !!!) member_of
292 {'sids': ['WD', 'AA'],
293 'device_sids': ['BA', 'BG']},
294 "D:(XA;;0x1f;;;AA;(!(!(!( !(!(!( !(!(!(Member_of{SID(BA)})))))))))))",
295 0x10),
296 ( # not * 9 (!!! !!! !!!) Not_Member_of
297 {'sids': ['WD', 'AA'],
298 'device_sids': ['BA', 'BG']},
299 ("D:(XA;;0x1f;;;AA;"
300 "(!(!(!( !(!(!( !(!(!( Not_Member_of{SID(AA)})))))))))))"),
301 0x10),
302 ( #resource ACE
303 {'sids': ['WD', 'AA'],
304 'device_claims': {"colour": ["blue"]}},
305 ('D:(XA;;0x1f;;;AA;(@Device.colour Contains @Resource.colour))'
306 'S:(RA;;;;;WD;("colour",TS,0,"blue"))'),
307 0x10),
308 ( #resource ACE ==
309 {'sids': ['WD', 'AA'],
310 'device_claims': {"colour": ["blue"]}},
311 ('D:(XA;;0x1f;;;AA;(@Device.colour == @Resource.colour))'
312 'S:(RA;;;;;WD;("colour",TS,0,"blue"))'),
313 0x10),
314 ( # device_claims, comparing single to single
315 {'sids': ['WD', 'AA'],
316 'device_claims': {"colour": "blue"}},
317 ('D:(XA;;0x1f;;;AA;(@Device.colour == "blue"))'),
318 0x10),
319 ( # device_claims == user_claims
320 {'sids': ['WD', 'AA'],
321 'user_claims': {"colour": "blue"},
322 'device_claims': {"colour": "blue"}},
323 ('D:(XA;;0x1f;;;AA;(@User.colour == @Device.colour))'),
324 0x10),
325 ( #resource ACE multi
326 {'sids': ['WD', 'AA'],
327 'device_claims': {"colour": ["blue", "red"]}},
328 ('D:(XA;;0x1f;;;AA;(@Device.colour Contains @Resource.colour))'
329 'S:(RA;;;;;WD;("colour",TS,0,"blue", "red"))'),
330 0x10),
334 @DynamicTestCase
335 class DenyTests(ConditionalAceClaimsBase):
336 name = "allow"
337 allow = False
338 data = [
339 ({}, "", DEFAULT_ACCESS),
340 ({'sids': ['S-1-1-0']}, "O:S-1-1-0D:(A;;0x1fe;;;WD)", DEFAULT_ACCESS),
341 ({}, "O:WDD:(A;;GACR;;;CO)", DEFAULT_ACCESS),
342 ({'sids': ['S-1-1-0', 'S-1-222-444']},
343 ("D:(XA;;0x1ff;;;WD;(Member_of_Any{SID(S-1-222-333)}))"),
344 0x1),
345 ( # Without explicit 'everyone' SID in list of SIDs, this is
346 # denied because the ACE SID 'WD' doesn't match.
347 {'sids': ['S-1-222-333']},
348 ("D:(XA;;0x1ff;;;WD;(Member_of_Any{SID(S-1-222-333)}))"),
349 0x1),
350 ( # device_member_of && member_of, both false
351 {'sids': ['WD', 'AA'],
352 'device_sids': ['BA', 'BG']},
353 ("D:(XA;;0x1f;;;AA;"
354 "(Device_Member_of{SID(AA)} && Member_of{SID(WR)}))"),
355 0x10),
356 ( # device_member_of && member_of, first false
357 {'sids': ['WD', 'AA'],
358 'device_sids': ['BA', 'BG']},
359 ("D:(XA;;0x1f;;;AA;"
360 "(Device_Member_of{SID(AA)} && Member_of{SID(WD)}))"),
361 0x10),
362 ( # device_member_of && member_of, second false
363 {'sids': ['WD', 'AA'],
364 'device_sids': ['BA', 'BG']},
365 ("D:(XA;;0x1f;;;AA;"
366 "(Device_Member_of{SID(BA)} && Member_of{SID(BA)}))"),
367 0x10),
368 ( # device_member_of || member_of, both false
369 {'sids': ['WD', 'AA'],
370 'device_sids': ['BA', 'BG']},
371 ("D:(XA;;0x1f;;;AA;"
372 "(Device_Member_of{SID(AA)} || Member_of{SID(WR)}))"),
373 0x10),
374 ( # device_claims, comparing composite to single
375 {'sids': ['WD', 'AA'],
376 'device_claims': {"colour": ["orange", "blue"]}},
377 ('D:(XA;;0x1f;;;AA;(@Device.colour == "blue"))'),
378 0x10),
379 ( # not (!) member_of
380 {'sids': ['WD', 'AA'],
381 'device_sids': ['BA', 'BG']},
382 "D:(XA;;0x1f;;;AA;(! (Member_of{SID(AA)})))",
383 0x10),
384 ( # not not (!!) member_of
385 {'sids': ['WD', 'AA'],
386 'device_sids': ['BA', 'BG']},
387 "D:(XA;;0x1f;;;AA;(!(!( Member_of{SID(BA)}))))",
388 0x10),
389 ( # not * 8 (!!!! !!!!) member_of
390 {'sids': ['WD', 'AA'],
391 'device_sids': ['BA', 'BG']},
392 "D:(XA;;0x1f;;;AA;(!(!( !(!( !(!( !(!(Member_of{SID(BA)}))))))))))",
393 0x10),
394 ( # not * 3 (!!!) member_of
395 {'sids': ['WD', 'AA'],
396 'device_sids': ['BA', 'BG']},
397 "D:(XA;;0x1f;;;AA;(!(!(!(Member_of{SID(AA)})))))",
398 0x10),
399 ( # not * 3 (!!!) Not_Member_of
400 {'sids': ['WD', 'AA'],
401 'device_sids': ['BA', 'BG']},
402 "D:(XA;;0x1f;;;AA;(!(!(!(Not_Member_of{SID(BA)})))))",
403 0x10),
407 def _int_range(n, n_dupes=0, random_seed=None):
408 """Makes a list of stringified integers.
410 If n_unique is specified and less than n, there will be that many unique
411 values (and hence some duplicates). If random_seed is set, the list will be
412 shuffled.
414 claims = [str(x) for x in range(n)]
416 if random_seed is None:
417 if n_dupes:
418 claims *= 1 + (n + n_dupes) // n
419 return claims[:n + n_dupes]
421 random.seed(random_seed)
422 for i in range(n_dupes):
423 # this purposefully skews the distribution.
424 claims.append(random.choice(claims))
426 random.shuffle(claims)
427 return claims
430 def _str_range(n, n_dupes=0, random_seed=None, mix_case=False):
431 """Create a list of strings with somewhat controllable disorder.
433 ints = _int_range(n, n_dupes, random_seed)
434 claims = [f'a{i}' for i in ints]
436 if mix_case:
437 if random_seed is None:
438 random.seed(0)
439 for i in range(len(claims)):
440 if random.random() < 0.5:
441 claims[i] = claims[i].upper()
443 return claims
446 def claim_str_range(*args, name="foo", case_sensitive=False, **kwargs):
447 """String value range as a CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1"""
448 vals = _str_range(*args, **kwargs)
449 claim = list_to_claim(name, vals, case_sensitive=case_sensitive)
450 return claim
453 def claim_int_range(*args, name="foo", case_sensitive=False, **kwargs):
454 """Int value range as a CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1"""
455 vals = _int_range(*args, **kwargs)
456 claim = list_to_claim(name, vals, case_sensitive=case_sensitive)
457 return claim
460 def ra_str_range(*args, name="foo", case_sensitive=False, **kwargs):
461 """Make a string claim as a resource attribute"""
462 claim = _str_range(*args, **kwargs)
463 values = '","'.join(claim)
464 c = (2 if case_sensitive else 0)
465 return f'(RA;;;;;WD;("{name}",TS,{c},"{values}"))'
468 def ra_int_range(*args, name="foo", unsigned=False, **kwargs):
469 """Return an integer claim range as a resource attribute."""
470 ints = _int_range(*args, **kwargs)
471 values = ','.join(str(x) for x in ints)
472 return f'(RA;;;;;WD;("{name}",T{"U" if unsigned else "I"},0,{values}))'
475 def composite_int(*args, **kwargs):
476 """Integer conditional ACE composite"""
477 claim = _int_range(*args, **kwargs)
478 values = ', '.join(claim)
479 return '{' + values + '}'
482 def composite_str(*args, **kwargs):
483 """String conditional ACE composite"""
484 claim = _str_range(*args, **kwargs)
485 values = '", "'.join(claim)
486 return '{"' + values + '"}'
489 @DynamicTestCase
490 class ConditionalAceLargeComposites(ConditionalAceClaimsBase):
491 """Here we are dynamically generating claims and composites with large numbers
492 of members, and using them in comparisons. Sometimes the comparisons are
493 meant to fail, and sometimes not.
495 maxDiff = 0
497 @classmethod
498 def setUpDynamicTestCases(cls):
499 cls.domain_sid = security.dom_sid("S-1-22-333-4444")
500 for i, row in enumerate(cls.data):
501 name, allow, token, sddl = row
502 name = f'{i+1:03}-{name}'
503 if 'sids' not in token:
504 token['sids'] = ['AU', 'WD']
505 if allow:
506 cls.generate_dynamic_test('test_allow',
507 name, token, sddl, 0x10)
508 else:
509 cls.generate_dynamic_test('test_deny',
510 name, token, sddl, 0x10)
512 fuzz_seed_dir = get_env_dir('SAMBA_WRITE_FUZZ_STRINGS_DIR')
513 if fuzz_seed_dir is not None:
514 cls._write_sddl_strings_for_fuzz_seeds(fuzz_seed_dir)
517 data = [
519 "90-disorderly-strings-claim-vs-claim-case-sensitive-with-dupes",
520 False,
521 {'user_claims': {"c": claim_str_range(90,
522 random_seed=2),
523 "d": claim_str_range(90, 90,
524 case_sensitive=True,
525 random_seed=3)}},
526 ('D:(XA;;FA;;;WD;(@USER.c == @USER.d))')
529 # this one currently fails before we get to compare_composites()
530 "0-vs-0",
531 True,
532 {'user_claims': {"c": claim_str_range(0)}},
533 ('D:(XA;;FA;;;WD;(@USER.c == @USER.c))')
536 "50-orderly-strings",
537 True,
538 {'user_claims': {"c": claim_str_range(50)}},
539 (f'D:(XA;;FA;;;WD;(@USER.c == {composite_str(50)}))')
542 "50-disorderly-strings-same-disorder",
543 True,
544 {'user_claims': {"c": claim_str_range(50, random_seed=1)}},
545 (f'D:(XA;;FA;;;WD;(@USER.c == {composite_str(50, random_seed=1)}))')
548 "200-disorderly-strings",
549 True,
550 {'user_claims': {"c": claim_str_range(200, random_seed=1)}},
551 (f'D:(XA;;FA;;;WD;(@USER.c == {composite_str(200, random_seed=2)}))')
554 "50-orderly-vs-disorderly-strings",
555 True,
556 {'user_claims': {"c": claim_str_range(50)}},
557 (f'D:(XA;;FA;;;WD;(@USER.c == {composite_str(50, random_seed=1)}))')
560 "50-disorderly-vs-orderly-strings",
561 True,
562 {'user_claims': {"c": claim_str_range(50, random_seed=1)}},
563 (f'D:(XA;;FA;;;WD;(@USER.c == {composite_str(50)}))')
566 "99-orderly-strings",
567 True,
568 {'user_claims': {"c": claim_str_range(99)}},
569 (f'D:(XA;;FA;;;WD;(@USER.c == {composite_str(99)}))')
572 "99-disorderly-strings",
573 True,
574 {'user_claims': {"c": claim_str_range(99, random_seed=1)}},
575 (f'D:(XA;;FA;;;WD;(@USER.c == {composite_str(99, random_seed=2)}))')
578 "99-orderly-vs-disorderly-strings",
579 True,
580 {'user_claims': {"c": claim_str_range(99)}},
581 (f'D:(XA;;FA;;;WD;(@USER.c == {composite_str(99, random_seed=1)}))')
584 "99-disorderly-vs-orderly-strings",
585 True,
586 {'user_claims': {"c": claim_str_range(99, random_seed=1)}},
587 (f'D:(XA;;FA;;;WD;(@USER.c == {composite_str(99)}))')
590 "39-orderly-strings-vs-39+60-dupes",
591 True,
592 {'user_claims': {"c": claim_str_range(39)}},
593 (f'D:(XA;;FA;;;WD;(@USER.c == {composite_str(39, 60)}))')
596 "39-disorderly-strings-vs-39+60-dupes",
597 True,
598 {'user_claims': {"c": claim_str_range(39, random_seed=1)}},
599 (f'D:(XA;;FA;;;WD;(@USER.c == {composite_str(39, 60, random_seed=1)}))')
602 "39-orderly-vs-disorderly-strings-vs-39+60-dupes",
603 True,
604 {'user_claims': {"c": claim_str_range(39)}},
605 (f'D:(XA;;FA;;;WD;(@USER.c == {composite_str(39, 60, random_seed=1)}))')
608 "39-disorderly-vs-orderly-strings-vs-39+60-dupes",
609 True,
610 {'user_claims': {"c": claim_str_range(39, random_seed=1)}},
611 (f'D:(XA;;FA;;;WD;(@USER.c == {composite_str(39, 60)}))')
614 "3-orderly-strings-vs-3+60-dupes",
615 True,
616 {'user_claims': {"c": claim_str_range(3)}},
617 (f'D:(XA;;FA;;;WD;(@USER.c == {composite_str(3, 60)}))')
620 "3-disorderly-strings-vs-3+60-dupes",
621 True,
622 {'user_claims': {"c": claim_str_range(3, random_seed=1)}},
623 (f'D:(XA;;FA;;;WD;(@USER.c == {composite_str(3, 60, random_seed=1)}))')
626 "3-orderly-vs-disorderly-strings-vs-3+60-dupes",
627 True,
628 {'user_claims': {"c": claim_str_range(3)}},
629 (f'D:(XA;;FA;;;WD;(@USER.c == {composite_str(3, 60, random_seed=1)}))')
632 "3-disorderly-vs-orderly-strings-vs-3+60-dupes",
633 True,
634 {'user_claims': {"c": claim_str_range(3, random_seed=1)}},
635 (f'D:(XA;;FA;;;WD;(@USER.c == {composite_str(3, 60)}))')
638 "3-orderly-strings-vs-3+61-dupes",
639 True,
640 {'user_claims': {"c": claim_str_range(3)}},
641 (f'D:(XA;;FA;;;WD;(@USER.c == {composite_str(3, 61)}))')
645 "63-orderly-strings-vs-62+1-dupe",
646 False,
647 {'user_claims': {"c": claim_str_range(63)}},
648 (f'D:(XA;;FA;;;WD;(@USER.c == {composite_str(62, 1)}))')
651 "102+1-dupe-vs-102+1-dupe",
652 False,
653 # this is an invalid claim
654 {'user_claims': {"c": claim_str_range(102, 1)}},
655 (f'D:(XA;;FA;;;WD;(@USER.c == {composite_str(102, 1)}))')
658 "0-vs-1",
659 False,
660 {'user_claims': {"c": claim_str_range(0),
661 "d": claim_str_range(1)}},
662 ('D:(XA;;FA;;;WD;(@USER.c == @USER.d))')
665 "2+1-dupe-vs-2+1-dupe",
666 False,
667 {'user_claims': {"c": claim_str_range(2, 1)}},
668 (f'D:(XA;;FA;;;WD;(@USER.c == {composite_str(2, 1)}))')
671 "63-disorderly-strings-vs-62+1-dupe",
672 False,
673 {'user_claims': {"c": claim_str_range(63, random_seed=1)}},
674 (f'D:(XA;;FA;;;WD;(@USER.c == {composite_str(62, 1, random_seed=1)}))')
677 "63-disorderly-strings-vs-63+800-dupe",
678 True,
679 {'user_claims': {"c": claim_str_range(63, random_seed=1)}},
680 (f'D:(XA;;FA;;;WD;(@USER.c == {composite_str(63, 800, random_seed=1)}))')
683 "63-disorderly-strings-vs-62+800-dupe",
684 False,
685 {'user_claims': {"c": claim_str_range(63, random_seed=1)}},
686 (f'D:(XA;;FA;;;WD;(@USER.c == {composite_str(62, 800, random_seed=1)}))')
689 "9-orderly-strings",
690 True,
691 {'user_claims': {"c": claim_str_range(9)}},
692 (f'D:(XA;;FA;;;WD;(@USER.c == {composite_str(9)}))')
695 "9-orderly-strings-claim-vs-itself",
696 True,
697 {'user_claims': {"c": claim_str_range(9)}},
698 ('D:(XA;;FA;;;WD;(@USER.c == @USER.c))')
701 "300-orderly-strings-claim-vs-itself",
702 True,
703 {'user_claims': {"c": claim_str_range(300)}},
704 ('D:(XA;;FA;;;WD;(@USER.c == @USER.c))')
707 "900-disorderly-strings-claim-vs-claim",
708 True,
709 {'user_claims': {"c": claim_str_range(900, random_seed=1),
710 "d": claim_str_range(900, random_seed=1)}},
711 ('D:(XA;;FA;;;WD;(@USER.c == @USER.d))')
714 "9-orderly-strings-claim-mixed-case-vs-claim-case-sensitive",
715 False,
716 {'user_claims': {"c": claim_str_range(9, mix_case=True),
717 "d": claim_str_range(9, case_sensitive=True)}},
718 ('D:(XA;;FA;;;WD;(@USER.c == @USER.d))')
721 "9-disorderly-strings-claim-vs-claim-case-sensitive-mixed-case",
722 False,
723 {'user_claims': {"c": claim_str_range(9,random_seed=1),
724 "d": claim_str_range(9,
725 mix_case=True,
726 case_sensitive=True)}},
727 ('D:(XA;;FA;;;WD;(@USER.c == @USER.d))')
730 "9-disorderly-strings-claim-vs-claim-case-sensitive-both-mixed-case",
731 False,
732 {'user_claims': {"c": claim_str_range(9,
733 mix_case=True,
734 random_seed=1),
735 "d": claim_str_range(9,
736 mix_case=True,
737 case_sensitive=True)}},
738 ('D:(XA;;FA;;;WD;(@USER.c == @USER.d))')
741 "9-disorderly-strings-claim-vs-claim-case-sensitive-ne",
742 True,
743 {'user_claims': {"c": claim_str_range(9,random_seed=1),
744 "d": claim_str_range(9,
745 mix_case=True,
746 case_sensitive=True)}},
747 ('D:(XA;;FA;;;WD;(@USER.c != @USER.d))')
751 "5-disorderly-strings-claim-vs-claim-case-sensitive-with-dupes-all-mixed-case",
752 False,
753 {'user_claims': {"c": claim_str_range(5,
754 mix_case=True,
755 random_seed=2),
756 "d": claim_str_range(5, 5,
757 mix_case=True,
758 random_seed=3,
759 case_sensitive=True)}},
760 ('D:(XA;;FA;;;WD;(@USER.c == @USER.d))')
763 "90-disorderly-strings-claim-vs-int-claim",
764 False,
765 {'user_claims': {"c": claim_str_range(90,
766 random_seed=2),
767 "d": claim_int_range(90,
768 random_seed=3)}},
769 ('D:(XA;;FA;;;WD;(@USER.c == @USER.d))')
772 "90-disorderly-ints-claim-vs-string-claim",
773 False,
774 {'user_claims': {"c": claim_int_range(90,
775 random_seed=2),
776 "d": claim_str_range(90,
777 random_seed=3)}},
778 ('D:(XA;;FA;;;WD;(@USER.c == @USER.d))')
781 "9-disorderly-strings-vs-9+90-dupes",
782 True,
783 {'user_claims': {"c": claim_str_range(9, random_seed=1)}},
784 (f'D:(XA;;FA;;;WD;(@USER.c == {composite_str(9, 90, random_seed=1)}))')
787 "9-disorderly-strings-vs-9+90-dupes-case-sensitive",
788 True,
789 {'user_claims': {"c": claim_str_range(9, random_seed=1, case_sensitive=True)}},
790 (f'D:(XA;;FA;;;WD;(@USER.c == {composite_str(9, 90, random_seed=2)}))')
793 "9-disorderly-strings-vs-9+90-dupes-mixed-case",
794 True,
795 {'user_claims': {"c": claim_str_range(9, random_seed=1, mix_case=True)}},
796 (f'D:(XA;;FA;;;WD;(@USER.c == {composite_str(9, 90, random_seed=2, mix_case=True)}))')
799 "9-disorderly-strings-vs-9+90-dupes-mixed-case-case-sensitive",
800 False,
801 {'user_claims': {"c": claim_str_range(9, random_seed=1, mix_case=True,
802 case_sensitive=True)}},
803 (f'D:(XA;;FA;;;WD;(@USER.c == {composite_str(9, 90, random_seed=2, mix_case=True)}))')
806 "99-disorderly-strings-vs-9+90-dupes-mixed-case",
807 False,
808 {'user_claims': {"c": claim_str_range(99, random_seed=1, mix_case=True)}},
809 (f'D:(XA;;FA;;;WD;(@USER.c == {composite_str(9, 90, random_seed=2, mix_case=True)}))')
813 "RA-99-disorderly-strings-vs-9+90-dupes-mixed-case",
814 False,
816 ('D:(XA;;FA;;;WD;(@RESOURCE.c == '
817 f'{composite_str(9, 90, random_seed=1, mix_case=True)}))'
818 f'S:{ra_str_range(99, random_seed=2, mix_case=True)}'
822 "RA-9+90-dupes-disorderly-strings-vs-9+90-dupes-mixed-case",
823 False,
825 ('D:(XA;;FA;;;WD;(@RESOURCE.c == '
826 f'{composite_str(9, 90, random_seed=1, mix_case=True)}))'
827 f'S:{ra_str_range(9, 90, random_seed=2, mix_case=True)}'
831 "90-disorderly-strings-claim-vs-missing-claim",
832 False,
833 {'user_claims': {"c": claim_str_range(90,
834 random_seed=2)}},
835 ('D:(XA;;FA;;;WD;(@USER.c == @USER.d))')
838 "missing-claim-vs-90-disorderly-strings",
839 False,
840 {'user_claims': {"c": claim_str_range(90,
841 random_seed=2)}},
842 ('D:(XA;;FA;;;WD;(@USER.z == @USER.c))')
846 "RA-9-disorderly-strings-vs-9-mixed-case",
847 False,
848 {'user_claims': {"c": claim_str_range(9,
849 random_seed=1,
850 mix_case=True),
853 ('D:(XA;;FA;;;WD;(@RESOURCE.c == @User.c))'
854 f'S:{ra_str_range(9, random_seed=2, mix_case=True)}'
859 "9-disorderly-strings-vs-9-RA-mixed-case",
860 False,
861 {'user_claims': {"c": claim_str_range(9,
862 random_seed=1,
863 mix_case=True),
866 ('D:(XA;;FA;;;WD;(@user.c == @resource.c))'
867 f'S:{ra_str_range(9, random_seed=2, mix_case=True)}'
872 "RA-29-disorderly-strings-vs-29-mixed-case",
873 False,
874 {'user_claims': {"c": claim_str_range(29,
875 random_seed=1,
876 mix_case=True),
879 ('D:(XA;;FA;;;WD;(@RESOURCE.c == @User.c))'
880 f'S:{ra_str_range(29, random_seed=2, mix_case=True)}'
884 "0-vs-0-ne",
885 False,
886 {'user_claims': {"c": claim_str_range(0)}},
887 ('D:(XA;;FA;;;WD;(@USER.c != @USER.c))')
890 "1-vs-1",
891 True,
892 {'user_claims': {"c": claim_str_range(1)}},
893 ('D:(XA;;FA;;;WD;(@USER.c == @USER.c))')
896 "1-vs-1-ne",
897 False,
898 {'user_claims': {"c": claim_str_range(1)}},
899 ('D:(XA;;FA;;;WD;(@USER.c != @USER.c))')