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."""
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
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`
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.
60 from json
import dumps
as q
# JSON quoting is C quoting, more or less
62 def wrapper(name
, token
, sddl
, access_desired
):
64 f(name
, token
, sddl
, access_desired
)
67 print('static void test_something(void **state)')
70 for s
in ('sids', 'device_sids'):
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'):
78 for name
, values
in token
[s
].items():
80 CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1
):
83 if not isinstance(values
, (list, tuple)):
85 v
= ', '.join(q(x
) for x
in values
)
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});')
92 print(f
'\tDENY_CHECK({access_desired:#x});')
99 class ConditionalAceClaimsBase(TestCase
):
103 def setUpDynamicTestCases(cls
):
104 cls
.domain_sid
= security
.dom_sid("S-1-22-333-4444")
107 for i
, row
in enumerate(cls
.data
):
108 token
, sddl
, access_desired
= row
109 name
= f
'{i+1:03}-{token}-{sddl}-{access_desired}'
111 name
= f
"{name[:125]}+{len(name) - 125}-more-characters"
114 print(f
"seen {row} after {len(seen)}")
118 cls
.generate_dynamic_test('test_allow',
119 name
, token
, sddl
, access_desired
)
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
)
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
:
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
)
150 sd
= security
.descriptor
.from_sddl(sddl
, self
.domain_sid
)
152 granted
= access_check(sd
, token
, access_desired
)
153 except NTSTATUSError
as e
:
156 if e
.args
[0] != NT_STATUS_ACCESS_DENIED
:
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
)
168 granted
= access_check(sd
, token
, access_desired
)
169 except NTSTATUSError
as e
:
170 if e
.args
[0] == NT_STATUS_ACCESS_DENIED
:
172 self
.fail(f
"failed with {e}, not access denied")
174 self
.fail("access allowed")
178 class AllowTests(ConditionalAceClaimsBase
):
183 {'sids': ['WD', 'AA'],
184 'device_claims': {"colour":["orange", "blue"]}},
186 '(@Device.colour == {"orange", "blue"}))'),
188 ( # device_claims, int >=
189 {'sids': ['WD', 'AA'],
190 'device_claims': {"legs": 4}},
191 ('D:(XA;;0x1f;;;AA;(@Device.legs >= 1))'),
193 ( # device_claims, int
194 {'sids': ['WD', 'AA'],
195 'device_claims': {"legs": 1}},
196 ('D:(XA;;0x1f;;;AA;(@Device.legs == 1))'),
198 ( # device_member_of && member_of
199 {'sids': ['WD', 'AA'],
200 'device_sids': ['BA', 'BG']},
202 "(Device_Member_of{SID(BA)} && Member_of{SID(WD)}))"),
204 ( # device_member_of || member_of, both true
205 {'sids': ['WD', 'AA'],
206 'device_sids': ['BA', 'BG']},
208 "(Device_Member_of{SID(AA)} || Member_of{SID(WD)}))"),
210 ( # device_member_of || member_of, second true
211 {'sids': ['WD', 'AA'],
212 'device_sids': ['BA', 'BG']},
214 "(Device_Member_of{SID(AA)} || Member_of{SID(WD)}))"),
216 ( # device_member_of || member_of, first true
217 {'sids': ['WD', 'AA'],
218 'device_sids': ['BA', 'BG']},
220 "(Device_Member_of{SID(BG)} || Member_of{SID(WR)}))"),
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)}))"),
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)}))",
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)}))",
233 ({'sids': ['WD', 'S-1-222-333']},
234 "O:S-1-1-0D:(XA;;0x1ff;;;WD;(Member_of{SID(S-1-1-0)}))",
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)))",
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)))",
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)))",
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)}))",
252 ({'sids': ['S-1-1-0', 'S-1-222-333']},
254 "(XA;;0x1ff;;;WD;(Member_of_Any{SID(S-1-1-0), SID(S-1-222-333)}))"),
256 ({'sids': ['S-1-1-0', 'S-1-222-333']},
258 "(XA;;0x1ff;;;WD;(Member_of_Any{SID(S-1-1-334), SID(S-1-222-333)}))"),
260 ({'sids': ['S-1-1-0', 'S-1-222-333']},
261 ("D:(XA;;0x1ff;;;WD;(Member_of_Any{SID(S-1-222-333)}))"),
263 ({'sids': ['S-1-77-88-99', 'AA']},
264 "D:(XA;;0x1f;;;AA;(Member_of{SID(S-1-77-88-99)}))",
267 {'sids': ['WD', 'AA'],
268 'device_sids': ['BA', 'BG']},
269 "D:(XA;;0x1f;;;AA;(Device_Member_of{SID(BA)}))",
272 {'sids': ['WD', 'AA'],
273 'device_sids': ['BA', 'BG']},
274 "D:(XA;;0x1f;;;AA;(Device_Member_of{SID(BA)}))",
276 ( # not (!) member_of
277 {'sids': ['WD', 'AA'],
278 'device_sids': ['BA', 'BG']},
279 "D:(XA;;0x1f;;;AA;(! (Member_of{SID(BA)})))",
281 ( # not not (!!) member_of
282 {'sids': ['WD', 'AA'],
283 'device_sids': ['BA', 'BG']},
284 "D:(XA;;0x1f;;;AA;(!(! (Member_of{SID(AA)}))))",
286 ( # not * 8 (!!!! !!!!) member_of
287 {'sids': ['WD', 'AA'],
288 'device_sids': ['BA', 'BG']},
289 "D:(XA;;0x1f;;;AA;(!(!(!(!(!(!(!(!( Member_of{SID(AA)}))))))))))",
291 ( # not * 9 (!!! !!! !!!) member_of
292 {'sids': ['WD', 'AA'],
293 'device_sids': ['BA', 'BG']},
294 "D:(XA;;0x1f;;;AA;(!(!(!( !(!(!( !(!(!(Member_of{SID(BA)})))))))))))",
296 ( # not * 9 (!!! !!! !!!) Not_Member_of
297 {'sids': ['WD', 'AA'],
298 'device_sids': ['BA', 'BG']},
300 "(!(!(!( !(!(!( !(!(!( Not_Member_of{SID(AA)})))))))))))"),
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"))'),
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"))'),
314 ( # device_claims, comparing single to single
315 {'sids': ['WD', 'AA'],
316 'device_claims': {"colour": "blue"}},
317 ('D:(XA;;0x1f;;;AA;(@Device.colour == "blue"))'),
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))'),
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"))'),
335 class DenyTests(ConditionalAceClaimsBase
):
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)}))"),
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)}))"),
350 ( # device_member_of && member_of, both false
351 {'sids': ['WD', 'AA'],
352 'device_sids': ['BA', 'BG']},
354 "(Device_Member_of{SID(AA)} && Member_of{SID(WR)}))"),
356 ( # device_member_of && member_of, first false
357 {'sids': ['WD', 'AA'],
358 'device_sids': ['BA', 'BG']},
360 "(Device_Member_of{SID(AA)} && Member_of{SID(WD)}))"),
362 ( # device_member_of && member_of, second false
363 {'sids': ['WD', 'AA'],
364 'device_sids': ['BA', 'BG']},
366 "(Device_Member_of{SID(BA)} && Member_of{SID(BA)}))"),
368 ( # device_member_of || member_of, both false
369 {'sids': ['WD', 'AA'],
370 'device_sids': ['BA', 'BG']},
372 "(Device_Member_of{SID(AA)} || Member_of{SID(WR)}))"),
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"))'),
379 ( # not (!) member_of
380 {'sids': ['WD', 'AA'],
381 'device_sids': ['BA', 'BG']},
382 "D:(XA;;0x1f;;;AA;(! (Member_of{SID(AA)})))",
384 ( # not not (!!) member_of
385 {'sids': ['WD', 'AA'],
386 'device_sids': ['BA', 'BG']},
387 "D:(XA;;0x1f;;;AA;(!(!( Member_of{SID(BA)}))))",
389 ( # not * 8 (!!!! !!!!) member_of
390 {'sids': ['WD', 'AA'],
391 'device_sids': ['BA', 'BG']},
392 "D:(XA;;0x1f;;;AA;(!(!( !(!( !(!( !(!(Member_of{SID(BA)}))))))))))",
394 ( # not * 3 (!!!) member_of
395 {'sids': ['WD', 'AA'],
396 'device_sids': ['BA', 'BG']},
397 "D:(XA;;0x1f;;;AA;(!(!(!(Member_of{SID(AA)})))))",
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)})))))",
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
414 claims
= [str(x
) for x
in range(n
)]
416 if random_seed
is None:
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
)
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
]
437 if random_seed
is None:
439 for i
in range(len(claims
)):
440 if random
.random() < 0.5:
441 claims
[i
] = claims
[i
].upper()
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
)
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
)
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
+ '"}'
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.
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']
506 cls
.generate_dynamic_test('test_allow',
507 name
, token
, sddl
, 0x10)
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
)
519 "90-disorderly-strings-claim-vs-claim-case-sensitive-with-dupes",
521 {'user_claims': {"c": claim_str_range(90,
523 "d": claim_str_range(90, 90,
526 ('D:(XA;;FA;;;WD;(@USER.c == @USER.d))')
529 # this one currently fails before we get to compare_composites()
532 {'user_claims': {"c": claim_str_range(0)}},
533 ('D:(XA;;FA;;;WD;(@USER.c == @USER.c))')
536 "50-orderly-strings",
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",
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",
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",
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",
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",
568 {'user_claims': {"c": claim_str_range(99)}},
569 (f
'D:(XA;;FA;;;WD;(@USER.c == {composite_str(99)}))')
572 "99-disorderly-strings",
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",
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",
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",
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",
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",
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",
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",
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",
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",
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",
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",
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",
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",
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)}))')
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",
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",
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",
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",
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)}))')
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",
697 {'user_claims': {"c": claim_str_range(9)}},
698 ('D:(XA;;FA;;;WD;(@USER.c == @USER.c))')
701 "300-orderly-strings-claim-vs-itself",
703 {'user_claims': {"c": claim_str_range(300)}},
704 ('D:(XA;;FA;;;WD;(@USER.c == @USER.c))')
707 "900-disorderly-strings-claim-vs-claim",
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",
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",
723 {'user_claims': {"c": claim_str_range(9,random_seed
=1),
724 "d": claim_str_range(9,
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",
732 {'user_claims': {"c": claim_str_range(9,
735 "d": claim_str_range(9,
737 case_sensitive
=True)}},
738 ('D:(XA;;FA;;;WD;(@USER.c == @USER.d))')
741 "9-disorderly-strings-claim-vs-claim-case-sensitive-ne",
743 {'user_claims': {"c": claim_str_range(9,random_seed
=1),
744 "d": claim_str_range(9,
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",
753 {'user_claims': {"c": claim_str_range(5,
756 "d": claim_str_range(5, 5,
759 case_sensitive
=True)}},
760 ('D:(XA;;FA;;;WD;(@USER.c == @USER.d))')
763 "90-disorderly-strings-claim-vs-int-claim",
765 {'user_claims': {"c": claim_str_range(90,
767 "d": claim_int_range(90,
769 ('D:(XA;;FA;;;WD;(@USER.c == @USER.d))')
772 "90-disorderly-ints-claim-vs-string-claim",
774 {'user_claims': {"c": claim_int_range(90,
776 "d": claim_str_range(90,
778 ('D:(XA;;FA;;;WD;(@USER.c == @USER.d))')
781 "9-disorderly-strings-vs-9+90-dupes",
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",
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",
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",
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",
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",
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",
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",
833 {'user_claims': {"c": claim_str_range(90,
835 ('D:(XA;;FA;;;WD;(@USER.c == @USER.d))')
838 "missing-claim-vs-90-disorderly-strings",
840 {'user_claims': {"c": claim_str_range(90,
842 ('D:(XA;;FA;;;WD;(@USER.z == @USER.c))')
846 "RA-9-disorderly-strings-vs-9-mixed-case",
848 {'user_claims': {"c": claim_str_range(9,
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",
861 {'user_claims': {"c": claim_str_range(9,
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",
874 {'user_claims': {"c": claim_str_range(29,
879 ('D:(XA;;FA;;;WD;(@RESOURCE.c == @User.c))'
880 f
'S:{ra_str_range(29, random_seed=2, mix_case=True)}'
886 {'user_claims': {"c": claim_str_range(0)}},
887 ('D:(XA;;FA;;;WD;(@USER.c != @USER.c))')
892 {'user_claims': {"c": claim_str_range(1)}},
893 ('D:(XA;;FA;;;WD;(@USER.c == @USER.c))')
898 {'user_claims': {"c": claim_str_range(1)}},
899 ('D:(XA;;FA;;;WD;(@USER.c != @USER.c))')