mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
ipatests: add test for password policies
Primarily testing integration of libpwpolicy but it also exercises some of the existing policy. https://pagure.io/freeipa/issue/6964 https://pagure.io/freeipa/issue/5948 https://pagure.io/freeipa/issue/2445 https://pagure.io/freeipa/issue/298 Signed-off-by: Rob Crittenden <rcritten@redhat.com> Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com> Reviewed-By: Christian Heimes <cheimes@redhat.com>
This commit is contained in:
263
ipatests/test_integration/test_pwpolicy.py
Normal file
263
ipatests/test_integration/test_pwpolicy.py
Normal file
@@ -0,0 +1,263 @@
|
||||
#
|
||||
# Copyright (C) 2020 FreeIPA Contributors see COPYING for license
|
||||
#
|
||||
"""Misc test for 'ipa' CLI regressions
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
import pytest
|
||||
|
||||
from ipatests.test_integration.base import IntegrationTest
|
||||
|
||||
from ipatests.pytest_ipa.integration import tasks
|
||||
|
||||
USER = 'tuser'
|
||||
PASSWORD = 'Secret123'
|
||||
POLICY = 'test'
|
||||
|
||||
|
||||
class TestPWPolicy(IntegrationTest):
|
||||
"""
|
||||
Test password policy in action.
|
||||
"""
|
||||
|
||||
topology = 'line'
|
||||
|
||||
@classmethod
|
||||
def install(cls, mh):
|
||||
super(TestPWPolicy, cls).install(mh)
|
||||
|
||||
tasks.kinit_admin(cls.master)
|
||||
cls.master.run_command(['ipa', 'user-add', USER,
|
||||
'--first', 'Test',
|
||||
'--last', 'User'])
|
||||
cls.master.run_command(['ipa', 'group-add', POLICY])
|
||||
cls.master.run_command(['ipa', 'group-add-member', POLICY,
|
||||
'--users', USER])
|
||||
cls.master.run_command(['ipa', 'pwpolicy-add', POLICY,
|
||||
'--priority', '1'])
|
||||
cls.master.run_command(['ipa', 'passwd', USER],
|
||||
stdin_text='{password}\n{password}\n'.format(
|
||||
password=PASSWORD
|
||||
))
|
||||
|
||||
def kinit_as_user(self, host, old_password, new_password, user=USER,
|
||||
raiseonerr=True):
|
||||
"""kinit to an account with an expired password"""
|
||||
return host.run_command(
|
||||
['kinit', user],
|
||||
raiseonerr=raiseonerr,
|
||||
stdin_text='{old}\n{new}\n{new}\n'.format(
|
||||
old=old_password, new=new_password
|
||||
),
|
||||
)
|
||||
|
||||
def reset_password(self, host, user=USER, password=PASSWORD):
|
||||
tasks.kinit_admin(host)
|
||||
host.run_command(
|
||||
['ipa', 'passwd', user],
|
||||
stdin_text='{password}\n{password}\n'.format(password=password),
|
||||
)
|
||||
|
||||
def set_pwpolicy(self, minlength=None, maxrepeat=None, maxsequence=None,
|
||||
dictcheck=None, usercheck=None, minclasses=None):
|
||||
tasks.kinit_admin(self.master)
|
||||
args = ["ipa", "pwpolicy-mod", POLICY]
|
||||
if minlength is not None:
|
||||
args.append("--minlength={}".format(minlength))
|
||||
if maxrepeat is not None:
|
||||
args.append("--maxrepeat={}".format(maxrepeat))
|
||||
if maxsequence is not None:
|
||||
args.append("--maxsequence={}".format(maxsequence))
|
||||
if dictcheck is not None:
|
||||
args.append("--dictcheck={}".format(dictcheck))
|
||||
if usercheck is not None:
|
||||
args.append("--usercheck={}".format(usercheck))
|
||||
if minclasses is not None:
|
||||
args.append("--minclasses={}".format(minclasses))
|
||||
self.master.run_command(args)
|
||||
|
||||
self.reset_password(self.master)
|
||||
|
||||
def clean_pwpolicy(self):
|
||||
"""Set all policy values we care about to zero/false"""
|
||||
self.master.run_command(
|
||||
["ipa", "pwpolicy-mod", POLICY,
|
||||
"--maxrepeat", "0",
|
||||
"--maxsequence", "0",
|
||||
"--usercheck", "false",
|
||||
"--dictcheck" ,"false",
|
||||
"--minlife", "0",
|
||||
"--minlength", "0",
|
||||
"--minclasses", "0",],
|
||||
)
|
||||
|
||||
@pytest.fixture
|
||||
def reset_pwpolicy(self):
|
||||
"""Fixture to ensure policy is reset between tests"""
|
||||
yield
|
||||
tasks.kinit_admin(self.master)
|
||||
self.clean_pwpolicy()
|
||||
|
||||
def test_maxrepeat(self, reset_pwpolicy):
|
||||
self.set_pwpolicy(maxrepeat=2)
|
||||
# good passwords
|
||||
for password in ('Secret123', 'Password'):
|
||||
self.reset_password(self.master)
|
||||
self.kinit_as_user(self.master, PASSWORD, password)
|
||||
self.reset_password(self.master)
|
||||
tasks.ldappasswd_user_change(USER, PASSWORD, password, self.master)
|
||||
|
||||
self.reset_password(self.master)
|
||||
|
||||
# bad passwords
|
||||
for password in ('Secret1111', 'passsword'):
|
||||
result = self.kinit_as_user(self.master, PASSWORD, password,
|
||||
raiseonerr=False)
|
||||
assert result.returncode == 1
|
||||
result = tasks.ldappasswd_user_change(USER, PASSWORD, password,
|
||||
self.master,
|
||||
raiseonerr=False)
|
||||
assert result.returncode == 1
|
||||
assert 'Password has too many consecutive characters' in \
|
||||
result.stdout_text
|
||||
|
||||
def test_maxsequence(self, reset_pwpolicy):
|
||||
self.set_pwpolicy(maxsequence=3)
|
||||
# good passwords
|
||||
for password in ('Password123', 'Passwordabc'):
|
||||
self.reset_password(self.master)
|
||||
self.kinit_as_user(self.master, PASSWORD, password)
|
||||
self.reset_password(self.master)
|
||||
tasks.ldappasswd_user_change(USER, PASSWORD, password, self.master)
|
||||
|
||||
self.reset_password(self.master)
|
||||
|
||||
# bad passwords
|
||||
for password in ('Password1234', 'Passwordabcde'):
|
||||
result = self.kinit_as_user(self.master, PASSWORD, password,
|
||||
raiseonerr=False)
|
||||
assert result.returncode == 1
|
||||
result = tasks.ldappasswd_user_change(USER, PASSWORD, password,
|
||||
self.master,
|
||||
raiseonerr=False)
|
||||
assert result.returncode == 1
|
||||
assert 'Password contains a monotonic sequence' in \
|
||||
result.stdout_text
|
||||
|
||||
def test_usercheck(self, reset_pwpolicy):
|
||||
self.set_pwpolicy(usercheck=True)
|
||||
for password in ('tuserpass', 'passoftuser'):
|
||||
result = self.kinit_as_user(self.master, PASSWORD, password,
|
||||
raiseonerr=False)
|
||||
assert result.returncode == 1
|
||||
result = tasks.ldappasswd_user_change(USER, PASSWORD, password,
|
||||
self.master,
|
||||
raiseonerr=False)
|
||||
assert result.returncode == 1
|
||||
assert 'Password contains username' in \
|
||||
result.stdout_text
|
||||
|
||||
# test with valid password
|
||||
self.kinit_as_user(self.master, PASSWORD, 'bamOncyftAv0')
|
||||
|
||||
def test_dictcheck(self, reset_pwpolicy):
|
||||
self.set_pwpolicy(dictcheck=True)
|
||||
for password in ('password', 'bookends', 'BaLtim0re'):
|
||||
result = self.kinit_as_user(self.master, PASSWORD, password,
|
||||
raiseonerr=False)
|
||||
assert result.returncode == 1
|
||||
result = tasks.ldappasswd_user_change(USER, PASSWORD, password,
|
||||
self.master,
|
||||
raiseonerr=False)
|
||||
assert result.returncode == 1
|
||||
assert 'Password is based on a dictionary word' in \
|
||||
result.stdout_text
|
||||
|
||||
# test with valid password
|
||||
self.kinit_as_user(self.master, PASSWORD, 'bamOncyftAv0')
|
||||
|
||||
def test_minclasses(self, reset_pwpolicy):
|
||||
self.set_pwpolicy(minclasses=2)
|
||||
for password in ('password', 'bookends'):
|
||||
result = self.kinit_as_user(self.master, PASSWORD, password,
|
||||
raiseonerr=False)
|
||||
assert result.returncode == 1
|
||||
assert 'Password does not contain enough character' in \
|
||||
result.stdout_text
|
||||
result = tasks.ldappasswd_user_change(USER, PASSWORD, password,
|
||||
self.master,
|
||||
raiseonerr=False)
|
||||
assert result.returncode == 1
|
||||
assert 'Password is too simple' in \
|
||||
result.stdout_text
|
||||
|
||||
# test with valid password
|
||||
for valid in ('Password', 'password1', 'password!'):
|
||||
self.kinit_as_user(self.master, PASSWORD, valid)
|
||||
self.reset_password(self.master)
|
||||
|
||||
self.set_pwpolicy(minclasses=3)
|
||||
for password in ('password1', 'Bookends'):
|
||||
result = self.kinit_as_user(self.master, PASSWORD, password,
|
||||
raiseonerr=False)
|
||||
assert result.returncode == 1
|
||||
assert 'Password does not contain enough character' in \
|
||||
result.stdout_text
|
||||
result = tasks.ldappasswd_user_change(USER, PASSWORD, password,
|
||||
self.master,
|
||||
raiseonerr=False)
|
||||
assert result.returncode == 1
|
||||
assert 'Password is too simple' in \
|
||||
result.stdout_text
|
||||
|
||||
# test with valid password
|
||||
for valid in ('Passw0rd', 'password1!', 'Password!'):
|
||||
self.kinit_as_user(self.master, PASSWORD, valid)
|
||||
self.reset_password(self.master)
|
||||
|
||||
def test_minlength_mod(self, reset_pwpolicy):
|
||||
"""Test that the pwpolicy minlength overrides our policy
|
||||
"""
|
||||
|
||||
# With a minlength of 4 all settings of pwq should fail
|
||||
self.master.run_command(
|
||||
["ipa", "pwpolicy-mod", POLICY,
|
||||
"--minlength", "4",]
|
||||
)
|
||||
for values in (('--maxrepeat', '4'),
|
||||
('--maxsequence', '4'),
|
||||
('--dictcheck', 'true'),
|
||||
('--usercheck', 'true')):
|
||||
args = ["ipa", "pwpolicy-mod", POLICY]
|
||||
args.extend(values)
|
||||
result = self.master.run_command(args, raiseonerr=False)
|
||||
assert result.returncode != 0
|
||||
assert 'minlength' in result.stderr_text
|
||||
|
||||
# With any pwq value set, setting minlife < 6 should fail
|
||||
for values in (('--maxrepeat', '4'),
|
||||
('--maxsequence', '4'),
|
||||
('--dictcheck', 'true'),
|
||||
('--usercheck', 'true')):
|
||||
self.clean_pwpolicy()
|
||||
args = ["ipa", "pwpolicy-mod", POLICY]
|
||||
args.extend(values)
|
||||
self.master.run_command(args)
|
||||
result = self.master.run_command(
|
||||
["ipa", "pwpolicy-mod", POLICY,
|
||||
"--minlength", "4",], raiseonerr=False
|
||||
)
|
||||
assert result.returncode != 0
|
||||
assert 'minlength' in result.stderr_text
|
||||
|
||||
def test_minlength_add(self):
|
||||
"""Test that adding a new policy with minlength is caught.
|
||||
"""
|
||||
result = self.master.run_command(
|
||||
["ipa", "pwpolicy-add", "test_add",
|
||||
"--maxrepeat", "4", "--minlength", "4", "--priority", "2"],
|
||||
raiseonerr=False
|
||||
)
|
||||
assert result.returncode != 0
|
||||
assert 'minlength' in result.stderr_text
|
||||
Reference in New Issue
Block a user