summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFlorence Blanc-Renaud <flo@redhat.com>2018-02-14 14:06:48 +0100
committerChristian Heimes <cheimes@redhat.com>2018-02-15 14:10:48 +0100
commitc701cd21d31e6bdf5f1078cdfca49e410e093e28 (patch)
tree37a51ccbbaf65801777de2971f3857ace69b7524
parent8b6506a5f1176ad768bb0e513436009906b8ff63 (diff)
downloadfreeipa-c701cd21d31e6bdf5f1078cdfca49e410e093e28.tar.gz
freeipa-c701cd21d31e6bdf5f1078cdfca49e410e093e28.tar.xz
freeipa-c701cd21d31e6bdf5f1078cdfca49e410e093e28.zip
389-ds OTP lasttoken plugin: Add unit test
Add a xmlrpc test checking that a user cannot delete his last OTP token. Related to https://pagure.io/freeipa/issue/7012 Reviewed-By: Nathaniel McCallum <npmccallum@redhat.com> Reviewed-By: Alexey Slaykovsky <alexey@slaykovsky.com>
-rw-r--r--ipatests/test_xmlrpc/test_otptoken_plugin.py173
1 files changed, 173 insertions, 0 deletions
diff --git a/ipatests/test_xmlrpc/test_otptoken_plugin.py b/ipatests/test_xmlrpc/test_otptoken_plugin.py
new file mode 100644
index 000000000..2a152464b
--- /dev/null
+++ b/ipatests/test_xmlrpc/test_otptoken_plugin.py
@@ -0,0 +1,173 @@
+#
+# Copyright (C) 2018 FreeIPA Contributors see COPYING for license
+#
+
+
+"""
+Test the otptoken plugin.
+"""
+
+from __future__ import print_function
+
+import pytest
+
+from ipalib import api, errors
+from ipatests.util import change_principal, unlock_principal_password
+from ipatests.test_xmlrpc.xmlrpc_test import XMLRPC_test
+from ipatests.test_xmlrpc.tracker.user_plugin import UserTracker
+
+
+user_password = u'userSecretPassword123'
+
+
+@pytest.fixture
+def user(request):
+ tracker = UserTracker(name=u'user_for_otp_test',
+ givenname=u'Test', sn=u'User for OTP')
+ return tracker.make_fixture(request)
+
+
+def id_function(arg):
+ """
+ Return a label for the test parameters.
+
+ The params can be:
+ - the global config (list containing ipauserauthtypes)
+ in this case we need to extract the 'disabled' auth type to evaluate
+ whether user setting override is allowed
+ Example: [u'disabled', u'otp'] will return a label noOverride-otp
+ [u'otp', u'password'] otp+password
+ - the user config (list containing ipauserauthtypes)
+ - the expected outcome (boolean True if delete should be allowed)
+ """
+
+ if isinstance(arg, list):
+ # The arg is a list, need to extract the override flag
+ labels = list()
+ if u'disabled' in arg:
+ labels.append('noOverride')
+
+ label = 'default'
+ if arg:
+ without_override = [item for item in arg if item != u'disabled']
+ if without_override:
+ label = '+'.join(without_override)
+ labels.append(label)
+
+ return "-".join(labels)
+
+ if isinstance(arg, bool):
+ return "allowed" if arg else "forbidden"
+
+ return 'default'
+
+
+class TestDeleteLastOtpToken(XMLRPC_test):
+
+ @pytest.mark.parametrize(
+ "globalCfg,userCfg,allowDelLast", [
+ # When Global config is not set and prevents user override,
+ # it is possible to delete last token
+ ([u'disabled'], None, True),
+ ([u'disabled'], [u'otp'], True),
+ ([u'disabled'], [u'password'], True),
+ ([u'disabled'], [u'password', u'otp'], True),
+ # When Global config is not set and allows user override,
+ # the userCfg applies
+ # Deletion is forbidden only when usercfg = otp only
+ (None, None, True),
+ (None, [u'otp'], False),
+ (None, [u'password'], True),
+ (None, [u'password', u'otp'], True),
+ # When Global config is set to otp and prevents user override,
+ # it is forbidden to delete last token
+ ([u'disabled', u'otp'], None, False),
+ ([u'disabled', u'otp'], [u'otp'], False),
+ ([u'disabled', u'otp'], [u'password'], False),
+ ([u'disabled', u'otp'], [u'password', u'otp'], False),
+ # When Global config is set to otp and allows user override,
+ # the userCfg applies
+ # Deletion is forbidden when usercfg = otp only or usercfg not set
+ ([u'otp'], None, False),
+ ([u'otp'], [u'otp'], False),
+ ([u'otp'], [u'password'], True),
+ ([u'otp'], [u'password', u'otp'], True),
+ # When Global config is set to password and prevents user override,
+ # it is possible to delete last token
+ ([u'disabled', u'password'], None, True),
+ ([u'disabled', u'password'], [u'otp'], True),
+ ([u'disabled', u'password'], [u'password'], True),
+ ([u'disabled', u'password'], [u'password', u'otp'], True),
+ # When Global config is set to password and allows user override,
+ # the userCfg applies
+ # Deletion is forbidden when usercfg = otp only
+ ([u'password'], None, True),
+ ([u'password'], [u'otp'], False),
+ ([u'password'], [u'password'], True),
+ ([u'password'], [u'password', u'otp'], True),
+ # When Global config is set to password+otp and prevents user
+ # override, it is possible to delete last token
+ ([u'disabled', u'password', u'otp'], None, True),
+ ([u'disabled', u'password', u'otp'], [u'otp'], True),
+ ([u'disabled', u'password', u'otp'], [u'password'], True),
+ ([u'disabled', u'password', u'otp'], [u'password', u'otp'], True),
+ # When Global config is set to password+otp and allows user
+ # override, the userCfg applies
+ # Deletion is forbidden when usercfg = otp only
+ ([u'password', u'otp'], None, True),
+ ([u'password', u'otp'], [u'otp'], False),
+ ([u'password', u'otp'], [u'password'], True),
+ ([u'password', u'otp'], [u'password', u'otp'], True),
+ ],
+ ids=id_function)
+ def test_delete(self, globalCfg, userCfg, allowDelLast, user):
+ """
+ Test the deletion of the last otp token
+
+ The user auth type can be defined at a global level, or
+ per-user if the override is not disabled.
+ Depending on the resulting setting, the deletion of last token
+ is allowed or forbidden.
+ """
+ # Save current global config
+ result = api.Command.config_show()
+ current_globalCfg = result.get('ipauserauthtype', None)
+
+ try:
+ # Set the global config for the test
+ api.Command.config_mod(ipauserauthtype=globalCfg)
+ except errors.EmptyModlist:
+ pass
+
+ try:
+ user.ensure_exists()
+ api.Command.user_mod(user.name, userpassword=user_password)
+ unlock_principal_password(user.name,
+ user_password, user_password)
+ # Set the user config for the test
+ api.Command.user_mod(user.name, ipauserauthtype=userCfg)
+
+ # Connect as user, create and delete the token
+ with change_principal(user.name, user_password):
+ api.Command.otptoken_add(u'lastotp', description=u'last otp',
+ ipatokenowner=user.name)
+ if allowDelLast:
+ # We are expecting the del command to succeed
+ api.Command.otptoken_del(u'lastotp')
+ else:
+ # We are expecting the del command to fail
+ with pytest.raises(errors.DatabaseError):
+ api.Command.otptoken_del(u'lastotp')
+
+ finally:
+ # Make sure the token is removed
+ try:
+ api.Command.otptoken_del(u'lastotp',)
+ except errors.NotFound:
+ pass
+
+ # Restore the previous ipauserauthtype
+ try:
+ api.Command.config_mod(ipauserauthtype=current_globalCfg)
+ except errors.EmptyModlist:
+ pass