diff options
-rw-r--r-- | API.txt | 76 | ||||
-rw-r--r-- | ipaserver/plugins/baseuser.py | 57 | ||||
-rw-r--r-- | ipaserver/plugins/caacl.py | 17 | ||||
-rw-r--r-- | ipaserver/plugins/cert.py | 88 | ||||
-rw-r--r-- | ipaserver/plugins/host.py | 25 | ||||
-rw-r--r-- | ipaserver/plugins/passwd.py | 21 | ||||
-rw-r--r-- | ipaserver/plugins/service.py | 101 | ||||
-rw-r--r-- | ipaserver/plugins/vault.py | 46 | ||||
-rw-r--r-- | ipatests/test_xmlrpc/test_host_plugin.py | 1 | ||||
-rw-r--r-- | ipatests/test_xmlrpc/test_service_plugin.py | 9 | ||||
-rw-r--r-- | ipatests/test_xmlrpc/test_stageuser_plugin.py | 6 | ||||
-rw-r--r-- | ipatests/test_xmlrpc/test_user_plugin.py | 5 |
12 files changed, 213 insertions, 239 deletions
@@ -738,14 +738,14 @@ option: Int('max_serial_number?', autofill=False) option: Int('min_serial_number?', autofill=False) option: Str('no_host*', cli_name='no_hosts') option: Flag('no_members', autofill=True, default=True) -option: Str('no_service*', cli_name='no_services') +option: Principal('no_service*', cli_name='no_services') option: Str('no_user*', cli_name='no_users') option: Flag('pkey_only?', autofill=True, default=False) option: Flag('raw', autofill=True, cli_name='raw', default=False) option: Int('revocation_reason?', autofill=False) option: DateTime('revokedon_from?', autofill=False) option: DateTime('revokedon_to?', autofill=False) -option: Str('service*', cli_name='services') +option: Principal('service*', cli_name='services') option: Int('sizelimit?') option: Str('subject?', autofill=False) option: Int('timelimit?') @@ -771,7 +771,7 @@ arg: Str('csr', cli_name='csr_file') option: Flag('add', autofill=True, default=False) option: Flag('all', autofill=True, cli_name='all', default=False) option: Str('cacn?', autofill=True, cli_name='ca', default=u'ipa') -option: Str('principal') +option: Principal('principal') option: Str('profile_id?') option: Flag('raw', autofill=True, cli_name='raw', default=False) option: Str('request_type', autofill=True, default=u'pkcs10') @@ -2436,7 +2436,7 @@ option: Bool('ipakrbokasdelegate?', autofill=False, cli_name='ok_as_delegate') option: Bool('ipakrbrequirespreauth?', autofill=False, cli_name='requires_pre_auth') option: Str('ipasshpubkey*', autofill=False, cli_name='sshpubkey') option: Str('krbprincipalauthind*', autofill=False, cli_name='auth_ind') -option: Str('krbprincipalname?', cli_name='principalname') +option: Principal('krbprincipalname?', cli_name='principalname') option: Str('l?', autofill=False, cli_name='locality') option: Str('macaddress*', autofill=False) option: Flag('no_members', autofill=True, default=False) @@ -3401,7 +3401,7 @@ output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>]) output: PrimaryKey('value') command: passwd/1 args: 3,2,3 -arg: Str('principal', autofill=True, cli_name='user') +arg: Principal('principal', autofill=True, cli_name='user') arg: Password('password') arg: Password('current_password', autofill=True, confirm=False) option: Password('otp?', confirm=False) @@ -4271,7 +4271,7 @@ output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>]) output: PrimaryKey('value') command: service_add/1 args: 1,12,3 -arg: Str('krbprincipalname', cli_name='principal') +arg: Principal('krbprincipalname', cli_name='principal') option: Str('addattr*', cli_name='addattr') option: Flag('all', autofill=True, cli_name='all', default=False) option: Flag('force', autofill=True, default=False) @@ -4289,7 +4289,7 @@ output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>]) output: PrimaryKey('value') command: service_add_cert/1 args: 1,5,3 -arg: Str('krbprincipalname', cli_name='principal') +arg: Principal('krbprincipalname', cli_name='principal') option: Flag('all', autofill=True, cli_name='all', default=False) option: Flag('no_members', autofill=True, default=False) option: Flag('raw', autofill=True, cli_name='raw', default=False) @@ -4300,7 +4300,7 @@ output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>]) output: PrimaryKey('value') command: service_add_host/1 args: 1,5,3 -arg: Str('krbprincipalname', cli_name='principal') +arg: Principal('krbprincipalname', cli_name='principal') option: Flag('all', autofill=True, cli_name='all', default=False) option: Str('host*', alwaysask=True, cli_name='hosts') option: Flag('no_members', autofill=True, default=False) @@ -4311,7 +4311,7 @@ output: Output('failed', type=[<type 'dict'>]) output: Entry('result') command: service_allow_create_keytab/1 args: 1,8,3 -arg: Str('krbprincipalname', cli_name='principal') +arg: Principal('krbprincipalname', cli_name='principal') option: Flag('all', autofill=True, cli_name='all', default=False) option: Str('group*', alwaysask=True, cli_name='groups') option: Str('host*', alwaysask=True, cli_name='hosts') @@ -4325,7 +4325,7 @@ output: Output('failed', type=[<type 'dict'>]) output: Entry('result') command: service_allow_retrieve_keytab/1 args: 1,8,3 -arg: Str('krbprincipalname', cli_name='principal') +arg: Principal('krbprincipalname', cli_name='principal') option: Flag('all', autofill=True, cli_name='all', default=False) option: Str('group*', alwaysask=True, cli_name='groups') option: Str('host*', alwaysask=True, cli_name='hosts') @@ -4339,7 +4339,7 @@ output: Output('failed', type=[<type 'dict'>]) output: Entry('result') command: service_del/1 args: 1,2,3 -arg: Str('krbprincipalname+', cli_name='principal') +arg: Principal('krbprincipalname+', cli_name='principal') option: Flag('continue', autofill=True, cli_name='continue', default=False) option: Str('version?') output: Output('result', type=[<type 'dict'>]) @@ -4347,14 +4347,14 @@ output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>]) output: ListOfPrimaryKeys('value') command: service_disable/1 args: 1,1,3 -arg: Str('krbprincipalname', cli_name='principal') +arg: Principal('krbprincipalname', cli_name='principal') option: Str('version?') output: Output('result', type=[<type 'bool'>]) output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>]) output: PrimaryKey('value') command: service_disallow_create_keytab/1 args: 1,8,3 -arg: Str('krbprincipalname', cli_name='principal') +arg: Principal('krbprincipalname', cli_name='principal') option: Flag('all', autofill=True, cli_name='all', default=False) option: Str('group*', alwaysask=True, cli_name='groups') option: Str('host*', alwaysask=True, cli_name='hosts') @@ -4368,7 +4368,7 @@ output: Output('failed', type=[<type 'dict'>]) output: Entry('result') command: service_disallow_retrieve_keytab/1 args: 1,8,3 -arg: Str('krbprincipalname', cli_name='principal') +arg: Principal('krbprincipalname', cli_name='principal') option: Flag('all', autofill=True, cli_name='all', default=False) option: Str('group*', alwaysask=True, cli_name='groups') option: Str('host*', alwaysask=True, cli_name='hosts') @@ -4386,7 +4386,7 @@ arg: Str('criteria?') option: Flag('all', autofill=True, cli_name='all', default=False) option: StrEnum('ipakrbauthzdata*', autofill=False, cli_name='pac_type', values=[u'MS-PAC', u'PAD', u'NONE']) option: Str('krbprincipalauthind*', autofill=False, cli_name='auth_ind') -option: Str('krbprincipalname?', autofill=False, cli_name='principal') +option: Principal('krbprincipalname?', autofill=False, cli_name='principal') option: Str('man_by_host*', cli_name='man_by_hosts') option: Flag('no_members', autofill=True, default=True) option: Str('not_man_by_host*', cli_name='not_man_by_hosts') @@ -4401,7 +4401,7 @@ output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>]) output: Output('truncated', type=[<type 'bool'>]) command: service_mod/1 args: 1,13,3 -arg: Str('krbprincipalname', cli_name='principal') +arg: Principal('krbprincipalname', cli_name='principal') option: Str('addattr*', cli_name='addattr') option: Flag('all', autofill=True, cli_name='all', default=False) option: Str('delattr*', cli_name='delattr') @@ -4420,7 +4420,7 @@ output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>]) output: PrimaryKey('value') command: service_remove_cert/1 args: 1,5,3 -arg: Str('krbprincipalname', cli_name='principal') +arg: Principal('krbprincipalname', cli_name='principal') option: Flag('all', autofill=True, cli_name='all', default=False) option: Flag('no_members', autofill=True, default=False) option: Flag('raw', autofill=True, cli_name='raw', default=False) @@ -4431,7 +4431,7 @@ output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>]) output: PrimaryKey('value') command: service_remove_host/1 args: 1,5,3 -arg: Str('krbprincipalname', cli_name='principal') +arg: Principal('krbprincipalname', cli_name='principal') option: Flag('all', autofill=True, cli_name='all', default=False) option: Str('host*', alwaysask=True, cli_name='hosts') option: Flag('no_members', autofill=True, default=False) @@ -4442,7 +4442,7 @@ output: Output('failed', type=[<type 'dict'>]) output: Entry('result') command: service_show/1 args: 1,6,3 -arg: Str('krbprincipalname', cli_name='principal') +arg: Principal('krbprincipalname', cli_name='principal') option: Flag('all', autofill=True, cli_name='all', default=False) option: Flag('no_members', autofill=True, default=False) option: Str('out?') @@ -4646,7 +4646,7 @@ option: Str('ipatokenradiusconfiglink?', cli_name='radius') option: Str('ipatokenradiususername?', cli_name='radius_username') option: StrEnum('ipauserauthtype*', cli_name='user_auth_type', values=[u'password', u'radius', u'otp']) option: DateTime('krbprincipalexpiration?', cli_name='principal_expiration') -option: Str('krbprincipalname?', autofill=True, cli_name='principal') +option: Principal('krbprincipalname?', autofill=True, cli_name='principal') option: Str('l?', cli_name='city') option: Str('loginshell?', cli_name='shell') option: Str('mail*', cli_name='email') @@ -4717,7 +4717,7 @@ option: Str('ipatokenradiusconfiglink?', autofill=False, cli_name='radius') option: Str('ipatokenradiususername?', autofill=False, cli_name='radius_username') option: StrEnum('ipauserauthtype*', autofill=False, cli_name='user_auth_type', values=[u'password', u'radius', u'otp']) option: DateTime('krbprincipalexpiration?', autofill=False, cli_name='principal_expiration') -option: Str('krbprincipalname?', autofill=False, cli_name='principal') +option: Principal('krbprincipalname?', autofill=False, cli_name='principal') option: Str('l?', autofill=False, cli_name='city') option: Str('loginshell?', autofill=False, cli_name='shell') option: Str('mail*', autofill=False, cli_name='email') @@ -5633,7 +5633,7 @@ option: Str('ipatokenradiusconfiglink?', cli_name='radius') option: Str('ipatokenradiususername?', cli_name='radius_username') option: StrEnum('ipauserauthtype*', cli_name='user_auth_type', values=[u'password', u'radius', u'otp']) option: DateTime('krbprincipalexpiration?', cli_name='principal_expiration') -option: Str('krbprincipalname?', autofill=True, cli_name='principal') +option: Principal('krbprincipalname?', autofill=True, cli_name='principal') option: Str('l?', cli_name='city') option: Str('loginshell?', cli_name='shell') option: Str('mail*', cli_name='email') @@ -5732,7 +5732,7 @@ option: Str('ipatokenradiusconfiglink?', autofill=False, cli_name='radius') option: Str('ipatokenradiususername?', autofill=False, cli_name='radius_username') option: StrEnum('ipauserauthtype*', autofill=False, cli_name='user_auth_type', values=[u'password', u'radius', u'otp']) option: DateTime('krbprincipalexpiration?', autofill=False, cli_name='principal_expiration') -option: Str('krbprincipalname?', autofill=False, cli_name='principal') +option: Principal('krbprincipalname?', autofill=False, cli_name='principal') option: Str('l?', autofill=False, cli_name='city') option: Str('loginshell?', autofill=False, cli_name='shell') option: Str('mail*', autofill=False, cli_name='email') @@ -5899,7 +5899,7 @@ option: Bytes('ipavaultsalt?', cli_name='salt') option: StrEnum('ipavaulttype?', autofill=True, cli_name='type', default=u'symmetric', values=[u'standard', u'symmetric', u'asymmetric']) option: Flag('no_members', autofill=True, default=False) option: Flag('raw', autofill=True, cli_name='raw', default=False) -option: Str('service?') +option: Principal('service?') option: Str('setattr*', cli_name='setattr') option: Flag('shared?', autofill=True, default=False) option: Str('username?', cli_name='user') @@ -5914,7 +5914,7 @@ option: Flag('all', autofill=True, cli_name='all', default=False) option: Str('group*', alwaysask=True, cli_name='groups') option: Flag('no_members', autofill=True, default=False) option: Flag('raw', autofill=True, cli_name='raw', default=False) -option: Str('service?') +option: Principal('service?') option: Str('services*', alwaysask=True, cli_name='services') option: Flag('shared?', autofill=True, default=False) option: Str('user*', alwaysask=True, cli_name='users') @@ -5930,7 +5930,7 @@ option: Flag('all', autofill=True, cli_name='all', default=False) option: Str('group*', alwaysask=True, cli_name='groups') option: Flag('no_members', autofill=True, default=False) option: Flag('raw', autofill=True, cli_name='raw', default=False) -option: Str('service?') +option: Principal('service?') option: Str('services*', alwaysask=True, cli_name='services') option: Flag('shared?', autofill=True, default=False) option: Str('user*', alwaysask=True, cli_name='users') @@ -5945,7 +5945,7 @@ arg: Str('cn', cli_name='name') option: Flag('all', autofill=True, cli_name='all', default=False) option: Bytes('nonce') option: Flag('raw', autofill=True, cli_name='raw', default=False) -option: Str('service?') +option: Principal('service?') option: Bytes('session_key') option: Flag('shared?', autofill=True, default=False) option: Str('username?', cli_name='user') @@ -5958,7 +5958,7 @@ command: vault_del/1 args: 1,5,3 arg: Str('cn+', cli_name='name') option: Flag('continue', autofill=True, cli_name='continue', default=False) -option: Str('service?') +option: Principal('service?') option: Flag('shared?', autofill=True, default=False) option: Str('username?', cli_name='user') option: Str('version?') @@ -5975,7 +5975,7 @@ option: StrEnum('ipavaulttype?', autofill=False, cli_name='type', default=u'symm option: Flag('no_members', autofill=True, default=True) option: Flag('pkey_only?', autofill=True, default=False) option: Flag('raw', autofill=True, cli_name='raw', default=False) -option: Str('service?') +option: Principal('service?') option: Flag('services?', autofill=True, default=False) option: Flag('shared?', autofill=True, default=False) option: Int('sizelimit?', autofill=False) @@ -6000,7 +6000,7 @@ option: StrEnum('ipavaulttype?', autofill=False, cli_name='type', default=u'symm option: Flag('no_members', autofill=True, default=False) option: Flag('raw', autofill=True, cli_name='raw', default=False) option: Flag('rights', autofill=True, default=False) -option: Str('service?') +option: Principal('service?') option: Str('setattr*', cli_name='setattr') option: Flag('shared?', autofill=True, default=False) option: Str('username?', cli_name='user') @@ -6015,7 +6015,7 @@ option: Flag('all', autofill=True, cli_name='all', default=False) option: Str('group*', alwaysask=True, cli_name='groups') option: Flag('no_members', autofill=True, default=False) option: Flag('raw', autofill=True, cli_name='raw', default=False) -option: Str('service?') +option: Principal('service?') option: Str('services*', alwaysask=True, cli_name='services') option: Flag('shared?', autofill=True, default=False) option: Str('user*', alwaysask=True, cli_name='users') @@ -6031,7 +6031,7 @@ option: Flag('all', autofill=True, cli_name='all', default=False) option: Str('group*', alwaysask=True, cli_name='groups') option: Flag('no_members', autofill=True, default=False) option: Flag('raw', autofill=True, cli_name='raw', default=False) -option: Str('service?') +option: Principal('service?') option: Str('services*', alwaysask=True, cli_name='services') option: Flag('shared?', autofill=True, default=False) option: Str('user*', alwaysask=True, cli_name='users') @@ -6045,7 +6045,7 @@ args: 1,7,3 arg: Str('cn', cli_name='name') option: Flag('all', autofill=True, cli_name='all', default=False) option: Flag('raw', autofill=True, cli_name='raw', default=False) -option: Str('service?') +option: Principal('service?') option: Bytes('session_key') option: Flag('shared?', autofill=True, default=False) option: Str('username?', cli_name='user') @@ -6060,7 +6060,7 @@ option: Flag('all', autofill=True, cli_name='all', default=False) option: Flag('no_members', autofill=True, default=False) option: Flag('raw', autofill=True, cli_name='raw', default=False) option: Flag('rights', autofill=True, default=False) -option: Str('service?') +option: Principal('service?') option: Flag('shared?', autofill=True, default=False) option: Str('username?', cli_name='user') option: Str('version?') @@ -6082,7 +6082,7 @@ option: Flag('all', autofill=True, cli_name='all', default=False) option: Str('group*', alwaysask=True, cli_name='groups') option: Flag('no_members', autofill=True, default=False) option: Flag('raw', autofill=True, cli_name='raw', default=False) -option: Str('service?') +option: Principal('service?') option: Str('services*', alwaysask=True, cli_name='services') option: Flag('shared?', autofill=True, default=False) option: Str('user*', alwaysask=True, cli_name='users') @@ -6094,7 +6094,7 @@ output: Entry('result') command: vaultcontainer_del/1 args: 0,5,3 option: Flag('continue', autofill=True, cli_name='continue', default=False) -option: Str('service?') +option: Principal('service?') option: Flag('shared?', autofill=True, default=False) option: Str('username?', cli_name='user') option: Str('version?') @@ -6107,7 +6107,7 @@ option: Flag('all', autofill=True, cli_name='all', default=False) option: Str('group*', alwaysask=True, cli_name='groups') option: Flag('no_members', autofill=True, default=False) option: Flag('raw', autofill=True, cli_name='raw', default=False) -option: Str('service?') +option: Principal('service?') option: Str('services*', alwaysask=True, cli_name='services') option: Flag('shared?', autofill=True, default=False) option: Str('user*', alwaysask=True, cli_name='users') @@ -6122,7 +6122,7 @@ option: Flag('all', autofill=True, cli_name='all', default=False) option: Flag('no_members', autofill=True, default=False) option: Flag('raw', autofill=True, cli_name='raw', default=False) option: Flag('rights', autofill=True, default=False) -option: Str('service?') +option: Principal('service?') option: Flag('shared?', autofill=True, default=False) option: Str('username?', cli_name='user') option: Str('version?') diff --git a/ipaserver/plugins/baseuser.py b/ipaserver/plugins/baseuser.py index 9c4af66f9..cbb04aaad 100644 --- a/ipaserver/plugins/baseuser.py +++ b/ipaserver/plugins/baseuser.py @@ -23,13 +23,16 @@ import six from ipalib import api, errors from ipalib import Flag, Int, Password, Str, Bool, StrEnum, DateTime, Bytes +from ipalib.parameters import Principal from ipalib.plugable import Registry from .baseldap import ( DN, LDAPObject, LDAPCreate, LDAPUpdate, LDAPSearch, LDAPDelete, LDAPRetrieve, LDAPAddMember, LDAPRemoveMember) -from .service import validate_certificate +from ipaserver.plugins.service import ( + validate_certificate, validate_realm, normalize_principal) from ipalib.request import context from ipalib import _ +from ipapython import kerberos from ipapython.ipautil import ipa_generate_password from ipapython.ipavalidate import Email from ipalib.util import ( @@ -93,45 +96,14 @@ def convert_nsaccountlock(entry_attrs): nsaccountlock = Bool('temp') entry_attrs['nsaccountlock'] = nsaccountlock.convert(entry_attrs['nsaccountlock'][0]) -def split_principal(principal): - """ - Split the principal into its components and do some basic validation. - - Automatically append our realm if it wasn't provided. - """ - realm = None - parts = principal.split('@') - user = parts[0].lower() - if len(parts) > 2: - raise errors.MalformedUserPrincipal(principal=principal) - - if len(parts) == 2: - realm = parts[1].upper() - # At some point we'll support multiple realms - if realm != api.env.realm: - raise errors.RealmMismatch() - else: - realm = api.env.realm - - return (user, realm) -def validate_principal(ugettext, principal): - """ - All the real work is done in split_principal. - """ - (user, realm) = split_principal(principal) - return None - -def normalize_principal(principal): - """ - Ensure that the name in the principal is lower-case. The realm is - upper-case by convention but it isn't required. - - The principal is validated at this point. - """ - (user, realm) = split_principal(principal) - return unicode('%s@%s' % (user, realm)) +def normalize_user_principal(value): + principal = kerberos.Principal(normalize_principal(value)) + lowercase_components = ((principal.username.lower(),) + + principal.components[1:]) + return unicode( + kerberos.Principal(lowercase_components, realm=principal.realm)) def fix_addressbook_permission_bindrule(name, template, is_new, @@ -239,13 +211,16 @@ class baseuser(LDAPObject): cli_name='shell', label=_('Login shell'), ), - Str('krbprincipalname?', validate_principal, + Principal( + 'krbprincipalname?', + validate_realm, cli_name='principal', label=_('Kerberos principal'), - default_from=lambda uid: '%s@%s' % (uid.lower(), api.env.realm), + default_from=lambda uid: kerberos.Principal.from_text( + uid.lower(), realm=api.env.realm), autofill=True, flags=['no_update'], - normalizer=lambda value: normalize_principal(value), + normalizer=normalize_user_principal, ), DateTime('krbprincipalexpiration?', cli_name='principal_expiration', diff --git a/ipaserver/plugins/caacl.py b/ipaserver/plugins/caacl.py index a543a1de7..3f813a7ef 100644 --- a/ipaserver/plugins/caacl.py +++ b/ipaserver/plugins/caacl.py @@ -3,6 +3,7 @@ # import pyhbac +import six from ipalib import api, errors, output from ipalib import Bool, Str, StrEnum @@ -13,10 +14,11 @@ from .baseldap import ( LDAPUpdate, LDAPRetrieve, LDAPAddMember, LDAPRemoveMember, global_output_params, pkey_to_value) from .hbacrule import is_all -from .service import normalize_principal, split_any_principal from ipalib import _, ngettext from ipapython.dn import DN +if six.PY3: + unicode = str __doc__ = _(""" Manage CA ACL rules. @@ -58,24 +60,21 @@ register = Registry() def _acl_make_request(principal_type, principal, ca_id, profile_id): """Construct HBAC request for the given principal, CA and profile""" - service, name, realm = split_any_principal(principal) req = pyhbac.HbacRequest() req.targethost.name = ca_id req.service.name = profile_id - if principal_type == 'user': - req.user.name = name - elif principal_type == 'host': - req.user.name = name + if principal_type == 'user' or principal_type == 'host': + req.user.name = principal.username elif principal_type == 'service': - req.user.name = normalize_principal(principal) + req.user.name = unicode(principal) groups = [] if principal_type == 'user': - user_obj = api.Command.user_show(name)['result'] + user_obj = api.Command.user_show(principal.username)['result'] groups = user_obj.get('memberof_group', []) groups += user_obj.get('memberofindirect_group', []) elif principal_type == 'host': - host_obj = api.Command.host_show(name)['result'] + host_obj = api.Command.host_show(principal.hostname)['result'] groups = host_obj.get('memberof_hostgroup', []) groups += host_obj.get('memberofindirect_hostgroup', []) req.user.groups = sorted(set(groups)) diff --git a/ipaserver/plugins/cert.py b/ipaserver/plugins/cert.py index 4cd2ab096..2f7904cd7 100644 --- a/ipaserver/plugins/cert.py +++ b/ipaserver/plugins/cert.py @@ -38,18 +38,18 @@ from ipalib import ngettext from ipalib.constants import IPA_CA_CN from ipalib.crud import Create, PKQuery, Retrieve, Search from ipalib.frontend import Method, Object -from ipalib.parameters import Bytes, DateTime, DNParam +from ipalib.parameters import Bytes, DateTime, DNParam, Principal from ipalib.plugable import Registry from .virtual import VirtualCommand from .baseldap import pkey_to_value -from .service import split_any_principal from .certprofile import validate_profile_id from .caacl import acl_evaluate from ipalib.text import _ from ipalib.request import context from ipalib import output -from .service import validate_principal +from ipapython import kerberos from ipapython.dn import DN +from ipaserver.plugins.service import normalize_principal, validate_realm if six.PY3: unicode = str @@ -216,35 +216,21 @@ def normalize_serial_number(num): return unicode(num) -def get_host_from_principal(principal): - """ - Given a principal with or without a realm return the - host portion. - """ - validate_principal(None, principal) - realm = principal.find('@') - slash = principal.find('/') - if realm == -1: - realm = len(principal) - hostname = principal[slash+1:realm] - - return hostname - def ca_enabled_check(): if not api.Command.ca_is_enabled()['result']: raise errors.NotFound(reason=_('CA is not configured')) -def caacl_check(principal_type, principal_string, ca, profile_id): +def caacl_check(principal_type, principal, ca, profile_id): principal_type_map = {USER: 'user', HOST: 'host', SERVICE: 'service'} if not acl_evaluate( principal_type_map[principal_type], - principal_string, ca, profile_id): + principal, ca, profile_id): raise errors.ACIError(info=_( "Principal '%(principal)s' " "is not permitted to use CA '%(ca)s' " "with profile '%(profile_id)s' for certificate issuance." ) % dict( - principal=principal_string, + principal=unicode(principal), ca=ca, profile_id=profile_id ) @@ -386,10 +372,12 @@ class cert_request(Create, BaseCertMethod, VirtualCommand): operation="request certificate" takes_options = ( - Str( + Principal( 'principal', + validate_realm, label=_('Principal'), doc=_('Principal for this certificate (e.g. HTTP/test.example.com)'), + normalizer=normalize_principal ), Flag( 'add', @@ -432,27 +420,29 @@ class cert_request(Create, BaseCertMethod, VirtualCommand): taskgroup (directly or indirectly via role membership). """ - principal_string = kw.get('principal') - principal = split_any_principal(principal_string) - servicename, principal_name, realm = principal - if servicename is None: + principal = kw.get('principal') + principal_string = unicode(principal) + + if principal.is_user: principal_type = USER - elif servicename == 'host': + elif principal.is_host: principal_type = HOST else: principal_type = SERVICE - bind_principal = split_any_principal(getattr(context, 'principal')) - bind_service, bind_name, bind_realm = bind_principal + bind_principal = kerberos.Principal( + getattr(context, 'principal')) + bind_principal_string = unicode(bind_principal) - if bind_service is None: + if bind_principal.is_user: bind_principal_type = USER - elif bind_service == 'host': + elif bind_principal.is_host: bind_principal_type = HOST else: bind_principal_type = SERVICE - if bind_principal != principal and bind_principal_type != HOST: + if (bind_principal_string != principal_string and + bind_principal_type != HOST): # Can the bound principal request certs for another principal? self.check_access() @@ -463,7 +453,7 @@ class cert_request(Create, BaseCertMethod, VirtualCommand): bypass_caacl = False if not bypass_caacl: - caacl_check(principal_type, principal_string, ca, profile_id) + caacl_check(principal_type, principal, ca, profile_id) try: subject = pkcs10.get_subject(csr) @@ -474,7 +464,8 @@ class cert_request(Create, BaseCertMethod, VirtualCommand): error=_("Failure decoding Certificate Signing Request: %s") % e) # self-service and host principals may bypass SAN permission check - if bind_principal != principal and bind_principal_type != HOST: + if (bind_principal_string != principal_string + and bind_principal_type != HOST): if '2.5.29.17' in extensions: self.check_access('request certificate with subjectaltname') @@ -486,9 +477,11 @@ class cert_request(Create, BaseCertMethod, VirtualCommand): if principal_type == SERVICE: principal_obj = api.Command['service_show'](principal_string, all=True) elif principal_type == HOST: - principal_obj = api.Command['host_show'](principal_name, all=True) + principal_obj = api.Command['host_show']( + principal.hostname, all=True) elif principal_type == USER: - principal_obj = api.Command['user_show'](principal_name, all=True) + principal_obj = api.Command['user_show']( + principal.username, all=True) except errors.NotFound as e: if add: if principal_type == SERVICE: @@ -512,14 +505,14 @@ class cert_request(Create, BaseCertMethod, VirtualCommand): error=_("No Common Name was found in subject of request.")) if principal_type in (SERVICE, HOST): - if cn.lower() != principal_name.lower(): + if cn.lower() != principal.hostname.lower(): raise errors.ACIError( info=_("hostname in subject of request '%(cn)s' " "does not match principal hostname '%(hostname)s'") - % dict(cn=cn, hostname=principal_name)) + % dict(cn=cn, hostname=principal.hostname)) elif principal_type == USER: # check user name - if cn != principal_name: + if cn != principal.username: raise errors.ValidationError( name='csr', error=_("DN commonName does not match user's login") @@ -545,13 +538,11 @@ class cert_request(Create, BaseCertMethod, VirtualCommand): if name_type == pkcs10.SAN_DNSNAME: name = unicode(name) alt_principal_obj = None - alt_principal_string = None + alt_principal_string = unicode(principal) try: if principal_type == HOST: - alt_principal_string = 'host/%s@%s' % (name, realm) alt_principal_obj = api.Command['host_show'](name, all=True) elif principal_type == SERVICE: - alt_principal_string = '%s/%s@%s' % (servicename, name, realm) alt_principal_obj = api.Command['service_show']( alt_principal_string, all=True) elif principal_type == USER: @@ -574,11 +565,10 @@ class cert_request(Create, BaseCertMethod, VirtualCommand): "Insufficient privilege to create a certificate " "with subject alt name '%s'.") % name) if alt_principal_string is not None and not bypass_caacl: - caacl_check( - principal_type, alt_principal_string, ca, profile_id) + caacl_check(principal_type, principal, ca, profile_id) elif name_type in (pkcs10.SAN_OTHERNAME_KRB5PRINCIPALNAME, pkcs10.SAN_OTHERNAME_UPN): - if split_any_principal(name) != principal: + if name != principal_string: raise errors.ACIError( info=_("Principal '%s' in subject alt name does not " "match requested principal") % name) @@ -619,9 +609,9 @@ class cert_request(Create, BaseCertMethod, VirtualCommand): if principal_type == SERVICE: api.Command['service_mod'](principal_string, **kwargs) elif principal_type == HOST: - api.Command['host_mod'](principal_name, **kwargs) + api.Command['host_mod'](principal.hostname, **kwargs) elif principal_type == USER: - api.Command['user_mod'](principal_name, **kwargs) + api.Command['user_mod'](principal.username, **kwargs) return dict( result=result, @@ -748,10 +738,10 @@ class cert_show(Retrieve, CertMethod, VirtualCommand): self.check_access() except errors.ACIError as acierr: self.debug("Not granted by ACI to retrieve certificate, looking at principal") - bind_principal = getattr(context, 'principal') - if not bind_principal.startswith('host/'): + bind_principal = kerberos.Principal(getattr(context, 'principal')) + if not bind_principal.is_host: raise acierr - hostname = get_host_from_principal(bind_principal) + hostname = bind_principal.hostname ca_obj = api.Command.ca_show(options['cacn'])['result'] issuer_dn = ca_obj['ipacasubjectdn'][0] diff --git a/ipaserver/plugins/host.py b/ipaserver/plugins/host.py index de0aca5ca..6210e8c16 100644 --- a/ipaserver/plugins/host.py +++ b/ipaserver/plugins/host.py @@ -25,6 +25,7 @@ import six from ipalib import api, errors, util from ipalib import messages from ipalib import Str, Flag, Bytes +from ipalib.parameters import Principal from ipalib.plugable import Registry from .baseldap import (LDAPQuery, LDAPObject, LDAPCreate, LDAPDelete, LDAPUpdate, LDAPSearch, @@ -32,7 +33,8 @@ from .baseldap import (LDAPQuery, LDAPObject, LDAPCreate, LDAPRemoveMember, host_is_master, pkey_to_value, add_missing_object_class, LDAPAddAttribute, LDAPRemoveAttribute) -from .service import (split_principal, validate_certificate, +from ipaserver.plugins.service import ( + validate_realm, normalize_principal, validate_certificate, set_certificate_attrs, ticket_flags_params, update_krbticketflags, set_kerberos_attrs, rename_ipaallowedtoperform_from_ldap, rename_ipaallowedtoperform_to_ldap, revoke_certs) @@ -56,6 +58,7 @@ from ipapython.ipautil import ipa_generate_password, CheckedIPAddress from ipapython.dnsutil import DNSName from ipapython.ssh import SSHPublicKey from ipapython.dn import DN +from ipapython import kerberos from functools import reduce if six.PY3: @@ -509,8 +512,11 @@ class host(LDAPObject): label=_('Revocation reason'), flags={'virtual_attribute', 'no_create', 'no_update', 'no_search'}, ), - Str('krbprincipalname?', + Principal( + 'krbprincipalname?', + validate_realm, label=_('Principal name'), + normalizer=normalize_principal, flags=['no_create', 'no_update', 'no_search'], ), Str('macaddress*', @@ -758,8 +764,9 @@ class host_del(LDAPDelete): break else: for entry_attrs in services: - principal = entry_attrs['krbprincipalname'][0] - (service, hostname, realm) = split_principal(principal) + principal = kerberos.Principal( + entry_attrs['krbprincipalname'][0]) + hostname = principal.hostname if hostname.lower() == fqdn: api.Command['service_del'](principal) updatedns = options.get('updatedns', False) @@ -830,10 +837,13 @@ class host_mod(LDAPUpdate): member_attributes = ['managedby'] takes_options = LDAPUpdate.takes_options + ( - Str('krbprincipalname?', + Principal( + 'krbprincipalname?', + validate_realm, cli_name='principalname', label=_('Principal name'), doc=_('Kerberos principal name for this host'), + normalizer=normalize_principal, attribute=True, ), Flag('updatedns?', @@ -1155,8 +1165,9 @@ class host_disable(LDAPQuery): break else: for entry_attrs in services: - principal = entry_attrs['krbprincipalname'][0] - (service, hostname, realm) = split_principal(principal) + principal = kerberos.Principal( + entry_attrs['krbprincipalname'][0]) + hostname = principal.hostname if hostname.lower() == fqdn: try: api.Command['service_disable'](principal) diff --git a/ipaserver/plugins/passwd.py b/ipaserver/plugins/passwd.py index 253a0d35d..1576c4ca8 100644 --- a/ipaserver/plugins/passwd.py +++ b/ipaserver/plugins/passwd.py @@ -17,15 +17,22 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. +import six + from ipalib import api, errors, krb_utils from ipalib import Command -from ipalib import Str, Password +from ipalib import Password from ipalib import _ from ipalib import output +from ipalib.parameters import Principal from ipalib.plugable import Registry -from .baseuser import validate_principal, normalize_principal from ipalib.request import context +from ipapython import kerberos from ipapython.dn import DN +from ipaserver.plugins.service import validate_realm, normalize_principal + +if six.PY3: + unicode = str __doc__ = _(""" Set a user's password @@ -59,7 +66,7 @@ def get_current_password(principal): be ignored later. """ current_principal = krb_utils.get_principal() - if current_principal == normalize_principal(principal): + if current_principal == unicode(normalize_principal(principal)): return None else: return MAGIC_VALUE @@ -69,12 +76,14 @@ class passwd(Command): __doc__ = _("Set a user's password.") takes_args = ( - Str('principal', validate_principal, + Principal( + 'principal', + validate_realm, cli_name='user', label=_('User name'), primary_key=True, autofill=True, - default_from=lambda: krb_utils.get_principal(), + default_from=lambda: kerberos.Principal(krb_utils.get_principal()), normalizer=lambda value: normalize_principal(value), ), Password('password', @@ -114,6 +123,8 @@ class passwd(Command): """ ldap = self.api.Backend.ldap2 + principal = unicode(principal) + entry_attrs = ldap.find_entry_by_attr( 'krbprincipalname', principal, 'posixaccount', [''], DN(api.env.container_user, api.env.basedn) diff --git a/ipaserver/plugins/service.py b/ipaserver/plugins/service.py index c44ad7ac2..70bf31fd4 100644 --- a/ipaserver/plugins/service.py +++ b/ipaserver/plugins/service.py @@ -23,6 +23,7 @@ import six from ipalib import api, errors, messages from ipalib import Bytes, StrEnum, Bool, Str, Flag +from ipalib.parameters import Principal from ipalib.plugable import Registry from .baseldap import ( host_is_master, @@ -43,6 +44,7 @@ from ipalib import x509 from ipalib import _, ngettext from ipalib import util from ipalib import output +from ipapython import kerberos from ipapython.dn import DN import nss.nss as nss @@ -176,55 +178,29 @@ _ticket_flags_map = { _ticket_flags_default = _ticket_flags_map['ipakrbrequirespreauth'] -def split_any_principal(principal): - service = hostname = realm = None - - # Break down the principal into its component parts, which may or - # may not include the realm. - sp = principal.split('/') - name_and_realm = None - if len(sp) > 2: - raise errors.MalformedServicePrincipal(reason=_('unable to determine service')) - elif len(sp) == 2: - service = sp[0] - if len(service) == 0: - raise errors.MalformedServicePrincipal(reason=_('blank service')) - name_and_realm = sp[1] - else: - name_and_realm = sp[0] - - sr = name_and_realm.split('@') - if len(sr) > 2: - raise errors.MalformedServicePrincipal( - reason=_('unable to determine realm')) - - hostname = sr[0].lower() - if len(sr) == 2: - realm = sr[1].upper() - # At some point we'll support multiple realms - if realm != api.env.realm: - raise errors.RealmMismatch() - else: - realm = api.env.realm - - # Note that realm may be None. - return service, hostname, realm - -def split_principal(principal): - service, name, realm = split_any_principal(principal) - if service is None: - raise errors.MalformedServicePrincipal(reason=_('missing service')) - return service, name, realm - -def validate_principal(ugettext, principal): - (service, hostname, principal) = split_principal(principal) - return None - -def normalize_principal(principal): - # The principal is already validated when it gets here - (service, hostname, realm) = split_principal(principal) - # Put the principal back together again - principal = '%s/%s@%s' % (service, hostname, realm) + +def validate_realm(ugettext, principal): + """ + Check that the principal's realm matches IPA realm if present + """ + realm = principal.realm + if realm is not None and realm != api.env.realm: + raise errors.RealmMismatch() + + +def normalize_principal(value): + """ + Ensure that the name in the principal is lower-case. The realm is + upper-case by convention but it isn't required. + + The principal is validated at this point. + """ + try: + principal = kerberos.Principal(value, realm=api.env.realm) + except ValueError: + raise errors.ValidationError( + name='principal', reason=_("Malformed principal")) + return unicode(principal) def validate_certificate(ugettext, cert): @@ -291,16 +267,16 @@ def set_certificate_attrs(entry_attrs): entry_attrs['md5_fingerprint'] = unicode(nss.data_to_hex(nss.md5_digest(cert.der_data), 64)[0]) entry_attrs['sha1_fingerprint'] = unicode(nss.data_to_hex(nss.sha1_digest(cert.der_data), 64)[0]) -def check_required_principal(ldap, hostname, service): +def check_required_principal(ldap, principal): """ Raise an error if the host of this prinicipal is an IPA master and one of the principals required for proper execution. """ try: - host_is_master(ldap, hostname) + host_is_master(ldap, principal.hostname) except errors.ValidationError as e: service_types = ['HTTP', 'ldap', 'DNS', 'dogtagldap'] - if service in service_types: + if principal.service_name in service_types: raise errors.ValidationError(name='principal', error=_('This principal is required by the IPA master')) def update_krbticketflags(ldap, entry_attrs, attrs_list, options, existing): @@ -457,12 +433,15 @@ class service(LDAPObject): label_singular = _('Service') takes_params = ( - Str('krbprincipalname', validate_principal, + Principal( + 'krbprincipalname', + validate_realm, cli_name='principal', label=_('Principal'), doc=_('Service principal'), primary_key=True, - normalizer=lambda value: normalize_principal(value), + normalizer=normalize_principal, + require_service=True ), Bytes('usercertificate*', validate_certificate, cli_name='certificate', @@ -560,8 +539,10 @@ class service_add(LDAPCreate): def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): assert isinstance(dn, DN) - (service, hostname, realm) = split_principal(keys[-1]) - if service.lower() == 'host' and not options['force']: + principal = keys[-1] + hostname = principal.hostname + + if principal.is_host and not options['force']: raise errors.HostService() try: @@ -619,8 +600,7 @@ class service_del(LDAPDelete): # In the case of services we don't want IPA master services to be # deleted. This is a limited few though. If the user has their own # custom services allow them to manage them. - (service, hostname, realm) = split_principal(keys[-1]) - check_required_principal(ldap, hostname, service) + check_required_principal(ldap, keys[-1]) if self.api.Command.ca_is_enabled()['result']: try: entry_attrs = ldap.get_entry(dn, ['usercertificate']) @@ -647,8 +627,6 @@ class service_mod(LDAPUpdate): self.obj.validate_ipakrbauthzdata(entry_attrs) - (service, hostname, realm) = split_principal(keys[-1]) - # verify certificates certs = entry_attrs.get('usercertificate') or [] certs_der = [x509.normalize_certificate(c) for c in certs] @@ -873,8 +851,7 @@ class service_disable(LDAPQuery): dn = self.obj.get_dn(*keys, **options) entry_attrs = ldap.get_entry(dn, ['usercertificate']) - (service, hostname, realm) = split_principal(keys[-1]) - check_required_principal(ldap, hostname, service) + check_required_principal(ldap, keys[-1]) # See if we do any work at all here and if not raise an exception done_work = False diff --git a/ipaserver/plugins/vault.py b/ipaserver/plugins/vault.py index 380e4d478..c9b7cb942 100644 --- a/ipaserver/plugins/vault.py +++ b/ipaserver/plugins/vault.py @@ -17,25 +17,31 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. +import six + from ipalib.frontend import Command, Object from ipalib import api, errors from ipalib import Bytes, Flag, Str, StrEnum from ipalib import output from ipalib.crud import PKQuery, Retrieve +from ipalib.parameters import Principal from ipalib.plugable import Registry from .baseldap import LDAPObject, LDAPCreate, LDAPDelete,\ LDAPSearch, LDAPUpdate, LDAPRetrieve, LDAPAddMember, LDAPRemoveMember,\ LDAPModMember, pkey_to_value from ipalib.request import context -from .baseuser import split_principal -from .service import normalize_principal +from .service import normalize_principal, validate_realm from ipalib import _, ngettext +from ipapython import kerberos from ipapython.dn import DN if api.env.in_server: import pki.account import pki.key +if six.PY3: + unicode = str + __doc__ = _(""" Vaults """) + _(""" @@ -191,8 +197,9 @@ EXAMPLES: register = Registry() vault_options = ( - Str( + Principal( 'service?', + validate_realm, doc=_('Service name of the service vault'), normalizer=normalize_principal, ), @@ -342,17 +349,15 @@ class vaultcontainer(LDAPObject): parent_dn = super(vaultcontainer, self).get_dn(*keys, **options) if not count: - principal = getattr(context, 'principal') + principal = kerberos.Principal(getattr(context, 'principal')) - if principal.startswith('host/'): + if principal.is_host: raise errors.NotImplementedError( reason=_('Host is not supported')) - - (name, realm) = split_principal(principal) - if '/' in name: - service = principal + elif principal.is_service: + service = unicode(principal) else: - user = name + user = principal.username if service: dn = DN(('cn', service), ('cn', 'services'), parent_dn) @@ -660,17 +665,15 @@ class vault(LDAPObject): rdns = DN(*dn[:-len(container_dn)]) if not count: - principal = getattr(context, 'principal') + principal = kerberos.Principal(getattr(context, 'principal')) - if principal.startswith('host/'): + if principal.is_host: raise errors.NotImplementedError( reason=_('Host is not supported')) - - (name, realm) = split_principal(principal) - if '/' in name: - service = principal + elif principal.is_service: + service = unicode(principal) else: - user = name + user = principal.username if service: parent_dn = DN(('cn', service), ('cn', 'services'), container_dn) @@ -770,12 +773,11 @@ class vault_add_internal(LDAPCreate): raise errors.InvocationError( format=_('KRA service is not enabled')) - principal = getattr(context, 'principal') - (name, realm) = split_principal(principal) - if '/' in name: - owner_dn = self.api.Object.service.get_dn(name) + principal = kerberos.Principal(getattr(context, 'principal')) + if principal.is_service: + owner_dn = self.api.Object.service.get_dn(unicode(principal)) else: - owner_dn = self.api.Object.user.get_dn(name) + owner_dn = self.api.Object.user.get_dn(principal.username) parent_dn = DN(*dn[1:]) diff --git a/ipatests/test_xmlrpc/test_host_plugin.py b/ipatests/test_xmlrpc/test_host_plugin.py index e6fc68a15..4ddabefff 100644 --- a/ipatests/test_xmlrpc/test_host_plugin.py +++ b/ipatests/test_xmlrpc/test_host_plugin.py @@ -357,6 +357,7 @@ class TestHostWithService(XMLRPC_test): result=dict( dn=service1dn, krbprincipalname=[service1], + krbcanonicalname=[service1], objectclass=objectclasses.service, managedby_host=[host.fqdn], ipauniqueid=[fuzzy_uuid], diff --git a/ipatests/test_xmlrpc/test_service_plugin.py b/ipatests/test_xmlrpc/test_service_plugin.py index 2e38b8d6b..69af06873 100644 --- a/ipatests/test_xmlrpc/test_service_plugin.py +++ b/ipatests/test_xmlrpc/test_service_plugin.py @@ -193,6 +193,7 @@ class test_service(Declarative): result=dict( dn=service1dn, krbprincipalname=[service1], + krbcanonicalname=[service1], objectclass=objectclasses.service, ipauniqueid=[fuzzy_uuid], managedby_host=[fqdn1], @@ -262,6 +263,7 @@ class test_service(Declarative): dict( dn=service1dn, krbprincipalname=[service1], + krbcanonicalname=service1, managedby_host=[fqdn1], has_keytab=False, ), @@ -281,6 +283,7 @@ class test_service(Declarative): dict( dn=service1dn, krbprincipalname=[service1], + krbcanonicalname=service1, has_keytab=False, ), ], @@ -619,7 +622,9 @@ class test_service(Declarative): dict( desc='Create service with malformed principal "foo"', command=('service_add', [u'foo'], {}), - expected=errors.MalformedServicePrincipal(reason='missing service') + expected=errors.ValidationError( + name='principal', + error='Service principal is required') ), @@ -715,6 +720,7 @@ class test_service_in_role(Declarative): result=dict( dn=service1dn, krbprincipalname=[service1], + krbcanonicalname=[service1], objectclass=objectclasses.service, ipauniqueid=[fuzzy_uuid], managedby_host=[fqdn1], @@ -919,6 +925,7 @@ class test_service_allowed_to(Declarative): result=dict( dn=service1dn, krbprincipalname=[service1], + krbcanonicalname=[service1], objectclass=objectclasses.service, ipauniqueid=[fuzzy_uuid], managedby_host=[fqdn1], diff --git a/ipatests/test_xmlrpc/test_stageuser_plugin.py b/ipatests/test_xmlrpc/test_stageuser_plugin.py index 96f7e22b3..34cfaf87a 100644 --- a/ipatests/test_xmlrpc/test_stageuser_plugin.py +++ b/ipatests/test_xmlrpc/test_stageuser_plugin.py @@ -345,9 +345,9 @@ class TestCreateInvalidAttributes(XMLRPC_test): stageduser.ensure_missing() command = stageduser.make_create_command( options={u'krbprincipalname': invalidrealm2}) - with raises_exact(errors.MalformedUserPrincipal( - message=u'Principal is not of the form user@REALM: \'%s\'' % - invalidrealm2)): + with raises_exact(errors.ConversionError( + name='principal', error="Malformed principal: '{}'".format( + invalidrealm2))): command() diff --git a/ipatests/test_xmlrpc/test_user_plugin.py b/ipatests/test_xmlrpc/test_user_plugin.py index 6d58c53aa..8245dc7f0 100644 --- a/ipatests/test_xmlrpc/test_user_plugin.py +++ b/ipatests/test_xmlrpc/test_user_plugin.py @@ -806,8 +806,9 @@ class TestPrincipals(XMLRPC_test): ) command = testuser.make_create_command() - with raises_exact(errors.MalformedUserPrincipal( - principal=u'tuser1@BAD@NOTFOUND.ORG')): + with raises_exact(errors.ConversionError( + name='principal', error="Malformed principal: '{}'".format( + testuser.kwargs['krbprincipalname']))): command() def test_set_principal_expiration(self, user): |