#!/usr/bin/env python3 # Unix SMB/CIFS implementation. # Copyright (C) Stefan Metzmacher 2020 # Copyright (C) Catalyst.Net Ltd 2022 # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # import sys import os sys.path.insert(0, 'bin/python') os.environ['PYTHONUNBUFFERED'] = '1' import re import ldb from samba.dcerpc import claims, krb5pac, security from samba.tests import DynamicTestCase, env_get_var_value from samba.tests.krb5 import kcrypto from samba.tests.krb5.kcrypto import Enctype from samba.tests.krb5.kdc_base_test import KDCBaseTest from samba.tests.krb5.raw_testcase import Krb5EncryptionKey from samba.tests.krb5.rfc4120_constants import ( AES256_CTS_HMAC_SHA1_96, ARCFOUR_HMAC_MD5, KRB_TGS_REP, NT_PRINCIPAL, NT_SRV_INST, ) import samba.tests.krb5.rfc4120_pyasn1 as krb5_asn1 global_asn1_print = False global_hexdump = False class UnorderedList(list): def __eq__(self, other): if isinstance(other, UnorderedList): return sorted(self) == sorted(other) else: return False @DynamicTestCase class ClaimsTests(KDCBaseTest): @classmethod def setUpClass(cls): super().setUpClass() cls._search_iterator = None def setUp(self): super().setUp() self.do_asn1_print = global_asn1_print self.do_hexdump = global_hexdump def get_sample_dn(self): if self._search_iterator is None: samdb = self.get_samdb() type(self)._search_iterator = samdb.search_iterator() return str(next(self._search_iterator).dn) def get_binary_dn(self): return 'B:8:01010101:' + self.get_sample_dn() def remove_client_claims(self, ticket): def modify_pac_fn(pac): pac_buffers = pac.buffers for pac_buffer in pac_buffers: if pac_buffer.type == krb5pac.PAC_TYPE_CLIENT_CLAIMS_INFO: pac.num_buffers -= 1 pac_buffers.remove(pac_buffer) break else: self.fail('expected client claims in PAC') pac.buffers = pac_buffers return pac return self.modified_ticket( ticket, modify_pac_fn=modify_pac_fn, checksum_keys=self.get_krbtgt_checksum_key()) def test_delegation_claims(self): self.run_delegation_test(remove_claims=False) def test_delegation_claims_remove_claims(self): self.run_delegation_test(remove_claims=True) def run_delegation_test(self, remove_claims): service_creds = self.get_service_creds() service_spn = service_creds.get_spn() user_name = self.get_new_username() mach_name = self.get_new_username() samdb = self.get_samdb() user_creds, user_dn = self.create_account( samdb, user_name, self.AccountType.USER, additional_details={ 'middleName': 'user_old', }) mach_creds, mach_dn = self.create_account( samdb, mach_name, self.AccountType.COMPUTER, spn=f'host/{mach_name}', additional_details={ 'middleName': 'mach_old', 'msDS-AllowedToDelegateTo': service_spn, }) claim_id = self.get_new_username() self.create_claim(claim_id, enabled=True, attribute='middleName', single_valued=True, source_type='AD', for_classes=['user', 'computer'], value_type=claims.CLAIM_TYPE_STRING) options = 'forwardable' expected_flags = krb5_asn1.TicketFlags(options) expected_claims_user = { claim_id: { 'source_type': claims.CLAIMS_SOURCE_TYPE_AD, 'type': claims.CLAIM_TYPE_STRING, 'values': ['user_old'], }, } expected_claims_mac = { claim_id: { 'source_type': claims.CLAIMS_SOURCE_TYPE_AD, 'type': claims.CLAIM_TYPE_STRING, 'values': ['mach_old'], }, } user_tgt = self.get_tgt(user_creds, kdc_options=options, expect_pac=True, expected_flags=expected_flags, expect_client_claims=True, expected_client_claims=expected_claims_user) user_ticket = self.get_service_ticket( user_tgt, mach_creds, kdc_options=options, expect_pac=True, expected_flags=expected_flags, expect_client_claims=True, expected_client_claims=expected_claims_user) mach_tgt = self.get_tgt(mach_creds, expect_pac=True, expect_client_claims=True, expected_client_claims=expected_claims_mac) if remove_claims: user_ticket = self.remove_client_claims(user_ticket) mach_tgt = self.remove_client_claims(mach_tgt) # Change the value of the attributes used for the claim. msg = ldb.Message(ldb.Dn(samdb, user_dn)) msg['middleName'] = ldb.MessageElement('user_new', ldb.FLAG_MOD_REPLACE, 'middleName') samdb.modify(msg) # Change the value of the attributes used for the claim. msg = ldb.Message(ldb.Dn(samdb, mach_dn)) msg['middleName'] = ldb.MessageElement('mach_new', ldb.FLAG_MOD_REPLACE, 'middleName') samdb.modify(msg) additional_tickets = [user_ticket.ticket] options = str(krb5_asn1.KDCOptions('cname-in-addl-tkt')) user_realm = user_creds.get_realm() user_cname = self.PrincipalName_create(name_type=NT_PRINCIPAL, names=[user_name]) user_sid = self.get_objectSid(samdb, user_dn) mach_realm = mach_creds.get_realm() service_name = service_creds.get_username()[:-1] service_realm = service_creds.get_realm() service_sname = self.PrincipalName_create(name_type=NT_PRINCIPAL, names=['host', service_name]) service_decryption_key = self.TicketDecryptionKey_from_creds( service_creds) service_etypes = service_creds.tgs_supported_enctypes expected_proxy_target = service_creds.get_spn() expected_transited_services = [f'host/{mach_name}@{mach_realm}'] authenticator_subkey = self.RandomKey(Enctype.AES256) etypes = (AES256_CTS_HMAC_SHA1_96, ARCFOUR_HMAC_MD5) expected_claims = expected_claims_user if not remove_claims else None kdc_exchange_dict = self.tgs_exchange_dict( expected_crealm=user_realm, expected_cname=user_cname, expected_srealm=service_realm, expected_sname=service_sname, expected_account_name=user_name, expected_sid=user_sid, expected_supported_etypes=service_etypes, ticket_decryption_key=service_decryption_key, check_rep_fn=self.generic_check_kdc_rep, check_kdc_private_fn=self.generic_check_kdc_private, tgt=mach_tgt, authenticator_subkey=authenticator_subkey, kdc_options=options, expected_proxy_target=expected_proxy_target, expected_transited_services=expected_transited_services, expect_client_claims=not remove_claims, expected_client_claims=expected_claims, expect_pac=True) self._generic_kdc_exchange(kdc_exchange_dict, cname=None, realm=service_realm, sname=service_sname, etypes=etypes, additional_tickets=additional_tickets) def test_tgs_claims(self): self.run_tgs_test(remove_claims=False, to_krbtgt=False) def test_tgs_claims_remove_claims(self): self.run_tgs_test(remove_claims=True, to_krbtgt=False) def test_tgs_claims_to_krbtgt(self): self.run_tgs_test(remove_claims=False, to_krbtgt=True) def test_tgs_claims_remove_claims_to_krbtgt(self): self.run_tgs_test(remove_claims=True, to_krbtgt=True) def run_tgs_test(self, remove_claims, to_krbtgt): samdb = self.get_samdb() user_creds, user_dn = self.create_account(samdb, self.get_new_username(), additional_details={ 'middleName': 'foo', }) claim_id = self.get_new_username() self.create_claim(claim_id, enabled=True, attribute='middleName', single_valued=True, source_type='AD', for_classes=['user'], value_type=claims.CLAIM_TYPE_STRING) expected_claims = { claim_id: { 'source_type': claims.CLAIMS_SOURCE_TYPE_AD, 'type': claims.CLAIM_TYPE_STRING, 'values': ['foo'], }, } # Get a TGT for the user. tgt = self.get_tgt(user_creds, expect_pac=True, expect_client_claims=True, expected_client_claims=expected_claims) if remove_claims: tgt = self.remove_client_claims(tgt) # Change the value of the attribute used for the claim. msg = ldb.Message(ldb.Dn(samdb, user_dn)) msg['middleName'] = ldb.MessageElement('bar', ldb.FLAG_MOD_REPLACE, 'middleName') samdb.modify(msg) if to_krbtgt: target_creds = self.get_krbtgt_creds() srealm = target_creds.get_realm() sname = self.PrincipalName_create( name_type=NT_SRV_INST, names=[target_creds.get_username(), srealm]) else: target_creds = self.get_service_creds() sname = None # Get a service ticket for the user. The value should not have changed. self.get_service_ticket( tgt, target_creds, sname=sname, expect_pac=True, expect_client_claims=not remove_claims, expected_client_claims=(expected_claims if not remove_claims else None)) def test_device_info(self): self._run_device_info_test(to_krbtgt=False) def test_device_info_to_krbtgt(self): self._run_device_info_test(to_krbtgt=True) def _run_device_info_test(self, to_krbtgt): user_creds = self.get_cached_creds( account_type=self.AccountType.USER) user_tgt = self.get_tgt(user_creds) mach_creds = self.get_cached_creds( account_type=self.AccountType.COMPUTER) mach_tgt = self.get_tgt(mach_creds) samdb = self.get_samdb() expected_sid = self.get_objectSid(samdb, user_creds.get_dn()) subkey = self.RandomKey(user_tgt.session_key.etype) armor_subkey = self.RandomKey(subkey.etype) explicit_armor_key = self.generate_armor_key(armor_subkey, mach_tgt.session_key) armor_key = kcrypto.cf2(explicit_armor_key.key, subkey.key, b'explicitarmor', b'tgsarmor') armor_key = Krb5EncryptionKey(armor_key, None) if to_krbtgt: target_creds = self.get_krbtgt_creds() srealm = target_creds.get_realm() sname = self.PrincipalName_create( name_type=NT_SRV_INST, names=[target_creds.get_username(), srealm]) else: target_enctypes = security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED target_creds = self.get_cached_creds( account_type=self.AccountType.COMPUTER, opts={ 'supported_enctypes': target_enctypes, }) srealm = target_creds.get_realm() sname = self.PrincipalName_create( name_type=NT_PRINCIPAL, names=['host', target_creds.get_username()[:-1]]) decryption_key = self.TicketDecryptionKey_from_creds( target_creds) etypes = (AES256_CTS_HMAC_SHA1_96, ARCFOUR_HMAC_MD5) kdc_options = '0' pac_options = '1' # claims support kdc_exchange_dict = self.tgs_exchange_dict( expected_crealm=user_tgt.crealm, expected_cname=user_tgt.cname, expected_srealm=srealm, expected_sname=sname, ticket_decryption_key=decryption_key, generate_fast_fn=self.generate_simple_fast, generate_fast_armor_fn=self.generate_ap_req, check_rep_fn=self.generic_check_kdc_rep, check_kdc_private_fn=self.generic_check_kdc_private, tgt=user_tgt, armor_key=armor_key, armor_tgt=mach_tgt, armor_subkey=armor_subkey, pac_options=pac_options, authenticator_subkey=subkey, kdc_options=kdc_options, expect_pac=True, expect_pac_attrs=to_krbtgt, expect_pac_attrs_pac_request=to_krbtgt, expected_sid=expected_sid, expect_device_claims=not to_krbtgt, expect_device_info=not to_krbtgt) rep = self._generic_kdc_exchange(kdc_exchange_dict, cname=None, realm=srealm, sname=sname, etypes=etypes) self.check_reply(rep, KRB_TGS_REP) def test_device_claims(self): self._run_device_claims_test(to_krbtgt=False) def test_device_claims_to_krbtgt(self): self._run_device_claims_test(to_krbtgt=True) def _run_device_claims_test(self, to_krbtgt): user_creds = self.get_cached_creds( account_type=self.AccountType.USER) user_tgt = self.get_tgt(user_creds) samdb = self.get_samdb() mach_creds, mach_dn = self.create_account( samdb, self.get_new_username(), account_type=self.AccountType.COMPUTER, additional_details={ 'middleName': 'foo', }) claim_id = self.get_new_username() self.create_claim(claim_id, enabled=True, attribute='middleName', single_valued=True, source_type='AD', for_classes=['computer'], value_type=claims.CLAIM_TYPE_STRING) expected_claims = { claim_id: { 'source_type': claims.CLAIMS_SOURCE_TYPE_AD, 'type': claims.CLAIM_TYPE_STRING, 'values': ['foo'], }, } # Get a TGT for the computer. mach_tgt = self.get_tgt(mach_creds, expect_pac=True, expect_client_claims=True, expected_client_claims=expected_claims) # Change the value of the attribute used for the claim. msg = ldb.Message(ldb.Dn(samdb, mach_dn)) msg['middleName'] = ldb.MessageElement('bar', ldb.FLAG_MOD_REPLACE, 'middleName') samdb.modify(msg) # Get a service ticket for the user, using the computer's TGT as an # armor TGT. The value should not have changed. expected_sid = self.get_objectSid(samdb, user_creds.get_dn()) subkey = self.RandomKey(user_tgt.session_key.etype) armor_subkey = self.RandomKey(subkey.etype) explicit_armor_key = self.generate_armor_key(armor_subkey, mach_tgt.session_key) armor_key = kcrypto.cf2(explicit_armor_key.key, subkey.key, b'explicitarmor', b'tgsarmor') armor_key = Krb5EncryptionKey(armor_key, None) if to_krbtgt: target_creds = self.get_krbtgt_creds() srealm = target_creds.get_realm() sname = self.PrincipalName_create( name_type=NT_SRV_INST, names=[target_creds.get_username(), srealm]) else: target_enctypes = security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED target_creds = self.get_cached_creds( account_type=self.AccountType.COMPUTER, opts={ 'supported_enctypes': target_enctypes, }) srealm = target_creds.get_realm() sname = self.PrincipalName_create( name_type=NT_PRINCIPAL, names=['host', target_creds.get_username()[:-1]]) decryption_key = self.TicketDecryptionKey_from_creds( target_creds) etypes = (AES256_CTS_HMAC_SHA1_96, ARCFOUR_HMAC_MD5) kdc_options = '0' pac_options = '1' # claims support kdc_exchange_dict = self.tgs_exchange_dict( expected_crealm=user_tgt.crealm, expected_cname=user_tgt.cname, expected_srealm=srealm, expected_sname=sname, ticket_decryption_key=decryption_key, generate_fast_fn=self.generate_simple_fast, generate_fast_armor_fn=self.generate_ap_req, check_rep_fn=self.generic_check_kdc_rep, check_kdc_private_fn=self.generic_check_kdc_private, tgt=user_tgt, armor_key=armor_key, armor_tgt=mach_tgt, armor_subkey=armor_subkey, pac_options=pac_options, authenticator_subkey=subkey, kdc_options=kdc_options, expect_pac=True, expect_pac_attrs=to_krbtgt, expect_pac_attrs_pac_request=to_krbtgt, expected_sid=expected_sid, expect_device_info=not to_krbtgt, expect_device_claims=not to_krbtgt, expected_device_claims=expected_claims if not to_krbtgt else None) rep = self._generic_kdc_exchange(kdc_exchange_dict, cname=None, realm=srealm, sname=sname, etypes=etypes) self.check_reply(rep, KRB_TGS_REP) @classmethod def setUpDynamicTestCases(cls): FILTER = env_get_var_value('FILTER', allow_missing=True) for case in cls.cases: name = case.pop('name') if FILTER and not re.search(FILTER, name): continue name = re.sub(r'\W+', '_', name) # Run tests making requests both to the krbtgt and to our own # account. cls.generate_dynamic_test('test_claims', name, dict(case), False) cls.generate_dynamic_test('test_claims', name + '_to_self', dict(case), True) def _test_claims_with_args(self, case, to_self): account_class = case.pop('class') if account_class == 'user': account_type = self.AccountType.USER elif account_class == 'computer': account_type = self.AccountType.COMPUTER else: self.fail(f'Unknown class "{account_class}"') expected_claims = {} unexpected_claims = set() details = {} all_claims = case.pop('claims') for claim in all_claims: # Make a copy to avoid modifying the original. claim = dict(claim) claim_id = self.get_new_username() expected = claim.pop('expected', False) expected_values = claim.pop('expected_values', None) if not expected: self.assertIsNone(expected_values, 'claim not expected, ' 'but expected values provided') values = claim.pop('values', None) if values is not None: def get_placeholder(val): if val is self.sample_dn: return self.get_sample_dn() elif val is self.binary_dn: return self.get_binary_dn() else: return val def ldb_transform(val): if val is True: return 'TRUE' elif val is False: return 'FALSE' elif isinstance(val, int): return str(val) else: return val values_type = type(values) values = values_type(map(get_placeholder, values)) transformed_values = values_type(map(ldb_transform, values)) attribute = claim['attribute'] if attribute in details: self.assertEqual(details[attribute], transformed_values, 'conflicting values set for attribute') details[attribute] = transformed_values if expected_values is None: expected_values = values if expected: self.assertIsNotNone(expected_values, 'expected claim, but no value(s) set') value_type = claim['value_type'] expected_claims[claim_id] = { 'source_type': claims.CLAIMS_SOURCE_TYPE_AD, 'type': value_type, 'values': expected_values, } else: unexpected_claims.add(claim_id) self.create_claim(claim_id, **claim) details = ((k, v) for k, v in details.items()) creds = self.get_cached_creds(account_type=account_type, opts={ 'additional_details': details, }) self.assertFalse(case, 'unexpected parameters in testcase') if to_self: service_creds = self.get_service_creds() sname = self.PrincipalName_create( name_type=NT_PRINCIPAL, names=[service_creds.get_username()]) ticket_etype = Enctype.RC4 else: service_creds = None sname = None ticket_etype = None self.get_tgt(creds, sname=sname, target_creds=service_creds, ticket_etype=ticket_etype, expect_pac=True, expect_client_claims=True, expected_client_claims=expected_claims or None, unexpected_client_claims=unexpected_claims or None) sample_dn = object() binary_dn = object() security_descriptor = (b'\x01\x00\x04\x80\x14\x00\x00\x00\x00\x00\x00\x00' b'\x00\x00\x00\x00$\x00\x00\x00\x01\x02\x00\x00\x00' b'\x00\x00\x05 \x00\x00\x00 \x02\x00\x00\x04\x00' b'\x1c\x00\x01\x00\x00\x00\x00\x00\x14\x00\xff\x01' b'\x0f\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00' b'\x00\x00') cases = [ { 'name': 'no claims', 'claims': [], 'class': 'user', }, { 'name': 'simple AD-sourced claim', 'claims': [ { # 2.5.5.12 'enabled': True, 'attribute': 'carLicense', 'single_valued': True, 'source_type': 'AD', 'for_classes': ['user'], 'value_type': claims.CLAIM_TYPE_STRING, 'values': ['foo'], 'expected': True, }, ], 'class': 'user', }, { # Note: The order of these DNs may differ on Windows. 'name': 'dn string syntax', 'claims': [ { # 2.5.5.1 'enabled': True, 'attribute': 'msDS-AuthenticatedAtDC', 'single_valued': True, 'source_type': 'AD', 'for_classes': ['user'], 'value_type': claims.CLAIM_TYPE_STRING, 'values': UnorderedList([sample_dn, sample_dn, sample_dn]), 'expected': True, }, ], 'class': 'user', }, { 'name': 'dn string syntax, wrong value type', 'claims': [ { # 2.5.5.1 'enabled': True, 'attribute': 'msDS-AuthenticatedAtDC', 'single_valued': True, 'source_type': 'AD', 'for_classes': ['user'], 'value_type': claims.CLAIM_TYPE_BOOLEAN, 'values': UnorderedList([sample_dn, sample_dn, sample_dn]), }, ], 'class': 'user', }, { 'name': 'oid syntax', 'claims': [ { # 2.5.5.2 'enabled': True, 'attribute': 'objectClass', 'single_valued': True, 'source_type': 'AD', 'for_classes': ['user'], 'value_type': claims.CLAIM_TYPE_UINT64, 'expected_values': [655369, 65543, 65542, 65536], 'expected': True, }, ], 'class': 'user', }, { 'name': 'oid syntax 2', 'claims': [ { # 2.5.5.2 'enabled': True, 'attribute': 'objectClass', 'single_valued': True, 'source_type': 'AD', 'for_classes': ['computer'], 'value_type': claims.CLAIM_TYPE_UINT64, 'expected_values': [196638, 655369, 65543, 65542, 65536], 'expected': True, }, ], 'class': 'computer', }, { 'name': 'oid syntax, wrong value type', 'claims': [ { # 2.5.5.2 'enabled': True, 'attribute': 'objectClass', 'single_valued': True, 'source_type': 'AD', 'for_classes': ['user'], 'value_type': claims.CLAIM_TYPE_INT64, }, ], 'class': 'user', }, { 'name': 'boolean syntax, true', 'claims': [ { # 2.5.5.8 'enabled': True, 'attribute': 'msTSAllowLogon', 'single_valued': True, 'source_type': 'AD', 'for_classes': ['user'], 'value_type': claims.CLAIM_TYPE_BOOLEAN, 'values': [True], 'expected': True, }, ], 'class': 'user', }, { 'name': 'boolean syntax, false', 'claims': [ { # 2.5.5.8 'enabled': True, 'attribute': 'msTSAllowLogon', 'single_valued': True, 'source_type': 'AD', 'for_classes': ['user'], 'value_type': claims.CLAIM_TYPE_BOOLEAN, 'values': [False], 'expected': True, }, ], 'class': 'user', }, { 'name': 'boolean syntax, wrong value type', 'claims': [ { # 2.5.5.8 'enabled': True, 'attribute': 'msTSAllowLogon', 'single_valued': True, 'source_type': 'AD', 'for_classes': ['user'], 'value_type': claims.CLAIM_TYPE_STRING, 'values': [True], }, ], 'class': 'user', }, { 'name': 'integer syntax', 'claims': [ { # 2.5.5.9 'enabled': True, 'attribute': 'localeID', 'single_valued': True, 'source_type': 'AD', 'for_classes': ['user'], 'value_type': claims.CLAIM_TYPE_INT64, 'values': [3, 42, -999, 1000, 20000], 'expected_values': [3 << 32, 42 << 32, -999 << 32, 1000 << 32 | 0xffffffff, 20000 << 32], 'expected': True, }, ], 'class': 'user', }, { 'name': 'integer syntax, wrong value type', 'claims': [ { # 2.5.5.9 'enabled': True, 'attribute': 'localeID', 'single_valued': True, 'source_type': 'AD', 'for_classes': ['user'], 'value_type': claims.CLAIM_TYPE_UINT64, 'values': [3, 42, -999, 1000], }, ], 'class': 'user', }, { 'name': 'security descriptor syntax', 'claims': [ { # 2.5.5.15 'enabled': True, 'attribute': 'msDS-AllowedToActOnBehalfOfOtherIdentity', 'single_valued': True, 'source_type': 'AD', 'for_classes': ['computer'], 'value_type': claims.CLAIM_TYPE_STRING, 'values': [security_descriptor], 'expected_values': ['O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;' ';;S-1-0-0)'], 'expected': True, }, ], 'class': 'computer', }, { 'name': 'security descriptor syntax, wrong value type', 'claims': [ { # 2.5.5.15 'enabled': True, 'attribute': 'msDS-AllowedToActOnBehalfOfOtherIdentity', 'single_valued': True, 'source_type': 'AD', 'for_classes': ['computer'], 'value_type': claims.CLAIM_TYPE_UINT64, 'values': [security_descriptor], }, ], 'class': 'computer', }, { 'name': 'case insensitive string syntax (invalid)', 'claims': [ { # 2.5.5.4 'enabled': True, 'attribute': 'networkAddress', 'single_valued': True, 'source_type': 'AD', 'for_classes': ['user'], 'value_type': claims.CLAIM_TYPE_STRING, 'values': ['foo', 'bar'], }, ], 'class': 'user', }, { 'name': 'printable string syntax (invalid)', 'claims': [ { # 2.5.5.5 'enabled': True, 'attribute': 'displayNamePrintable', 'single_valued': True, 'source_type': 'AD', 'for_classes': ['user'], 'value_type': claims.CLAIM_TYPE_STRING, 'values': ['foo'], }, ], 'class': 'user', }, { 'name': 'numeric string syntax (invalid)', 'claims': [ { # 2.5.5.6 'enabled': True, 'attribute': 'internationalISDNNumber', 'single_valued': True, 'source_type': 'AD', 'for_classes': ['user'], 'value_type': claims.CLAIM_TYPE_STRING, 'values': ['foo', 'bar'], }, ], 'class': 'user', }, { 'name': 'dn binary syntax (invalid)', 'claims': [ { # 2.5.5.7 'enabled': True, 'attribute': 'msDS-RevealedUsers', 'single_valued': True, 'source_type': 'AD', 'for_classes': ['user'], 'value_type': claims.CLAIM_TYPE_STRING, 'values': [binary_dn, binary_dn, binary_dn], }, ], 'class': 'computer', }, { 'name': 'octet string syntax (invalid)', 'claims': [ { # 2.5.5.10 'enabled': True, 'attribute': 'jpegPhoto', 'single_valued': True, 'source_type': 'AD', 'for_classes': ['user'], 'value_type': claims.CLAIM_TYPE_STRING, 'values': ['foo', 'bar'], }, ], 'class': 'user', }, { 'name': 'utc time syntax (invalid)', 'claims': [ { # 2.5.5.11 'enabled': True, 'attribute': 'msTSExpireDate2', 'single_valued': True, 'source_type': 'AD', 'for_classes': ['user'], 'value_type': claims.CLAIM_TYPE_STRING, 'values': ['19700101000000.0Z'], }, ], 'class': 'user', }, { 'name': 'access point syntax (invalid)', 'claims': [ { # 2.5.5.17 'enabled': True, 'attribute': 'mS-DS-CreatorSID', 'single_valued': True, 'source_type': 'AD', 'for_classes': ['user'], 'value_type': claims.CLAIM_TYPE_STRING, }, ], 'class': 'user', }, { 'name': 'no value set', 'claims': [ { # 2.5.5.12 'enabled': True, 'attribute': 'carLicense', 'single_valued': True, 'source_type': 'AD', 'for_classes': ['user'], 'value_type': claims.CLAIM_TYPE_STRING, }, ], 'class': 'user', }, { 'name': 'multi-valued claim', 'claims': [ { # 2.5.5.12 'enabled': True, 'attribute': 'carLicense', 'single_valued': True, 'source_type': 'AD', 'for_classes': ['user'], 'value_type': claims.CLAIM_TYPE_STRING, 'values': ['foo', 'bar', 'baz'], 'expected': True, }, ], 'class': 'user', }, { 'name': 'missing attribute', 'claims': [ { 'enabled': True, 'single_valued': True, 'source_type': 'AD', 'for_classes': ['user'], 'value_type': claims.CLAIM_TYPE_STRING, }, ], 'class': 'user', }, { 'name': 'invalid attribute', 'claims': [ { # 2.5.5.10 'enabled': True, 'attribute': 'unicodePwd', 'single_valued': True, 'source_type': 'AD', 'for_classes': ['user'], 'value_type': claims.CLAIM_TYPE_STRING, }, ], 'class': 'user', }, { 'name': 'incorrect value type', 'claims': [ { # 2.5.5.12 'enabled': True, 'attribute': 'carLicense', 'single_valued': True, 'source_type': 'AD', 'for_classes': ['user'], 'value_type': claims.CLAIM_TYPE_INT64, 'values': ['foo'], }, ], 'class': 'user', }, { 'name': 'invalid value type', 'claims': [ { # 2.5.5.12 'enabled': True, 'attribute': 'carLicense', 'single_valued': True, 'source_type': 'AD', 'for_classes': ['user'], 'value_type': 0, 'values': ['foo'], }, ], 'class': 'user', }, { 'name': 'missing value type', 'claims': [ { # 2.5.5.12 'enabled': True, 'attribute': 'carLicense', 'single_valued': True, 'source_type': 'AD', 'for_classes': ['user'], 'values': ['foo'], }, ], 'class': 'user', }, { 'name': 'duplicate claim', 'claims': [ { # 2.5.5.12 'enabled': True, 'attribute': 'carLicense', 'single_valued': True, 'source_type': 'AD', 'for_classes': ['user'], 'value_type': claims.CLAIM_TYPE_STRING, 'values': ['foo'], 'expected': True, }, { # 2.5.5.12 'enabled': True, 'attribute': 'carLicense', 'single_valued': True, 'source_type': 'AD', 'for_classes': ['user'], 'value_type': claims.CLAIM_TYPE_STRING, 'values': ['foo'], 'expected': True, }, ], 'class': 'user', }, { 'name': 'multiple claims', 'claims': [ { # 2.5.5.12 'enabled': True, 'attribute': 'carLicense', 'single_valued': True, 'source_type': 'AD', 'for_classes': ['user'], 'value_type': claims.CLAIM_TYPE_STRING, 'values': ['foo', 'bar', 'baz'], 'expected': True, }, { # 2.5.5.8 'enabled': True, 'attribute': 'msTSAllowLogon', 'single_valued': True, 'source_type': 'AD', 'for_classes': ['user'], 'value_type': claims.CLAIM_TYPE_BOOLEAN, 'values': [True], 'expected': True, }, ], 'class': 'user', }, { 'name': 'case difference for source type', 'claims': [ { # 2.5.5.12 'enabled': True, 'attribute': 'carLicense', 'single_valued': True, 'source_type': 'ad', 'for_classes': ['user'], 'value_type': claims.CLAIM_TYPE_STRING, 'values': ['foo'], 'expected': True, }, ], 'class': 'user', }, { 'name': 'unhandled source type', 'claims': [ { # 2.5.5.12 'enabled': True, 'attribute': 'carLicense', 'single_valued': True, 'source_type': '', 'for_classes': ['user'], 'value_type': claims.CLAIM_TYPE_STRING, 'values': ['foo'], }, ], 'class': 'user', }, { 'name': 'disabled claim', 'claims': [ { # 2.5.5.12 'enabled': False, 'attribute': 'carLicense', 'single_valued': True, 'source_type': 'AD', 'for_classes': ['user'], 'value_type': claims.CLAIM_TYPE_STRING, 'values': ['foo'], }, ], 'class': 'user', }, { 'name': 'not enabled claim', 'claims': [ { # 2.5.5.12 'attribute': 'carLicense', 'single_valued': True, 'source_type': 'AD', 'for_classes': ['user'], 'value_type': claims.CLAIM_TYPE_STRING, 'values': ['foo'], }, ], 'class': 'user', }, { 'name': 'not applicable to any class', 'claims': [ { # 2.5.5.12 'enabled': True, 'attribute': 'carLicense', 'single_valued': True, 'source_type': 'AD', 'value_type': claims.CLAIM_TYPE_STRING, 'values': ['foo'], }, ], 'class': 'user', }, { 'name': 'not applicable to class', 'claims': [ { # 2.5.5.12 'enabled': True, 'attribute': 'carLicense', 'single_valued': True, 'source_type': 'AD', 'for_classes': ['user'], 'value_type': claims.CLAIM_TYPE_STRING, 'values': ['foo'], }, ], 'class': 'computer', }, { 'name': 'applicable to class', 'claims': [ { # 2.5.5.12 'enabled': True, 'attribute': 'carLicense', 'single_valued': True, 'source_type': 'AD', 'for_classes': ['user', 'computer'], 'value_type': claims.CLAIM_TYPE_STRING, 'values': ['foo'], 'expected': True, }, ], 'class': 'computer', }, { 'name': 'applicable to base class', 'claims': [ { # 2.5.5.12 'enabled': True, 'attribute': 'carLicense', 'single_valued': True, 'source_type': 'AD', 'for_classes': ['top'], 'value_type': claims.CLAIM_TYPE_STRING, 'values': ['foo'], }, ], 'class': 'user', }, { 'name': 'applicable to base class 2', 'claims': [ { # 2.5.5.12 'enabled': True, 'attribute': 'carLicense', 'single_valued': True, 'source_type': 'AD', 'for_classes': ['organizationalPerson'], 'value_type': claims.CLAIM_TYPE_STRING, 'values': ['foo'], }, ], 'class': 'user', }, ] if __name__ == '__main__': global_asn1_print = False global_hexdump = False import unittest unittest.main()