From ae3c281a64c994cae10709a2e284f3830de64781 Mon Sep 17 00:00:00 2001 From: Florence Blanc-Renaud Date: Jun 01 2023 06:20:37 +0000 Subject: XMLRPC tests: test new passkey commands Add tests for: ipa passkeyconfig-show ipa passkeyconfig-mod ipa user-add-passkey LOGIN PASSKEY ipa user-remove-passkey LOGIN PASSKEY ipa stageuser-add-passkey LOGIN PASSKEY ipa stageuser-remove-passkey LOGIN PASSKEY Related: https://pagure.io/freeipa/issue/9261 Signed-off-by: Florence Blanc-Renaud Reviewed-By: Alexander Bokovoy --- diff --git a/ipatests/test_xmlrpc/objectclasses.py b/ipatests/test_xmlrpc/objectclasses.py index 8a1a7bd..2d48e94 100644 --- a/ipatests/test_xmlrpc/objectclasses.py +++ b/ipatests/test_xmlrpc/objectclasses.py @@ -244,3 +244,9 @@ idp = [ 'top', 'ipaidp', ] + +passkeyconfig = [ + 'top', + 'nscontainer', + 'ipapasskeyconfigobject', +] diff --git a/ipatests/test_xmlrpc/test_passkey_plugin.py b/ipatests/test_xmlrpc/test_passkey_plugin.py new file mode 100644 index 0000000..2fe6dd9 --- /dev/null +++ b/ipatests/test_xmlrpc/test_passkey_plugin.py @@ -0,0 +1,144 @@ +# +# Copyright (C) 2022 FreeIPA Contributors see COPYING for license +# + +import pytest + +from ipalib import errors +from ipatests.test_xmlrpc.xmlrpc_test import XMLRPC_test, raises_exact +from ipatests.test_xmlrpc.tracker.passkey_plugin import PasskeyconfigTracker +from ipatests.test_xmlrpc.tracker.user_plugin import UserTracker +from ipatests.test_xmlrpc.tracker.stageuser_plugin import StageUserTracker + + +@pytest.fixture(scope='class') +def passkey_config(request, xmlrpc_setup): + tracker = PasskeyconfigTracker() + return tracker.make_fixture(request) + + +class TestPasskeyconfig(XMLRPC_test): + @pytest.mark.parametrize("userverification", ['on', 'off', 'default']) + def test_config_mod(self, passkey_config, userverification): + """ + Test the passkeyconfig-mod CLI with possible values for + --require-user-verification parameter. + """ + passkey_config.update( + {'iparequireuserverification': userverification}, + {'iparequireuserverification': [userverification]} + ) + + def test_config_mod_invalid_requireverif(self, passkey_config): + """ + Test the passkeyconfig-mod CLI with invalid values for + --require-user-verification parameter. + """ + cmd = passkey_config.make_update_command( + updates={'iparequireuserverification': 'Invalid'} + ) + + with pytest.raises(errors.ValidationError): + cmd() + + def test_config_show(self, passkey_config): + """ + Test the passkeyconfig-show command. + """ + passkey_config.retrieve() + + +PASSKEY_USER = 'passkeyuser' +PASSKEY_KEY = ("passkey:" + "E8Zay6UJm6PG/GcQnej2WMyUrWqijejBCqPWFX6THPrx" + "ab01Z59bUgutipn5MIk8/zMU6RBlp7jSbkNJsZtomw==," + "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgryfr3YR" + "M9OVdWHEDrbvcSyT5D0b/8Ks+fMp8MM0BXV/FOo436ZP" + "jUqSU+2LOXVGdKkJU1XBiwl+n/X+vGD1vw==") + + +@pytest.fixture +def passkeyuser(request): + user = UserTracker(PASSKEY_USER, 'passkey', 'user') + return user.make_fixture(request) + + +class TestAddRemovePasskey(XMLRPC_test): + def test_add_passkey(self, passkeyuser): + passkeyuser.ensure_exists() + passkeyuser.add_passkey(ipapasskey=PASSKEY_KEY) + passkeyuser.ensure_missing() + + def test_remove_passkey(self, passkeyuser): + passkeyuser.ensure_exists() + passkeyuser.add_passkey(ipapasskey=PASSKEY_KEY) + passkeyuser.remove_passkey(ipapasskey=PASSKEY_KEY) + + @pytest.mark.parametrize("key", ['wrongval', 'passkey:123', 'passkey,123']) + def test_add_passkey_invalid(self, passkeyuser, key): + passkeyuser.ensure_exists() + cmd = passkeyuser.make_command('user_add_passkey', + passkeyuser.name) + with raises_exact(errors.ValidationError( + name='passkey', + error='"{}" is not a valid passkey mapping'.format(key))): + cmd(key) + + def test_add_passkey_invalidid(self, passkeyuser): + passkeyuser.ensure_exists() + key = ("passkey:123," + "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgryfr3YRM9OVdWHEDrbvc" + "SyT5D0b/8Ks+fMp8MM0BXV/FOo436ZPjUqSU+2LOXVGdKkJU1XBiwl+n/X" + "+vGD1vw==") + msg = '"{}" is not a valid passkey mapping, invalid id' + cmd = passkeyuser.make_command('user_add_passkey', + passkeyuser.name) + with raises_exact(errors.ValidationError( + name='passkey', + error=msg.format(key))): + cmd(key) + + def test_add_passkey_invalidpem(self, passkeyuser): + passkeyuser.ensure_exists() + key = ("passkey:" + "E8Zay6UJm6PG/GcQnej2WMyUrWqijejBCqPWFX6THPrxab01Z59bUguti" + "pn5MIk8/zMU6RBlp7jSbkNJsZtomw==," + "wrongpem") + msg = '"{}" is not a valid passkey mapping, invalid key' + cmd = passkeyuser.make_command('user_add_passkey', + passkeyuser.name) + with raises_exact(errors.ValidationError( + name='passkey', + error=msg.format(key))): + cmd(key) + + +STAGEPASSKEY_USER = 'stagepasskeyuser' + + +@pytest.fixture +def stagepasskeyuser(request): + user = StageUserTracker(STAGEPASSKEY_USER, 'stagepasskey', 'user') + return user.make_fixture(request) + + +class TestStageAddRemovePassKey(XMLRPC_test): + def test_add_passkey(self, stagepasskeyuser): + stagepasskeyuser.ensure_exists() + stagepasskeyuser.add_passkey(ipapasskey=PASSKEY_KEY) + stagepasskeyuser.ensure_missing() + + def test_remove_passkey(self, stagepasskeyuser): + stagepasskeyuser.ensure_exists() + stagepasskeyuser.add_passkey(ipapasskey=PASSKEY_KEY) + stagepasskeyuser.remove_passkey(ipapasskey=PASSKEY_KEY) + + @pytest.mark.parametrize("key", ['wrongval', 'passkey:123', 'passkey,123']) + def test_add_passkey_invalid(self, stagepasskeyuser, key): + stagepasskeyuser.ensure_exists() + cmd = stagepasskeyuser.make_command('user_add_passkey', + stagepasskeyuser.name) + with raises_exact(errors.ValidationError( + name='passkey', + error='"{}" is not a valid passkey mapping'.format(key))): + cmd(key) diff --git a/ipatests/test_xmlrpc/tracker/passkey_plugin.py b/ipatests/test_xmlrpc/tracker/passkey_plugin.py new file mode 100644 index 0000000..14b0e93 --- /dev/null +++ b/ipatests/test_xmlrpc/tracker/passkey_plugin.py @@ -0,0 +1,126 @@ +# +# Copyright (C) 2022 FreeIPA Contributors see COPYING for license +# + +from ipapython.dn import DN +from ipatests.test_xmlrpc import objectclasses +from ipatests.test_xmlrpc.tracker.base import ConfigurationTracker +from ipatests.test_xmlrpc.xmlrpc_test import fuzzy_string +from ipatests.util import assert_deepequal + + +class PasskeyconfigTracker(ConfigurationTracker): + retrieve_keys = { + 'dn', + 'iparequireuserverification', + } + + retrieve_all_keys = retrieve_keys | { + 'cn', + 'objectclass', + 'aci', + } + + update_keys = retrieve_keys - {'dn'} + singlevalue_keys = {'iparequireuserverification'} + + def __init__(self, default_version=None): + super(PasskeyconfigTracker, self).__init__( + default_version=default_version) + + self.attrs = { + 'dn': DN(self.api.env.container_passkey, self.api.env.basedn), + 'cn': [self.api.env.container_passkey[0].value], + 'objectclass': objectclasses.passkeyconfig, + 'aci': [fuzzy_string], + 'iparequireuserverif': self.api.Command.passkeyconfig_show( + )['result']['iparequireuserverification'], + } + + def make_update_command(self, updates): + return self.make_command('passkeyconfig_mod', **updates) + + def check_update(self, result, extra_keys=()): + assert_deepequal( + dict( + value=None, + summary=None, + result=self.filter_attrs(self.update_keys | set(extra_keys)), + ), + result + ) + + def make_retrieve_command(self, all=False, raw=False): + return self.make_command('passkeyconfig_show', all=all, raw=raw) + + def check_retrieve(self, result, all=False, raw=False): + if all: + expected = self.filter_attrs(self.retrieve_all_keys) + else: + expected = self.filter_attrs(self.retrieve_keys) + assert_deepequal( + dict( + value=None, + summary=None, + result=expected, + ), + result + ) + + +class PasskeyMixin: + def _make_add_passkey(self): + raise NotImplementedError("_make_add_passkey method must be " + "implemented in instance.") + + def _make_remove_passkey(self): + raise NotImplementedError("_make_remove_passkey method must be " + "implemented in instance.") + + def add_passkey(self, **kwargs): + cmd = self._make_add_passkey() + result = cmd(**kwargs) + data = kwargs.get('ipapasskey', []) + if not isinstance(data, list): + data = [data] + self.attrs.setdefault('ipapasskey', []).extend(data) + + expected = dict( + summary=('Added passkey mappings to user ' + '"{}"'.format(self.name)), + value=self.name, + result=dict( + uid=(self.name,), + ), + ) + + if self.attrs['ipapasskey']: + expected['result']['ipapasskey'] = ( + self.attrs['ipapasskey']) + + assert_deepequal(expected, result) + + def remove_passkey(self, **kwargs): + cmd = self._make_remove_passkey() + + result = cmd(**kwargs) + data = kwargs.get('ipapasskey', []) + if not isinstance(data, list): + data = [data] + + for key in data: + self.attrs['ipapasskey'].remove(key) + + expected = dict( + summary=('Removed passkey mappings from user ' + '"{}"'.format(self.name)), + value=self.name, + result=dict( + uid=(self.name,), + ), + ) + if self.attrs['ipapasskey']: + expected['result']['ipapasskey'] = ( + self.attrs['ipapasskey']) + + assert_deepequal(expected, result) diff --git a/ipatests/test_xmlrpc/tracker/stageuser_plugin.py b/ipatests/test_xmlrpc/tracker/stageuser_plugin.py index 31a8ce5..17744a9 100644 --- a/ipatests/test_xmlrpc/tracker/stageuser_plugin.py +++ b/ipatests/test_xmlrpc/tracker/stageuser_plugin.py @@ -8,6 +8,7 @@ from ipalib import api, errors from ipaplatform.constants import constants as platformconstants from ipatests.test_xmlrpc.tracker.base import Tracker +from ipatests.test_xmlrpc.tracker.passkey_plugin import PasskeyMixin from ipatests.test_xmlrpc.tracker.kerberos_aliases import KerberosAliasMixin from ipatests.test_xmlrpc import objectclasses from ipatests.test_xmlrpc.xmlrpc_test import ( @@ -30,7 +31,7 @@ sshpubkeyfp = (u'SHA256:cStA9o5TRSARbeketEOooMUMSWRSsArIAXloBZ4vNsE ' 'public key test (ssh-rsa)') -class StageUserTracker(KerberosAliasMixin, Tracker): +class StageUserTracker(PasskeyMixin, KerberosAliasMixin, Tracker): """ Tracker class for staged user LDAP object Implements helper functions for host plugin. @@ -285,3 +286,10 @@ class StageUserTracker(KerberosAliasMixin, Tracker): def _make_remove_alias_cmd(self): return self.make_command('stageuser_remove_principal', self.name) + + # Passkey mapping methods + def _make_add_passkey(self): + return self.make_command('stageuser_add_passkey', self.name) + + def _make_remove_passkey(self): + return self.make_command('stageuser_remove_passkey', self.name) diff --git a/ipatests/test_xmlrpc/tracker/user_plugin.py b/ipatests/test_xmlrpc/tracker/user_plugin.py index 499e422..0c4f5c5 100644 --- a/ipatests/test_xmlrpc/tracker/user_plugin.py +++ b/ipatests/test_xmlrpc/tracker/user_plugin.py @@ -16,12 +16,14 @@ from ipatests.test_xmlrpc.xmlrpc_test import ( from ipatests.test_xmlrpc.tracker.base import Tracker from ipatests.test_xmlrpc.tracker.kerberos_aliases import KerberosAliasMixin from ipatests.test_xmlrpc.tracker.certmapdata import CertmapdataMixin +from ipatests.test_xmlrpc.tracker.passkey_plugin import PasskeyMixin if six.PY3: unicode = str -class UserTracker(CertmapdataMixin, KerberosAliasMixin, Tracker): +class UserTracker(PasskeyMixin, CertmapdataMixin, KerberosAliasMixin, + Tracker): """ Class for host plugin like tests """ retrieve_keys = { @@ -553,3 +555,10 @@ class UserTracker(CertmapdataMixin, KerberosAliasMixin, Tracker): def _make_remove_certmap(self): return self.make_command('user_remove_certmapdata', self.name) + + # Passkey mapping methods + def _make_add_passkey(self): + return self.make_command('user_add_passkey', self.name) + + def _make_remove_passkey(self): + return self.make_command('user_remove_passkey', self.name)