Imported Debian patch 4.8.10-2
This commit is contained in:
committed by
Mario Fetka
parent
8bc559c5a1
commit
358acdd85f
@@ -2,6 +2,8 @@
|
||||
# Copyright (C) 2018 FreeIPA Contributors see COPYING for license
|
||||
#
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from cryptography import x509
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives import serialization, hashes
|
||||
@@ -9,19 +11,63 @@ from cryptography.hazmat.primitives.asymmetric import rsa
|
||||
from cryptography.x509.oid import NameOID
|
||||
|
||||
|
||||
def generate_csr(hostname):
|
||||
def generate_csr(cn, is_hostname=True):
|
||||
"""
|
||||
Generate certificate signing request
|
||||
|
||||
:param cn: common name (str|unicode)
|
||||
:param is_hostname: is the common name a hostname (default: True)
|
||||
"""
|
||||
key = rsa.generate_private_key(
|
||||
public_exponent=65537,
|
||||
key_size=2048,
|
||||
backend=default_backend()
|
||||
)
|
||||
hostname = u'{}'.format(hostname)
|
||||
if isinstance(cn, bytes):
|
||||
cn = cn.decode()
|
||||
csr = x509.CertificateSigningRequestBuilder()
|
||||
csr = csr.subject_name(
|
||||
x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, hostname)])
|
||||
x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, cn)])
|
||||
)
|
||||
if is_hostname:
|
||||
csr = csr.add_extension(
|
||||
x509.SubjectAlternativeName([x509.DNSName(cn)]),
|
||||
critical=False
|
||||
)
|
||||
|
||||
csr = csr.sign(key, hashes.SHA256(), default_backend())
|
||||
return csr.public_bytes(serialization.Encoding.PEM).decode()
|
||||
|
||||
|
||||
def generate_certificate(hostname):
|
||||
"""
|
||||
Generate self-signed certificate for some DNS name.
|
||||
The certificate is valid for 100 days from moment of generation.
|
||||
|
||||
:param hostname: DNS name (str|unicode)
|
||||
"""
|
||||
key = rsa.generate_private_key(
|
||||
public_exponent=65537,
|
||||
key_size=2048,
|
||||
backend=default_backend()
|
||||
)
|
||||
if isinstance(hostname, bytes):
|
||||
hostname = hostname.decode()
|
||||
subject = issuer = x509.Name(
|
||||
[x509.NameAttribute(NameOID.COMMON_NAME, hostname)]
|
||||
)
|
||||
|
||||
cert = x509.CertificateBuilder()
|
||||
cert = cert.subject_name(subject).issuer_name(issuer).public_key(
|
||||
key.public_key()
|
||||
).serial_number(
|
||||
x509.random_serial_number()
|
||||
).not_valid_before(
|
||||
datetime.utcnow()
|
||||
).not_valid_after(
|
||||
datetime.utcnow() + timedelta(days=100)
|
||||
).add_extension(
|
||||
x509.SubjectAlternativeName([x509.DNSName(hostname)]),
|
||||
critical=False
|
||||
)
|
||||
csr = csr.sign(key, hashes.SHA256(), default_backend())
|
||||
return csr.public_bytes(serialization.Encoding.PEM).decode()
|
||||
).sign(key, hashes.SHA256(), default_backend())
|
||||
return cert.public_bytes(serialization.Encoding.PEM).decode()
|
||||
|
||||
@@ -31,7 +31,7 @@ FILLED_LOGIN_FORM = {
|
||||
('username', 'Username', True, True, 'text', 'username',
|
||||
PKEY, 'Username'),
|
||||
('password', 'Password', True, True, 'password', 'password',
|
||||
PASSWD_ITEST_USER, 'Password or Password+One-Time-Password'),
|
||||
PASSWD_ITEST_USER, 'Password or Password+One-Time Password'),
|
||||
],
|
||||
# structure of buttons
|
||||
# button_name, button_title
|
||||
@@ -58,12 +58,12 @@ RESET_AND_LOGIN_FORM = {
|
||||
PKEY, None),
|
||||
('current_password', 'Current Password', False, True, 'password',
|
||||
'current_password', '', 'Current Password'),
|
||||
('otp', 'OTP', False, True, 'password', 'otp', '',
|
||||
'One-Time-Password'),
|
||||
('new_password', 'New Password', True, True, 'password',
|
||||
'new_password', '', 'New Password'),
|
||||
('verify_password', 'Verify Password', True, True, 'password',
|
||||
'verify_password', '', 'New Password'),
|
||||
('otp', 'OTP', False, True, 'password', 'otp', '',
|
||||
'One-Time Password'),
|
||||
],
|
||||
# structure of buttons
|
||||
# button_name, button_title
|
||||
@@ -89,12 +89,12 @@ RESET_PASSWORD_FORM = {
|
||||
'Username'),
|
||||
('current_password', 'Current Password', True, True, 'password',
|
||||
'current_password', '', 'Current Password'),
|
||||
('otp', 'OTP', False, True, 'password', 'otp', '',
|
||||
'One-Time-Password'),
|
||||
('new_password', 'New Password', True, True, 'password',
|
||||
'new_password', '', 'New Password'),
|
||||
('verify_password', 'Verify Password', True, True, 'password',
|
||||
'verify_password', '', 'New Password'),
|
||||
('otp', 'OTP', False, True, 'password', 'otp', '',
|
||||
'One-Time Password'),
|
||||
],
|
||||
# structure of buttons
|
||||
# button_name, button_title
|
||||
@@ -121,7 +121,7 @@ EMPTY_LOGIN_FORM = {
|
||||
('username', 'Username', False, True, 'text', 'username', '',
|
||||
'Username'),
|
||||
('password', 'Password', False, True, 'password', 'password', '',
|
||||
'Password or Password+One-Time-Password'),
|
||||
'Password or Password+One-Time Password'),
|
||||
],
|
||||
# structure of buttons
|
||||
# button_name, button_title
|
||||
@@ -146,7 +146,7 @@ LOGIN_FORM = {
|
||||
('username', 'Username', True, True, 'text', 'username', PKEY,
|
||||
'Username'),
|
||||
('password', 'Password', True, True, 'password', 'password', '',
|
||||
'Password or Password+One-Time-Password'),
|
||||
'Password or Password+One-Time Password'),
|
||||
],
|
||||
# structure of buttons
|
||||
# button_name, button_title
|
||||
|
||||
@@ -2,6 +2,19 @@
|
||||
# Copyright (C) 2018 FreeIPA Contributors see COPYING for license
|
||||
#
|
||||
|
||||
from ipaplatform.constants import constants as platformconstants
|
||||
|
||||
# for example, user_u:s0
|
||||
selinuxuser1 = platformconstants.SELINUX_USERMAP_ORDER.split("$")[0]
|
||||
selinuxuser2 = platformconstants.SELINUX_USERMAP_ORDER.split("$")[1]
|
||||
|
||||
selinux_mcs_max = platformconstants.SELINUX_MCS_MAX
|
||||
selinux_mls_max = platformconstants.SELINUX_MLS_MAX
|
||||
|
||||
second_mls_level = 's{}'.format(list(range(0, selinux_mls_max + 1))[0])
|
||||
second_mcs_level = 'c{}'.format(list(range(0, selinux_mcs_max + 1))[0])
|
||||
mcs_range = '{0}.{0}'.format(second_mcs_level)
|
||||
|
||||
ENTITY = 'selinuxusermap'
|
||||
|
||||
PKEY = 'itest-selinuxusermap'
|
||||
@@ -9,7 +22,7 @@ DATA = {
|
||||
'pkey': PKEY,
|
||||
'add': [
|
||||
('textbox', 'cn', PKEY),
|
||||
('textbox', 'ipaselinuxuser', 'user_u:s0'),
|
||||
('textbox', 'ipaselinuxuser', selinuxuser1),
|
||||
],
|
||||
'mod': [
|
||||
('textarea', 'description', 'itest-selinuxusermap desc'),
|
||||
@@ -21,7 +34,7 @@ DATA2 = {
|
||||
'pkey': PKEY2,
|
||||
'add': [
|
||||
('textbox', 'cn', PKEY2),
|
||||
('textbox', 'ipaselinuxuser', 'unconfined_u:s0-s0:c0.c1023'),
|
||||
('textbox', 'ipaselinuxuser', selinuxuser2),
|
||||
],
|
||||
'mod': [
|
||||
('textarea', 'description', 'itest-selinuxusermap desc2'),
|
||||
@@ -33,7 +46,7 @@ DATA_MLS_RANGE = {
|
||||
'pkey': PKEY_MLS_RANGE,
|
||||
'add': [
|
||||
('textbox', 'cn', PKEY_MLS_RANGE),
|
||||
('textbox', 'ipaselinuxuser', 'user_u:s0-s1'),
|
||||
('textbox', 'ipaselinuxuser', 'foo:s0-{}'.format(second_mls_level)),
|
||||
],
|
||||
}
|
||||
|
||||
@@ -42,7 +55,9 @@ DATA_MCS_RANGE = {
|
||||
'pkey': PKEY_MCS_RANGE,
|
||||
'add': [
|
||||
('textbox', 'cn', PKEY_MCS_RANGE),
|
||||
('textbox', 'ipaselinuxuser', 'user_u:s0-s15:c0.c1023'),
|
||||
('textbox', 'ipaselinuxuser',
|
||||
'foo:s0-s{}:c0.c{}'.format(selinux_mls_max, selinux_mcs_max)
|
||||
),
|
||||
],
|
||||
}
|
||||
|
||||
@@ -51,7 +66,10 @@ DATA_MCS_COMMAS = {
|
||||
'pkey': PKEY_MCS_COMMAS,
|
||||
'add': [
|
||||
('textbox', 'cn', PKEY_MCS_COMMAS),
|
||||
('textbox', 'ipaselinuxuser', 'user_u:s0-s1:c0,c2,c15.c26'),
|
||||
('textbox', 'ipaselinuxuser',
|
||||
'foo:s0-{}:c0,{},{}'.format(
|
||||
second_mls_level, second_mcs_level, mcs_range),
|
||||
),
|
||||
],
|
||||
}
|
||||
|
||||
@@ -60,7 +78,9 @@ DATA_MLS_SINGLE_VAL = {
|
||||
'pkey': PKEY_MLS_SINGLE_VAL,
|
||||
'add': [
|
||||
('textbox', 'cn', PKEY_MLS_SINGLE_VAL),
|
||||
('textbox', 'ipaselinuxuser', 'user_u:s0-s0:c0.c1023'),
|
||||
('textbox', 'ipaselinuxuser',
|
||||
'foo:s0-s0:c0.c{}'.format(selinux_mcs_max)
|
||||
),
|
||||
],
|
||||
}
|
||||
|
||||
@@ -69,7 +89,7 @@ DATA_NON_EXIST_SEUSER = {
|
||||
'pkey': PKEY_NON_EXIST_SEUSER,
|
||||
'add': [
|
||||
('textbox', 'cn', PKEY_NON_EXIST_SEUSER),
|
||||
('textbox', 'ipaselinuxuser', 'abc:s0'),
|
||||
('textbox', 'ipaselinuxuser', 'foo:s0'),
|
||||
],
|
||||
}
|
||||
|
||||
@@ -78,7 +98,7 @@ DATA_INVALID_MCS = {
|
||||
'pkey': PKEY_INVALID_MCS,
|
||||
'add': [
|
||||
('textbox', 'cn', PKEY_INVALID_MCS),
|
||||
('textbox', 'ipaselinuxuser', 'user:s0:c'),
|
||||
('textbox', 'ipaselinuxuser', 'foo:s0:c'),
|
||||
],
|
||||
}
|
||||
|
||||
@@ -87,7 +107,7 @@ DATA_INVALID_MLS = {
|
||||
'pkey': PKEY_INVALID_MLS,
|
||||
'add': [
|
||||
('textbox', 'cn', PKEY_INVALID_MLS),
|
||||
('textbox', 'ipaselinuxuser', 'user'),
|
||||
('textbox', 'ipaselinuxuser', 'foo'),
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@@ -208,6 +208,19 @@ DATA_NO_LOGIN = {
|
||||
]
|
||||
}
|
||||
|
||||
PKEY_MEMBER_MANAGER = 'member-manager'
|
||||
PASSWD_MEMBER_MANAGER = 'Password123'
|
||||
DATA_MEMBER_MANAGER = {
|
||||
'pkey': PKEY_MEMBER_MANAGER,
|
||||
'add': [
|
||||
('textbox', 'uid', PKEY_MEMBER_MANAGER),
|
||||
('textbox', 'givenname', 'Name'),
|
||||
('textbox', 'sn', 'Surname'),
|
||||
('password', 'userpassword', PASSWD_MEMBER_MANAGER),
|
||||
('password', 'userpassword2', PASSWD_MEMBER_MANAGER),
|
||||
],
|
||||
}
|
||||
|
||||
SSH_RSA = (
|
||||
'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDBVmLXpTDhrYkABOPlADFk'
|
||||
'GV8/QfgQqUQ0xn29hk18t/NTEQOW/Daq4EF84e9aTiopRXIk7jahBLzwWTZI'
|
||||
|
||||
@@ -21,16 +21,44 @@
|
||||
Range tasks
|
||||
"""
|
||||
|
||||
import uuid
|
||||
|
||||
from ipatests.test_webui.ui_driver import UI_driver
|
||||
|
||||
LOCAL_ID_RANGE = 'ipa-local'
|
||||
TRUSTED_ID_RANGE = 'ipa-ad-trust'
|
||||
|
||||
|
||||
class range_tasks(UI_driver):
|
||||
|
||||
def get_shifts(self, idranges=None):
|
||||
BASE_RANGE_OVERLAPS_ERROR = (
|
||||
"Constraint violation: "
|
||||
"New base range overlaps with existing base range."
|
||||
)
|
||||
|
||||
if not idranges:
|
||||
result = self.execute_api_from_ui('idrange_find', [], {})
|
||||
idranges = result['result']['result']
|
||||
PRIMARY_RID_RANGE_OVERLAPS_ERROR = (
|
||||
"Constraint violation: "
|
||||
"New primary rid range overlaps with existing primary rid range."
|
||||
)
|
||||
|
||||
SECONDARY_RID_RANGE_OVERLAPS_ERROR = (
|
||||
"Constraint violation: "
|
||||
"New secondary rid range overlaps with existing secondary rid range."
|
||||
)
|
||||
|
||||
PRIMARY_AND_SECONDARY_RID_OVERLAP_ERROR = (
|
||||
"invalid 'ID Range setup': "
|
||||
"Primary RID range and secondary RID range cannot overlap"
|
||||
)
|
||||
|
||||
DELETE_PRIMARY_LOCAL_RANGE_ERROR = (
|
||||
"invalid 'ipabaseid,ipaidrangesize': range modification "
|
||||
"leaving objects with ID out of the defined range is not allowed"
|
||||
)
|
||||
|
||||
def get_shifts(self):
|
||||
result = self.execute_api_from_ui('idrange_find', [], {})
|
||||
idranges = result['result']['result']
|
||||
|
||||
max_id = 0
|
||||
max_rid = 0
|
||||
@@ -67,50 +95,141 @@ class range_tasks(UI_driver):
|
||||
domain = trusts[0]['cn']
|
||||
return domain
|
||||
|
||||
def get_data(self, pkey, size=50, add_data=None):
|
||||
def get_data(self, pkey=None, form_data=None, **kwargs):
|
||||
|
||||
if not add_data:
|
||||
add_data = self.get_add_data(pkey, size=size)
|
||||
if not pkey:
|
||||
pkey = 'itest-range-{}'.format(uuid.uuid4().hex[:8])
|
||||
|
||||
if form_data:
|
||||
form_data.cn = pkey
|
||||
else:
|
||||
form_data = self.get_add_form_data(pkey, **kwargs)
|
||||
|
||||
data = {
|
||||
'pkey': pkey,
|
||||
'add': add_data,
|
||||
'add': form_data.serialize(),
|
||||
'mod': [
|
||||
('textbox', 'ipaidrangesize', str(size + 1)),
|
||||
('textbox', 'ipaidrangesize', str(form_data.size + 1)),
|
||||
],
|
||||
}
|
||||
return data
|
||||
|
||||
def get_add_data(self, pkey, range_type='ipa-local', size=50, shift=100, domain=None):
|
||||
def get_add_form_data(self, pkey, range_type=LOCAL_ID_RANGE, size=50,
|
||||
domain=None, **kwargs):
|
||||
"""
|
||||
Generate RangeAddFormData instance with initial data based on existing
|
||||
ID ranges.
|
||||
:param kwargs: overrides default fields values (base_id, base_rid,
|
||||
secondary_base_rid)
|
||||
"""
|
||||
|
||||
base_id = self.max_id + shift
|
||||
shift = 100
|
||||
base_id = kwargs.get('base_id', self.max_id + shift)
|
||||
self.max_id = base_id + size
|
||||
|
||||
base_rid = self.max_rid + shift
|
||||
self.max_rid = base_rid + size
|
||||
|
||||
add = [
|
||||
('textbox', 'cn', pkey),
|
||||
('textbox', 'ipabaseid', str(base_id)),
|
||||
('textbox', 'ipaidrangesize', str(size)),
|
||||
('textbox', 'ipabaserid', str(base_rid)),
|
||||
('radio', 'iparangetype', range_type),
|
||||
('callback', self.check_range_type_mod, range_type)
|
||||
]
|
||||
|
||||
if not domain:
|
||||
if 'base_rid' in kwargs:
|
||||
base_rid = kwargs['base_rid']
|
||||
else:
|
||||
base_rid = self.max_rid + shift
|
||||
self.max_rid = base_rid + size
|
||||
add.append(('textbox', 'ipasecondarybaserid', str(base_rid)))
|
||||
if domain:
|
||||
add.append(('textbox', 'ipanttrusteddomainname', domain))
|
||||
|
||||
return add
|
||||
secondary_base_rid = None
|
||||
if not domain:
|
||||
if 'secondary_base_rid' in kwargs:
|
||||
secondary_base_rid = kwargs['secondary_base_rid']
|
||||
else:
|
||||
secondary_base_rid = self.max_rid + shift
|
||||
self.max_rid = secondary_base_rid + size
|
||||
|
||||
return RangeAddFormData(
|
||||
pkey, base_id, base_rid,
|
||||
secondary_base_rid=secondary_base_rid,
|
||||
range_type=range_type,
|
||||
size=size,
|
||||
domain=domain,
|
||||
callback=self.check_range_type_mod
|
||||
)
|
||||
|
||||
def get_mod_form_data(self, **kwargs):
|
||||
return RangeModifyFormData(**kwargs)
|
||||
|
||||
def check_range_type_mod(self, range_type):
|
||||
if range_type == 'ipa-local':
|
||||
if range_type == LOCAL_ID_RANGE:
|
||||
self.assert_disabled("[name=ipanttrusteddomainname]")
|
||||
self.assert_disabled("[name=ipasecondarybaserid]", negative=True)
|
||||
elif range_type == 'ipa-ad-trust':
|
||||
self.assert_disabled("[name=ipanttrusteddomainname]", negative=True)
|
||||
elif range_type == TRUSTED_ID_RANGE:
|
||||
self.assert_disabled("[name=ipanttrusteddomainname]",
|
||||
negative=True)
|
||||
self.assert_disabled("[name=ipasecondarybaserid]")
|
||||
|
||||
|
||||
class RangeAddFormData:
|
||||
"""
|
||||
Class for storing and serializing of new ID Range form data.
|
||||
|
||||
Warning: Only for data transformation.
|
||||
Do not put any additional logic here!
|
||||
"""
|
||||
|
||||
def __init__(self, cn, base_id, base_rid=None, secondary_base_rid=None,
|
||||
range_type=LOCAL_ID_RANGE, size=50, domain=None,
|
||||
callback=None):
|
||||
self.cn = cn
|
||||
self.base_id = base_id
|
||||
self.base_rid = base_rid
|
||||
self.secondary_base_rid = secondary_base_rid
|
||||
self.range_type = range_type
|
||||
self.size = size
|
||||
self.domain = domain
|
||||
self.callback = callback
|
||||
|
||||
def serialize(self):
|
||||
|
||||
serialized = [
|
||||
('textbox', 'cn', self.cn),
|
||||
('textbox', 'ipabaseid', str(self.base_id)),
|
||||
('textbox', 'ipaidrangesize', str(self.size)),
|
||||
('textbox', 'ipabaserid', str(self.base_rid)),
|
||||
('radio', 'iparangetype', self.range_type),
|
||||
('callback', self.callback, self.range_type),
|
||||
]
|
||||
|
||||
if self.domain:
|
||||
serialized.append(('textbox',
|
||||
'ipanttrusteddomainname',
|
||||
self.domain))
|
||||
else:
|
||||
serialized.append(('textbox',
|
||||
'ipasecondarybaserid',
|
||||
str(self.secondary_base_rid)))
|
||||
|
||||
return serialized
|
||||
|
||||
|
||||
class RangeModifyFormData:
|
||||
"""
|
||||
Class for storing and serializing of modified ID Range form data.
|
||||
"""
|
||||
|
||||
def __init__(self, base_id=None, base_rid=None, secondary_base_rid=None,
|
||||
size=None):
|
||||
self.base_id = base_id
|
||||
self.base_rid = base_rid
|
||||
self.secondary_base_rid = secondary_base_rid
|
||||
self.size = size
|
||||
|
||||
def serialize(self):
|
||||
serialized = []
|
||||
|
||||
if self.base_id is not None:
|
||||
serialized.append(('textbox', 'ipabaseid', str(self.base_id)))
|
||||
if self.size is not None:
|
||||
serialized.append(('textbox', 'ipaidrangesize', str(self.size)))
|
||||
if self.base_rid is not None:
|
||||
serialized.append(('textbox', 'ipabaserid', str(self.base_rid)))
|
||||
if self.secondary_base_rid is not None:
|
||||
serialized.append(('textbox',
|
||||
'ipasecondarybaserid',
|
||||
str(self.secondary_base_rid)))
|
||||
|
||||
return serialized
|
||||
|
||||
@@ -21,8 +21,7 @@
|
||||
Automember tests
|
||||
"""
|
||||
|
||||
from ipatests.test_webui.ui_driver import UI_driver
|
||||
from ipatests.test_webui.ui_driver import screenshot
|
||||
from ipatests.test_webui.ui_driver import UI_driver, screenshot
|
||||
import ipatests.test_webui.data_hostgroup as hostgroup
|
||||
from ipatests.test_webui.test_host import host_tasks
|
||||
import pytest
|
||||
@@ -50,16 +49,180 @@ HOST_GROUP_DATA = {
|
||||
],
|
||||
}
|
||||
|
||||
SEARCH_CASES = {
|
||||
'name': 'search-123',
|
||||
'description': 'Short description !@#$%^&*()',
|
||||
'positive': [
|
||||
'search-123',
|
||||
'search',
|
||||
'search ',
|
||||
' search',
|
||||
'SEARCH',
|
||||
'123',
|
||||
'!@#$%^&*()',
|
||||
'hort descr',
|
||||
'description !',
|
||||
],
|
||||
'negative': [
|
||||
'searc-123',
|
||||
'321',
|
||||
'search 123',
|
||||
'search Short',
|
||||
'description!',
|
||||
],
|
||||
}
|
||||
|
||||
# Condition types
|
||||
INCLUSIVE = 'inclusive'
|
||||
EXCLUSIVE = 'exclusive'
|
||||
|
||||
|
||||
@pytest.mark.tier1
|
||||
class test_automember(UI_driver):
|
||||
class TestAutomember(UI_driver):
|
||||
|
||||
AUTOMEMBER_RULE_EXISTS_ERROR = (
|
||||
'Automember rule with name "{}" already exists'
|
||||
)
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def automember_setup(self, ui_driver_fsetup):
|
||||
self.init_app()
|
||||
|
||||
def add_user_group_rules(self, *pkeys, **kwargs):
|
||||
# We implicitly trigger "Add and Add Another" by passing multiple
|
||||
# records to add_record method.
|
||||
# TODO: Create more transparent mechanism to test "Add <entity>" dialog
|
||||
self.add_record(
|
||||
ENTITY,
|
||||
[{
|
||||
'pkey': pkey,
|
||||
'add': [('combobox', 'cn', pkey)],
|
||||
} for pkey in pkeys],
|
||||
facet='searchgroup',
|
||||
**kwargs
|
||||
)
|
||||
|
||||
def add_host_group_rules(self, *pkeys, **kwargs):
|
||||
self.add_record(
|
||||
ENTITY,
|
||||
[{
|
||||
'pkey': pkey,
|
||||
'add': [('combobox', 'cn', pkey)],
|
||||
} for pkey in pkeys],
|
||||
facet='searchhostgroup',
|
||||
**kwargs
|
||||
)
|
||||
|
||||
def add_user(self, pkey, name, surname):
|
||||
self.add_record('user', {
|
||||
'pkey': pkey,
|
||||
'add': [
|
||||
('textbox', 'uid', pkey),
|
||||
('textbox', 'givenname', name),
|
||||
('textbox', 'sn', surname),
|
||||
]
|
||||
})
|
||||
|
||||
def add_user_group(self, pkey, description=''):
|
||||
self.add_record('group', {
|
||||
'pkey': pkey,
|
||||
'add': [
|
||||
('textbox', 'cn', pkey),
|
||||
('textarea', 'description', description),
|
||||
]
|
||||
})
|
||||
|
||||
def add_host_group(self, pkey, description=''):
|
||||
self.add_record('hostgroup', {
|
||||
'pkey': pkey,
|
||||
'add': [
|
||||
('textbox', 'cn', pkey),
|
||||
('textarea', 'description', description),
|
||||
]
|
||||
})
|
||||
|
||||
def delete_users(self, *pkeys):
|
||||
self.delete('user', [{'pkey': pkey} for pkey in pkeys])
|
||||
|
||||
def delete_user_groups(self, *pkeys):
|
||||
self.delete('group', [{'pkey': pkey} for pkey in pkeys])
|
||||
|
||||
def delete_user_group_rules(self, *pkeys):
|
||||
self.delete(ENTITY, [{'pkey': pkey} for pkey in pkeys],
|
||||
facet='searchgroup')
|
||||
|
||||
def delete_host_groups(self, *pkeys):
|
||||
self.delete('hostgroup', [{'pkey': pkey} for pkey in pkeys])
|
||||
|
||||
def delete_host_group_rules(self, *pkeys):
|
||||
self.delete(ENTITY, [{'pkey': pkey} for pkey in pkeys],
|
||||
facet='searchhostgroup')
|
||||
|
||||
def add_conditions(self, conditions, condition_type):
|
||||
"""
|
||||
Add conditions to a rule
|
||||
|
||||
:param conditions: list of conditions where condition is a pair
|
||||
(attribute, expression)
|
||||
:param condition_type: can be 'inclusive' or 'exclusive'
|
||||
"""
|
||||
|
||||
name = 'automember{}regex'.format(condition_type)
|
||||
|
||||
attribute, expression = conditions[0]
|
||||
another_conditions = conditions[1:]
|
||||
another_conditions.reverse()
|
||||
|
||||
self.add_table_record(name, {'fields': [
|
||||
('selectbox', 'key', attribute),
|
||||
('textbox', name, expression)
|
||||
]}, add_another=bool(another_conditions))
|
||||
|
||||
while another_conditions:
|
||||
attribute, expression = another_conditions.pop()
|
||||
self.add_another_table_record(
|
||||
{'fields': [
|
||||
('selectbox', 'key', attribute),
|
||||
('textbox', name, expression)
|
||||
]},
|
||||
add_another=bool(another_conditions)
|
||||
)
|
||||
|
||||
def delete_conditions(self, conditions, condition_type):
|
||||
"""
|
||||
Delete rule conditions
|
||||
|
||||
:param conditions: list of conditions where condition is a pair
|
||||
(attribute, expression)
|
||||
:param condition_type: can be 'inclusive' or 'exclusive'
|
||||
"""
|
||||
|
||||
self.delete_record(
|
||||
['{}={}'.format(attr, exp) for attr, exp in conditions],
|
||||
parent=self.get_facet(),
|
||||
table_name='automember{}regex'.format(condition_type)
|
||||
)
|
||||
|
||||
def open_new_condition_dialog(self, condition_type):
|
||||
table = self.find_by_selector(
|
||||
"table[name='automember{}regex'].table".format(condition_type),
|
||||
strict=True
|
||||
)
|
||||
btn = self.find_by_selector(".btn[name=add]", table, strict=True)
|
||||
btn.click()
|
||||
self.wait()
|
||||
|
||||
def get_host_util(self):
|
||||
host_util = host_tasks()
|
||||
host_util.driver = self.driver
|
||||
host_util.config = self.config
|
||||
return host_util
|
||||
|
||||
@screenshot
|
||||
def test_crud(self):
|
||||
"""
|
||||
Basic CRUD: automember
|
||||
"""
|
||||
self.init_app()
|
||||
|
||||
# user group rule
|
||||
self.basic_crud(ENTITY, USER_GROUP_DATA,
|
||||
@@ -92,44 +255,25 @@ class test_automember(UI_driver):
|
||||
"""
|
||||
Test automember rebuild membership feature for hosts
|
||||
"""
|
||||
self.init_app()
|
||||
|
||||
host_util = host_tasks()
|
||||
host_util.driver = self.driver
|
||||
host_util.config = self.config
|
||||
host_util = self.get_host_util()
|
||||
domain = self.config.get('ipa_domain')
|
||||
host1 = 'web1.%s' % domain
|
||||
host2 = 'web2.%s' % domain
|
||||
|
||||
# Add a hostgroup
|
||||
self.add_record('hostgroup', {
|
||||
'pkey': 'webservers',
|
||||
'add': [
|
||||
('textbox', 'cn', 'webservers'),
|
||||
('textarea', 'description', 'webservers'),
|
||||
]
|
||||
})
|
||||
self.add_host_group('webservers', 'webservers')
|
||||
|
||||
# Add hosts
|
||||
self.add_record('host', host_util.get_data("web1", domain))
|
||||
self.add_record('host', host_util.get_data("web2", domain))
|
||||
|
||||
# Add an automember rule
|
||||
self.add_record(
|
||||
'automember',
|
||||
{'pkey': 'webservers', 'add': [('combobox', 'cn', 'webservers')]},
|
||||
facet='searchhostgroup'
|
||||
)
|
||||
self.add_host_group_rules('webservers')
|
||||
|
||||
# Add a condition for automember rule
|
||||
self.navigate_to_record('webservers')
|
||||
self.add_table_record(
|
||||
'automemberinclusiveregex',
|
||||
{'fields': [
|
||||
('selectbox', 'key', 'fqdn'),
|
||||
('textbox', 'automemberinclusiveregex', '^web[1-9]+')
|
||||
]}
|
||||
)
|
||||
self.add_conditions([('fqdn', '^web[1-9]+')], condition_type=INCLUSIVE)
|
||||
|
||||
# Assert that hosts are not members of hostgroup
|
||||
self.navigate_to_record('webservers', entity='hostgroup')
|
||||
@@ -152,11 +296,15 @@ class test_automember(UI_driver):
|
||||
# Remove host from hostgroup
|
||||
self.delete_record(host1)
|
||||
|
||||
# Assert that host is not a member of hostgroup
|
||||
# Assert that host was re-added to the group.
|
||||
# The behavior is expected with the plugin default setting:
|
||||
# the entry cn=Auto Membership Plugin,cn=plugins,cn=config has
|
||||
# a default value autoMemberProcessModifyOps: on
|
||||
#
|
||||
# See https://www.port389.org/docs/389ds/design/automember-postop-modify-design.html # noqa: E501
|
||||
self.facet_button_click('refresh')
|
||||
self.wait_for_request()
|
||||
self.assert_record(host1, negative=True)
|
||||
self.assert_record(host2, negative=True)
|
||||
self.assert_record(host1)
|
||||
|
||||
# Rebuild membership for all hosts, using action on hosts search facet
|
||||
self.navigate_by_menu('identity/host')
|
||||
@@ -170,63 +318,29 @@ class test_automember(UI_driver):
|
||||
self.assert_record(host2)
|
||||
|
||||
# Delete hostgroup, hosts and automember rule
|
||||
self.delete('hostgroup', [{'pkey': 'webservers'}])
|
||||
self.delete('host', [{'pkey': host1}, {'pkey': host2}])
|
||||
self.delete('automember', [{'pkey': 'webservers'}],
|
||||
facet='searchhostgroup')
|
||||
self.delete_host_group_rules('webservers')
|
||||
self.delete_host_groups('webservers')
|
||||
|
||||
@screenshot
|
||||
def test_rebuild_membership_users(self):
|
||||
"""
|
||||
Test automember rebuild membership feature for users
|
||||
"""
|
||||
self.init_app()
|
||||
|
||||
# Add a group
|
||||
self.add_record('group', {
|
||||
'pkey': 'devel',
|
||||
'add': [
|
||||
('textbox', 'cn', 'devel'),
|
||||
('textarea', 'description', 'devel'),
|
||||
]
|
||||
})
|
||||
self.add_user_group('devel', 'devel')
|
||||
|
||||
# Add a user
|
||||
self.add_record('user', {
|
||||
'pkey': 'dev1',
|
||||
'add': [
|
||||
('textbox', 'uid', 'dev1'),
|
||||
('textbox', 'givenname', 'Dev'),
|
||||
('textbox', 'sn', 'One'),
|
||||
]
|
||||
})
|
||||
|
||||
# Add another user
|
||||
self.add_record('user', {
|
||||
'pkey': 'dev2',
|
||||
'add': [
|
||||
('textbox', 'uid', 'dev2'),
|
||||
('textbox', 'givenname', 'Dev'),
|
||||
('textbox', 'sn', 'Two'),
|
||||
]
|
||||
})
|
||||
# Add users
|
||||
self.add_user('dev1', 'Dev', 'One')
|
||||
self.add_user('dev2', 'Dev', 'Two')
|
||||
|
||||
# Add an automember rule
|
||||
self.add_record(
|
||||
'automember',
|
||||
{'pkey': 'devel', 'add': [('combobox', 'cn', 'devel')]},
|
||||
facet='searchgroup'
|
||||
)
|
||||
self.add_user_group_rules('devel')
|
||||
|
||||
# Add a condition for automember rule
|
||||
self.navigate_to_record('devel')
|
||||
self.add_table_record(
|
||||
'automemberinclusiveregex',
|
||||
{'fields': [
|
||||
('selectbox', 'key', 'uid'),
|
||||
('textbox', 'automemberinclusiveregex', '^dev[1-9]+')
|
||||
]}
|
||||
)
|
||||
self.add_conditions([('uid', '^dev[1-9]+')], condition_type=INCLUSIVE)
|
||||
|
||||
# Assert that users are not members of group
|
||||
self.navigate_to_record('devel', entity='group')
|
||||
@@ -249,11 +363,15 @@ class test_automember(UI_driver):
|
||||
# Remove user from group
|
||||
self.delete_record('dev1')
|
||||
|
||||
# Assert that user is not a member of group
|
||||
# Assert that user was re-added to the group
|
||||
# The behavior is expected with the plugin default setting:
|
||||
# the entry cn=Auto Membership Plugin,cn=plugins,cn=config has
|
||||
# a default value autoMemberProcessModifyOps: on
|
||||
#
|
||||
# See https://www.port389.org/docs/389ds/design/automember-postop-modify-design.html # noqa: E501
|
||||
self.facet_button_click('refresh')
|
||||
self.wait_for_request()
|
||||
self.assert_record('dev1', negative=True)
|
||||
self.assert_record('dev2', negative=True)
|
||||
self.assert_record('dev1')
|
||||
|
||||
# Rebuild membership for all users, using action on users search facet
|
||||
self.navigate_by_menu('identity/user_search')
|
||||
@@ -267,6 +385,325 @@ class test_automember(UI_driver):
|
||||
self.assert_record('dev2')
|
||||
|
||||
# Delete group, users and automember rule
|
||||
self.delete('group', [{'pkey': 'devel'}])
|
||||
self.delete('user', [{'pkey': 'dev1'}, {'pkey': 'dev2'}])
|
||||
self.delete('automember', [{'pkey': 'devel'}], facet='searchgroup')
|
||||
self.delete_users('dev1', 'dev2')
|
||||
self.delete_user_group_rules('devel')
|
||||
self.delete_user_groups('devel')
|
||||
|
||||
@screenshot
|
||||
def test_add_multiple_user_group_rules(self):
|
||||
"""
|
||||
Test creating and deleting multiple user group rules
|
||||
"""
|
||||
|
||||
groups = ['group1', 'group2', 'group3']
|
||||
|
||||
for group in groups:
|
||||
self.add_user_group(group)
|
||||
|
||||
self.add_user_group_rules(*groups)
|
||||
self.delete_user_group_rules(*groups)
|
||||
|
||||
@screenshot
|
||||
def test_add_multiple_host_group_rules(self):
|
||||
"""
|
||||
Test creating and deleting multiple host group rules
|
||||
"""
|
||||
|
||||
groups = ['group1', 'group2', 'group3']
|
||||
|
||||
for group in groups:
|
||||
self.add_host_group(group)
|
||||
|
||||
self.add_host_group_rules(*groups)
|
||||
self.delete_host_group_rules(*groups)
|
||||
|
||||
@screenshot
|
||||
def test_search_user_group_rule(self):
|
||||
"""
|
||||
Test searching user group rules using filter
|
||||
"""
|
||||
|
||||
pkey = SEARCH_CASES['name']
|
||||
self.add_user_group(pkey, '')
|
||||
self.add_user_group_rules(pkey)
|
||||
self.navigate_to_record(pkey)
|
||||
self.mod_record(ENTITY, {'mod': [
|
||||
('textarea', 'description', SEARCH_CASES['description']),
|
||||
]}, facet='usergrouprule')
|
||||
self.navigate_to_entity(ENTITY, facet='searchgroup')
|
||||
|
||||
for text in SEARCH_CASES['positive']:
|
||||
self.apply_search_filter(text)
|
||||
self.wait_for_request()
|
||||
self.assert_record(pkey)
|
||||
|
||||
for text in SEARCH_CASES['negative']:
|
||||
self.apply_search_filter(text)
|
||||
self.wait_for_request()
|
||||
self.assert_record(pkey, negative=True)
|
||||
|
||||
self.delete_user_group_rules(pkey)
|
||||
self.delete_user_groups(pkey)
|
||||
|
||||
@screenshot
|
||||
def test_search_host_group_rule(self):
|
||||
"""
|
||||
Test searching host group rules using filter
|
||||
"""
|
||||
|
||||
pkey = SEARCH_CASES['name']
|
||||
self.add_host_group(pkey, '')
|
||||
self.add_host_group_rules(pkey, navigate=True)
|
||||
self.navigate_to_record(pkey)
|
||||
self.mod_record(ENTITY, {'mod': [
|
||||
('textarea', 'description', SEARCH_CASES['description']),
|
||||
]}, facet='hostgrouprule')
|
||||
self.navigate_to_entity(ENTITY, facet='searchhostgroup')
|
||||
|
||||
for text in SEARCH_CASES['positive']:
|
||||
self.apply_search_filter(text)
|
||||
self.wait_for_request()
|
||||
self.assert_record(pkey)
|
||||
|
||||
for text in SEARCH_CASES['negative']:
|
||||
self.apply_search_filter(text)
|
||||
self.wait_for_request()
|
||||
self.assert_record(pkey, negative=True)
|
||||
|
||||
self.delete_host_group_rules(pkey)
|
||||
self.delete_host_groups(pkey)
|
||||
|
||||
@screenshot
|
||||
def test_add_user_group_rule_conditions(self):
|
||||
"""
|
||||
Test creating and deleting user group rule conditions
|
||||
"""
|
||||
|
||||
pkey = 'devel'
|
||||
one_inc_condition = ('employeetype', '*engineer*')
|
||||
inc_conditions = [
|
||||
('cn', 'inclusive-expression'),
|
||||
('description', 'other-inclusive-expression'),
|
||||
]
|
||||
one_exc_condition = ('employeetype', '*manager*')
|
||||
exc_conditions = [
|
||||
('cn', 'exclusive-expression'),
|
||||
('description', 'other-exclusive-expression'),
|
||||
]
|
||||
|
||||
self.add_user_group(pkey)
|
||||
self.add_user_group_rules(pkey)
|
||||
|
||||
self.navigate_to_record(pkey)
|
||||
|
||||
self.add_conditions([one_inc_condition], condition_type=INCLUSIVE)
|
||||
self.add_conditions(inc_conditions, condition_type=INCLUSIVE)
|
||||
self.add_conditions([one_exc_condition], condition_type=EXCLUSIVE)
|
||||
self.add_conditions(exc_conditions, condition_type=EXCLUSIVE)
|
||||
|
||||
self.delete_conditions([one_inc_condition], condition_type=INCLUSIVE)
|
||||
self.delete_conditions(inc_conditions, condition_type=INCLUSIVE)
|
||||
self.delete_conditions([one_exc_condition], condition_type=EXCLUSIVE)
|
||||
self.delete_conditions(inc_conditions, condition_type=EXCLUSIVE)
|
||||
|
||||
self.delete_user_group_rules(pkey)
|
||||
self.delete_user_groups(pkey)
|
||||
|
||||
@screenshot
|
||||
def test_add_host_group_rule_conditions(self):
|
||||
"""
|
||||
Test creating and deleting user group rule conditions
|
||||
"""
|
||||
|
||||
pkey = 'devel'
|
||||
one_inc_condition = ('ipaclientversion', '4.8')
|
||||
inc_conditions = [
|
||||
('cn', 'inclusive-expression'),
|
||||
('description', 'other-inclusive-expression'),
|
||||
]
|
||||
one_exc_condition = ('ipaclientversion', '4.7')
|
||||
exc_conditions = [
|
||||
('cn', 'exclusive-expression'),
|
||||
('description', 'other-exclusive-expression'),
|
||||
]
|
||||
|
||||
self.add_host_group(pkey)
|
||||
self.add_host_group_rules(pkey)
|
||||
|
||||
self.navigate_to_record(pkey)
|
||||
|
||||
self.add_conditions([one_inc_condition], condition_type=INCLUSIVE)
|
||||
self.add_conditions(inc_conditions, condition_type=INCLUSIVE)
|
||||
self.add_conditions([one_exc_condition], condition_type=EXCLUSIVE)
|
||||
self.add_conditions(exc_conditions, condition_type=EXCLUSIVE)
|
||||
|
||||
self.delete_conditions([one_inc_condition], condition_type=INCLUSIVE)
|
||||
self.delete_conditions(inc_conditions, condition_type=INCLUSIVE)
|
||||
self.delete_conditions([one_exc_condition], condition_type=EXCLUSIVE)
|
||||
self.delete_conditions(inc_conditions, condition_type=EXCLUSIVE)
|
||||
|
||||
self.delete_host_group_rules(pkey)
|
||||
self.delete_host_groups(pkey)
|
||||
|
||||
@screenshot
|
||||
def test_cancel_new_user_group_rule_condition_dialog(self):
|
||||
"""
|
||||
Test canceling of creating new user group rule condition
|
||||
"""
|
||||
|
||||
pkey = 'devel'
|
||||
|
||||
self.add_user_group(pkey)
|
||||
self.add_user_group_rules(pkey)
|
||||
self.navigate_to_record(pkey)
|
||||
|
||||
for condition_type in [INCLUSIVE, EXCLUSIVE]:
|
||||
self.open_new_condition_dialog(condition_type)
|
||||
self.fill_fields([('selectbox', 'key', 'title')])
|
||||
self.dialog_button_click('cancel')
|
||||
|
||||
self.delete_user_group_rules(pkey)
|
||||
self.delete_user_groups(pkey)
|
||||
|
||||
@screenshot
|
||||
def test_cancel_new_host_group_rule_condition_dialog(self):
|
||||
"""
|
||||
Test canceling of creating new host group rule condition
|
||||
"""
|
||||
|
||||
pkey = 'devel'
|
||||
|
||||
self.add_host_group(pkey)
|
||||
self.add_host_group_rules(pkey)
|
||||
self.navigate_to_record(pkey)
|
||||
|
||||
for condition_type in [INCLUSIVE, EXCLUSIVE]:
|
||||
self.open_new_condition_dialog(condition_type)
|
||||
self.fill_fields([('selectbox', 'key', 'serverhostname')])
|
||||
self.dialog_button_click('cancel')
|
||||
|
||||
self.delete_host_group_rules(pkey)
|
||||
self.delete_host_groups(pkey)
|
||||
|
||||
@screenshot
|
||||
def test_set_default_user_group(self):
|
||||
"""
|
||||
Test setting default user group
|
||||
"""
|
||||
|
||||
pkey = 'default-user-group'
|
||||
user_pkey = 'some-user'
|
||||
|
||||
self.add_user_group(pkey)
|
||||
self.navigate_by_menu('identity/automember/amgroup')
|
||||
self.select_combobox('automemberdefaultgroup', pkey)
|
||||
self.dialog_button_click('ok')
|
||||
|
||||
self.add_user(user_pkey, 'Some', 'User')
|
||||
self.navigate_to_record(user_pkey)
|
||||
self.switch_to_facet('memberof_group')
|
||||
self.assert_record(pkey)
|
||||
|
||||
# Clear default user group
|
||||
self.navigate_by_menu('identity/automember/amgroup')
|
||||
self.select_combobox('automemberdefaultgroup', '')
|
||||
self.dialog_button_click('ok')
|
||||
|
||||
self.delete_users(user_pkey)
|
||||
self.delete_user_groups(pkey)
|
||||
|
||||
@screenshot
|
||||
def test_set_default_host_group(self):
|
||||
"""
|
||||
Test setting default host group
|
||||
"""
|
||||
|
||||
pkey = 'default-host-group'
|
||||
host_util = self.get_host_util()
|
||||
domain = self.config.get('ipa_domain')
|
||||
|
||||
self.add_host_group(pkey)
|
||||
self.navigate_by_menu('identity/automember/amhostgroup')
|
||||
self.select_combobox('automemberdefaultgroup', pkey)
|
||||
self.dialog_button_click('ok')
|
||||
|
||||
host_data = host_util.get_data('some-host', domain)
|
||||
self.add_record('host', host_data)
|
||||
self.navigate_to_record(host_data['pkey'])
|
||||
self.switch_to_facet('memberof_hostgroup')
|
||||
self.assert_record(pkey)
|
||||
|
||||
# Clear default host group
|
||||
self.navigate_by_menu('identity/automember/amhostgroup')
|
||||
self.select_combobox('automemberdefaultgroup', '')
|
||||
self.dialog_button_click('ok')
|
||||
|
||||
self.delete('host', [{'pkey': host_data['pkey']}])
|
||||
self.delete_host_groups(pkey)
|
||||
|
||||
@screenshot
|
||||
def test_add_user_group_rule_with_no_group(self):
|
||||
|
||||
self.add_record(
|
||||
ENTITY,
|
||||
{'pkey': 'empty-user-group', 'add': []},
|
||||
facet='searchgroup',
|
||||
negative=True
|
||||
)
|
||||
|
||||
@screenshot
|
||||
def test_add_host_group_rule_with_no_group(self):
|
||||
|
||||
self.add_record(
|
||||
ENTITY,
|
||||
{'pkey': 'empty-host-group', 'add': []},
|
||||
facet='searchhostgroup',
|
||||
negative=True
|
||||
)
|
||||
|
||||
@screenshot
|
||||
def test_add_user_group_rules_for_same_group(self):
|
||||
"""
|
||||
Test creating user group rules for same group
|
||||
"""
|
||||
|
||||
group_name = 'some-user-group'
|
||||
|
||||
self.add_user_group(group_name)
|
||||
self.add_user_group_rules(group_name)
|
||||
self.add_user_group_rules(group_name, negative=True, pre_delete=False)
|
||||
|
||||
self.assert_last_error_dialog(
|
||||
self.AUTOMEMBER_RULE_EXISTS_ERROR.format(group_name)
|
||||
)
|
||||
|
||||
self.delete_user_group_rules(group_name)
|
||||
self.delete_user_groups(group_name)
|
||||
|
||||
@screenshot
|
||||
def test_add_host_group_rules_for_same_group(self):
|
||||
"""
|
||||
Test creating host group rules for same group
|
||||
"""
|
||||
|
||||
group_name = 'some-host-group'
|
||||
|
||||
self.add_host_group(group_name)
|
||||
self.add_host_group_rules(group_name)
|
||||
self.add_host_group_rules(group_name, negative=True, pre_delete=False)
|
||||
|
||||
self.assert_last_error_dialog(
|
||||
self.AUTOMEMBER_RULE_EXISTS_ERROR.format(group_name)
|
||||
)
|
||||
|
||||
self.delete_host_group_rules(group_name)
|
||||
self.delete_host_groups(group_name)
|
||||
|
||||
@screenshot
|
||||
def test_cancel_group_rule_creating(self):
|
||||
"""
|
||||
Test canceling of creating new automember group rule
|
||||
"""
|
||||
|
||||
self.add_user_group_rules('some-user-group', dialog_btn='cancel')
|
||||
self.add_host_group_rules('some-host-group', dialog_btn='cancel')
|
||||
|
||||
@@ -21,8 +21,11 @@
|
||||
Automount tests
|
||||
"""
|
||||
|
||||
from ipatests.test_webui.ui_driver import UI_driver
|
||||
from ipatests.test_webui.ui_driver import screenshot
|
||||
from ipatests.test_webui.ui_driver import UI_driver, screenshot
|
||||
try:
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
except ImportError:
|
||||
pass
|
||||
import pytest
|
||||
|
||||
LOC_ENTITY = 'automountlocation'
|
||||
@@ -61,9 +64,165 @@ KEY_DATA = {
|
||||
]
|
||||
}
|
||||
|
||||
DIRECT_MAPS = [
|
||||
{
|
||||
'pkey': 'map1',
|
||||
'add': [
|
||||
('radio', 'method', 'add'),
|
||||
('textbox', 'automountmapname', 'map1'),
|
||||
('textarea', 'description', 'foobar'),
|
||||
],
|
||||
},
|
||||
{
|
||||
'pkey': 'map2',
|
||||
'add': [
|
||||
('radio', 'method', 'add'),
|
||||
('textbox', 'automountmapname', 'map2'),
|
||||
('textarea', 'description', 'foobar'),
|
||||
],
|
||||
},
|
||||
{
|
||||
'pkey': 'map3',
|
||||
'add': [
|
||||
('radio', 'method', 'add'),
|
||||
('textbox', 'automountmapname', 'map3'),
|
||||
('textarea', 'description', 'foobar'),
|
||||
],
|
||||
},
|
||||
{
|
||||
'pkey': 'map4',
|
||||
'add': [
|
||||
('radio', 'method', 'add'),
|
||||
('textbox', 'automountmapname', 'map4'),
|
||||
('textarea', 'description', 'foobar'),
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
INDIRECT_MAPS = [
|
||||
{
|
||||
'pkey': 'map1',
|
||||
'add': [
|
||||
('radio', 'method', 'add'),
|
||||
('textbox', 'automountmapname', 'map1'),
|
||||
('textarea', 'description', 'foobar'),
|
||||
],
|
||||
},
|
||||
{
|
||||
'pkey': 'map2',
|
||||
'add': [
|
||||
('radio', 'method', 'add_indirect'),
|
||||
('textbox', 'automountmapname', 'map2'),
|
||||
('textarea', 'description', 'foobar'),
|
||||
('textbox', 'key', 'mount1'),
|
||||
('textbox', 'parentmap', 'map1'),
|
||||
],
|
||||
'mod': [
|
||||
('textarea', 'description', 'modified'),
|
||||
]
|
||||
},
|
||||
{
|
||||
'pkey': 'map3',
|
||||
'add': [
|
||||
('radio', 'method', 'add_indirect'),
|
||||
('textbox', 'automountmapname', 'map3'),
|
||||
('textarea', 'description', 'foobar'),
|
||||
('textbox', 'key', 'mount2'),
|
||||
('textbox', 'parentmap', 'map1'),
|
||||
],
|
||||
},
|
||||
{
|
||||
'pkey': 'map4',
|
||||
'add': [
|
||||
('radio', 'method', 'add_indirect'),
|
||||
('textbox', 'automountmapname', 'map4'),
|
||||
('textarea', 'description', 'foobar'),
|
||||
('textbox', 'key', 'mount3'),
|
||||
('textbox', 'parentmap', 'map1'),
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
DIRECT_MAP_MOD = {
|
||||
'pkey': 'map1',
|
||||
'add': [
|
||||
('radio', 'method', 'add'),
|
||||
('textbox', 'automountmapname', 'map1'),
|
||||
('textarea', 'description', 'desc'),
|
||||
],
|
||||
'mod': [
|
||||
('textarea', 'description', 'modified'),
|
||||
]
|
||||
}
|
||||
|
||||
INDIRECT_MAP_MOD = {
|
||||
'pkey': 'map1',
|
||||
'add': [
|
||||
('radio', 'method', 'add_indirect'),
|
||||
('textbox', 'automountmapname', 'map1'),
|
||||
('textarea', 'description', 'desc'),
|
||||
('textbox', 'key', 'mount1'),
|
||||
],
|
||||
'mod': [
|
||||
('textarea', 'description', 'modified'),
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
class Location:
|
||||
def __init__(self, location, ui):
|
||||
self.ui = ui
|
||||
self.location = location
|
||||
|
||||
def __enter__(self):
|
||||
self.ui.add_record(LOC_ENTITY, self.location)
|
||||
self.ui.navigate_to_record(self.location['pkey'])
|
||||
return self
|
||||
|
||||
def __exit__(self, *args):
|
||||
self.ui.delete(LOC_ENTITY, [self.location])
|
||||
return False
|
||||
|
||||
|
||||
@pytest.mark.tier1
|
||||
class test_automount(UI_driver):
|
||||
class TestAutomount(UI_driver):
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def automount_setup(self, ui_driver_fsetup):
|
||||
self.init_app()
|
||||
|
||||
def add_key(self, key, mount_info, **kwargs):
|
||||
self.add_record(MAP_ENTITY, {
|
||||
'pkey': key,
|
||||
'add': [
|
||||
('textbox', 'automountkey', key),
|
||||
('textbox', 'automountinformation', mount_info),
|
||||
],
|
||||
}, facet='keys', **kwargs)
|
||||
|
||||
def _add_record_with_enter_key(self, record):
|
||||
self.delete_record(record['pkey'])
|
||||
|
||||
self.assert_no_dialog()
|
||||
self.facet_button_click('add')
|
||||
self.assert_dialog('add')
|
||||
self.fill_fields(record['add'])
|
||||
|
||||
dialog = self.get_dialog('add')
|
||||
dialog.send_keys(Keys.ENTER)
|
||||
self.wait_for_request()
|
||||
|
||||
def _delete_record_with_enter_key(self, pkey):
|
||||
assert self.has_record(pkey)
|
||||
|
||||
self.select_record(pkey)
|
||||
self.facet_button_click('remove')
|
||||
|
||||
dialog = self.get_dialog('add')
|
||||
dialog.send_keys(Keys.ENTER)
|
||||
self.wait_for_request(n=2)
|
||||
self.wait()
|
||||
|
||||
|
||||
@screenshot
|
||||
def test_crud(self):
|
||||
@@ -105,7 +264,7 @@ class test_automount(UI_driver):
|
||||
self.navigate_by_breadcrumb(LOC_PKEY)
|
||||
self.delete_record(MAP_PKEY)
|
||||
|
||||
## test indirect maps
|
||||
# test indirect maps
|
||||
direct_pkey = 'itest-direct'
|
||||
indirect_pkey = 'itest-indirect'
|
||||
|
||||
@@ -143,3 +302,323 @@ class test_automount(UI_driver):
|
||||
self.delete_record(indirect_pkey)
|
||||
self.navigate_by_breadcrumb('Automount Locations')
|
||||
self.delete_record(LOC_PKEY)
|
||||
|
||||
@screenshot
|
||||
def test_add_location_dialog(self):
|
||||
"""
|
||||
Test 'Add Automount Location' dialog behaviour
|
||||
"""
|
||||
|
||||
pkeys = ['loc1', 'loc2', 'loc3', 'loc4']
|
||||
locations = [{
|
||||
'pkey': pkey,
|
||||
'add': [('textbox', 'cn', pkey)]
|
||||
} for pkey in pkeys]
|
||||
|
||||
self.navigate_to_entity(LOC_ENTITY)
|
||||
|
||||
# Add and add another
|
||||
self.add_record(LOC_ENTITY, [locations[0], locations[1]],
|
||||
navigate=False)
|
||||
self.assert_record(locations[0]['pkey'])
|
||||
self.assert_record(locations[1]['pkey'])
|
||||
|
||||
# Add and edit
|
||||
self.add_record(LOC_ENTITY, locations[2], dialog_btn='add_and_edit',
|
||||
navigate=False)
|
||||
self.assert_facet(LOC_ENTITY, facet='maps')
|
||||
|
||||
# Cancel dialog
|
||||
self.add_record(LOC_ENTITY, locations[3], dialog_btn='cancel')
|
||||
self.assert_record(locations[3]['pkey'], negative=True)
|
||||
|
||||
# Add duplicated
|
||||
self.add_record(LOC_ENTITY, locations[0], navigate=False,
|
||||
negative=True, pre_delete=False)
|
||||
self.assert_last_error_dialog(
|
||||
'automount location with name "{}" already exists'
|
||||
.format(locations[0]['pkey'])
|
||||
)
|
||||
self.close_all_dialogs()
|
||||
|
||||
# Missing field
|
||||
self.add_record(LOC_ENTITY, {'pkey': 'loc5', 'add': []},
|
||||
navigate=False, negative=True)
|
||||
assert self.has_form_error('cn')
|
||||
self.close_all_dialogs()
|
||||
|
||||
# Delete multiple locations
|
||||
self.delete_record(pkeys)
|
||||
|
||||
@screenshot
|
||||
@pytest.mark.parametrize('maps', [DIRECT_MAPS, INDIRECT_MAPS])
|
||||
def test_add_map_dialog(self, maps):
|
||||
"""
|
||||
Test 'Add Automount Map' dialog behaviour
|
||||
"""
|
||||
|
||||
with Location(LOC_DATA, ui=self):
|
||||
# Add and add another
|
||||
self.add_record(LOC_ENTITY, [maps[0], maps[1]],
|
||||
facet='maps', navigate=False)
|
||||
self.assert_record(maps[0]['pkey'])
|
||||
self.assert_record(maps[1]['pkey'])
|
||||
|
||||
# Add and edit
|
||||
self.add_record(LOC_ENTITY, maps[2], dialog_btn='add_and_edit',
|
||||
facet='maps', navigate=False)
|
||||
self.assert_facet(MAP_ENTITY, facet='keys')
|
||||
|
||||
# Cancel dialog
|
||||
self.add_record(LOC_ENTITY, maps[3], facet='maps',
|
||||
dialog_btn='cancel')
|
||||
self.assert_record(maps[3]['pkey'], negative=True)
|
||||
|
||||
# Delete multiple maps
|
||||
self.delete_record([m['pkey'] for m in maps])
|
||||
|
||||
@screenshot
|
||||
def test_add_indirect_map_with_missing_fields(self):
|
||||
"""
|
||||
Test creating automount map without mapname and mountpoint
|
||||
"""
|
||||
|
||||
maps = {
|
||||
'automountmapname': {
|
||||
'pkey': 'map1',
|
||||
'add': [
|
||||
('radio', 'method', 'add_indirect'),
|
||||
('textarea', 'description', 'desc'),
|
||||
('textbox', 'key', 'mount1'),
|
||||
],
|
||||
},
|
||||
'key': {
|
||||
'pkey': 'map2',
|
||||
'add': [
|
||||
('radio', 'method', 'add_indirect'),
|
||||
('textbox', 'automountmapname', 'map1'),
|
||||
('textarea', 'description', 'desc'),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
with Location(LOC_DATA, ui=self):
|
||||
for missing_field, map_data in maps.items():
|
||||
self.add_record(LOC_ENTITY, map_data, negative=True,
|
||||
facet='maps', navigate=False)
|
||||
assert self.has_form_error(missing_field)
|
||||
self.dialog_button_click('cancel')
|
||||
self.assert_record(map_data['pkey'], negative=True)
|
||||
|
||||
@screenshot
|
||||
def test_add_duplicated_indirect_map(self):
|
||||
"""
|
||||
Test creating indirect automount map with duplicated field values
|
||||
"""
|
||||
|
||||
original_name = 'map1'
|
||||
original_key = 'mount1'
|
||||
|
||||
with Location(LOC_DATA, ui=self):
|
||||
self.add_record(LOC_ENTITY, {
|
||||
'pkey': original_name,
|
||||
'add': [
|
||||
('radio', 'method', 'add_indirect'),
|
||||
('textbox', 'automountmapname', original_name),
|
||||
('textarea', 'description', 'desc'),
|
||||
('textbox', 'key', original_key),
|
||||
],
|
||||
}, facet='maps', navigate=False)
|
||||
|
||||
self.add_record(LOC_ENTITY, {
|
||||
'pkey': 'map2',
|
||||
'add': [
|
||||
('radio', 'method', 'add_indirect'),
|
||||
('textbox', 'automountmapname', original_name),
|
||||
('textarea', 'description', 'desc'),
|
||||
('textbox', 'key', 'mount2'),
|
||||
],
|
||||
}, negative=True, facet='maps', navigate=False, pre_delete=False)
|
||||
self.assert_last_error_dialog(
|
||||
'automount map with name "{}" already exists'
|
||||
.format(original_name)
|
||||
)
|
||||
self.close_all_dialogs()
|
||||
|
||||
self.add_record(LOC_ENTITY, {
|
||||
'pkey': 'map3',
|
||||
'add': [
|
||||
('radio', 'method', 'add_indirect'),
|
||||
('textbox', 'automountmapname', 'map3'),
|
||||
('textarea', 'description', 'desc'),
|
||||
('textbox', 'key', original_key),
|
||||
],
|
||||
}, negative=True, facet='maps', navigate=False, pre_delete=False)
|
||||
self.assert_last_error_dialog(
|
||||
'key named {} already exists'.format(original_key)
|
||||
)
|
||||
self.close_all_dialogs()
|
||||
|
||||
@screenshot
|
||||
@pytest.mark.parametrize('map_data', [DIRECT_MAP_MOD, INDIRECT_MAP_MOD])
|
||||
def test_modify_map(self, map_data):
|
||||
"""
|
||||
Test automount map 'Settings' tab
|
||||
"""
|
||||
|
||||
with Location(LOC_DATA, ui=self):
|
||||
self.add_record(LOC_ENTITY, map_data,
|
||||
facet='maps', navigate=False)
|
||||
self.navigate_to_record(map_data['pkey'])
|
||||
self.switch_to_facet('details')
|
||||
|
||||
# Refresh
|
||||
self.fill_fields(map_data['mod'], undo=True)
|
||||
self.assert_facet_button_enabled('refresh')
|
||||
self.facet_button_click('refresh')
|
||||
self.wait_for_request()
|
||||
self.assert_facet_button_enabled('refresh')
|
||||
|
||||
# Revert
|
||||
self.mod_record(MAP_ENTITY, map_data, facet_btn='revert')
|
||||
|
||||
@screenshot
|
||||
def test_add_key_dialog(self):
|
||||
"""
|
||||
Test 'Add Automount Key' dialog behaviour
|
||||
"""
|
||||
|
||||
pkeys = ['key1', 'key2', 'key3', 'key4']
|
||||
|
||||
keys = [
|
||||
{
|
||||
'pkey': pkey,
|
||||
'add': [
|
||||
('textbox', 'automountkey', pkey),
|
||||
('textbox', 'automountinformation', '/itest/%s' % pkey),
|
||||
],
|
||||
} for pkey in pkeys
|
||||
]
|
||||
|
||||
with Location(LOC_DATA, ui=self):
|
||||
self.add_record(LOC_ENTITY, MAP_DATA, facet='maps', navigate=False)
|
||||
self.navigate_to_record(MAP_PKEY)
|
||||
|
||||
# Add and add another
|
||||
self.add_record(MAP_ENTITY, [keys[0], keys[1]],
|
||||
facet='keys', navigate=False)
|
||||
self.assert_record(keys[0]['pkey'])
|
||||
self.assert_record(keys[1]['pkey'])
|
||||
|
||||
# Add and edit
|
||||
self.add_record(MAP_ENTITY, keys[2], dialog_btn='add_and_edit',
|
||||
facet='keys', navigate=False)
|
||||
self.assert_facet(KEY_ENTITY, facet='details')
|
||||
|
||||
# Cancel dialog
|
||||
self.add_record(MAP_ENTITY, keys[3], facet='keys',
|
||||
dialog_btn='cancel')
|
||||
self.assert_record(keys[3]['pkey'], negative=True)
|
||||
|
||||
# Delete multiple keys
|
||||
self.delete_record(pkeys)
|
||||
|
||||
@screenshot
|
||||
def test_add_key_with_missing_fields(self):
|
||||
"""
|
||||
Test creating automount key without key name and mount information
|
||||
"""
|
||||
|
||||
keys = {
|
||||
'automountkey': {
|
||||
'pkey': 'key1',
|
||||
'add': [('textbox', 'automountinformation', '/itest/key')],
|
||||
},
|
||||
'automountinformation': {
|
||||
'pkey': 'key2',
|
||||
'add': [('textbox', 'automountkey', 'key2')],
|
||||
},
|
||||
}
|
||||
|
||||
with Location(LOC_DATA, ui=self):
|
||||
self.add_record(LOC_ENTITY, MAP_DATA, facet='maps', navigate=False)
|
||||
self.navigate_to_record(MAP_PKEY)
|
||||
|
||||
for missing_field, key in keys.items():
|
||||
self.add_record(MAP_ENTITY, key, negative=True,
|
||||
facet='keys', navigate=False)
|
||||
assert self.has_form_error(missing_field)
|
||||
self.dialog_button_click('cancel')
|
||||
self.assert_record(key['pkey'], negative=True)
|
||||
|
||||
@screenshot
|
||||
def test_add_duplicated_key(self):
|
||||
"""
|
||||
Test creating automount key with duplicated field values
|
||||
"""
|
||||
|
||||
with Location(LOC_DATA, ui=self):
|
||||
self.add_record(LOC_ENTITY, MAP_DATA, facet='maps', navigate=False)
|
||||
self.navigate_to_record(MAP_PKEY)
|
||||
|
||||
key = 'mount1'
|
||||
|
||||
self.add_key(key, '/itest/key', navigate=False)
|
||||
self.add_key(key, '/itest/key2', negative=True, navigate=False,
|
||||
pre_delete=False)
|
||||
self.assert_last_error_dialog(
|
||||
'key named {} already exists'.format(key)
|
||||
)
|
||||
self.close_all_dialogs()
|
||||
|
||||
@screenshot
|
||||
def test_modify_key(self):
|
||||
"""
|
||||
Test automount key 'Settings'
|
||||
"""
|
||||
|
||||
with Location(LOC_DATA, ui=self):
|
||||
self.add_record(LOC_ENTITY, MAP_DATA, facet='maps', navigate=False)
|
||||
self.navigate_to_record(MAP_PKEY)
|
||||
|
||||
self.add_record(MAP_ENTITY, KEY_DATA, facet='keys', navigate=False)
|
||||
self.navigate_to_record(KEY_PKEY)
|
||||
|
||||
# Refresh
|
||||
self.fill_fields(KEY_DATA['mod'])
|
||||
self.assert_facet_button_enabled('refresh')
|
||||
self.facet_button_click('refresh')
|
||||
self.wait_for_request()
|
||||
self.assert_facet_button_enabled('refresh')
|
||||
|
||||
# Revert
|
||||
self.mod_record(KEY_ENTITY, KEY_DATA, facet_btn='revert')
|
||||
|
||||
@screenshot
|
||||
def test_confirm_dialogs_with_enter_key(self):
|
||||
"""
|
||||
Test dialogs can be closed by pressing ENTER
|
||||
"""
|
||||
|
||||
# Add location
|
||||
self.navigate_to_entity(LOC_ENTITY, 'search')
|
||||
self._add_record_with_enter_key(LOC_DATA)
|
||||
|
||||
# Add map
|
||||
self.navigate_to_record(LOC_PKEY)
|
||||
self._add_record_with_enter_key(MAP_DATA)
|
||||
|
||||
# Add key
|
||||
self.navigate_to_record(MAP_PKEY)
|
||||
self._add_record_with_enter_key(KEY_DATA)
|
||||
|
||||
# Delete key
|
||||
self._delete_record_with_enter_key(KEY_PKEY)
|
||||
|
||||
# Delete map
|
||||
self.navigate_by_breadcrumb(LOC_PKEY)
|
||||
self._delete_record_with_enter_key(MAP_PKEY)
|
||||
|
||||
# Delete location
|
||||
self.navigate_to_entity(LOC_ENTITY)
|
||||
self._delete_record_with_enter_key(LOC_PKEY)
|
||||
|
||||
@@ -44,26 +44,6 @@ def search_pkey(self, pkey):
|
||||
self.wait_for_request(n=2)
|
||||
|
||||
|
||||
def add_cert(self, principal, csr):
|
||||
self.facet_button_click('request_cert')
|
||||
self.fill_textbox('principal', principal)
|
||||
self.check_option('add', 'checked')
|
||||
self.fill_textarea('csr', csr)
|
||||
self.dialog_button_click('issue')
|
||||
self.assert_notification(assert_text='Certificate requested')
|
||||
self.navigate_to_entity(ENTITY)
|
||||
rows = self.get_rows()
|
||||
return rows[-1]
|
||||
|
||||
|
||||
def revoke_cert(self, record, reason):
|
||||
self.navigate_to_entity(ENTITY)
|
||||
self.navigate_to_row_record(record)
|
||||
self.action_list_action('revoke_cert', False)
|
||||
self.select('select[name=revocation_reason]', reason)
|
||||
self.dialog_button_click('ok')
|
||||
|
||||
|
||||
def check_option_negative(self, date, option):
|
||||
self.navigate_to_entity(ENTITY)
|
||||
self.select('select[name=search_option]', option)
|
||||
@@ -103,12 +83,34 @@ def check_minimum_serial(self, serial, option):
|
||||
@pytest.mark.tier1
|
||||
class test_cert(UI_driver):
|
||||
|
||||
def setup(self, *args, **kwargs):
|
||||
super(test_cert, self).setup(*args, **kwargs)
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def cert_setup(self, ui_driver_fsetup):
|
||||
if not self.has_ca():
|
||||
self.skip('CA not configured')
|
||||
|
||||
def _add_and_revoke_cert(self, reason='1'):
|
||||
hostname = self.config.get('ipa_server')
|
||||
csr = generate_csr(hostname)
|
||||
|
||||
self.navigate_to_entity(ENTITY)
|
||||
self.facet_button_click('request_cert')
|
||||
self.fill_textbox('principal', 'HTTP/{}'.format(hostname))
|
||||
self.check_option('add', 'checked')
|
||||
self.fill_textarea('csr', csr)
|
||||
self.dialog_button_click('issue')
|
||||
self.assert_notification(assert_text='Certificate requested')
|
||||
self.navigate_to_entity(ENTITY)
|
||||
rows = self.get_rows()
|
||||
cert = rows[-1]
|
||||
|
||||
self.navigate_to_row_record(cert)
|
||||
self.action_list_action('revoke_cert', False)
|
||||
self.select('select[name=revocation_reason]', reason)
|
||||
self.dialog_button_click('ok')
|
||||
self.navigate_to_entity(ENTITY)
|
||||
|
||||
return cert
|
||||
|
||||
@screenshot
|
||||
def test_read(self):
|
||||
"""
|
||||
@@ -160,18 +162,11 @@ class test_cert(UI_driver):
|
||||
Try to search certificates by revocation reason
|
||||
"""
|
||||
self.init_app()
|
||||
self.navigate_to_entity(ENTITY)
|
||||
|
||||
# add a new cert
|
||||
hostname = self.config.get('ipa_server')
|
||||
csr = generate_csr(hostname)
|
||||
record = add_cert(self, 'HTTP/{}'.format(hostname), csr)
|
||||
|
||||
# revoke added cert
|
||||
revoke_cert(self, record, '1')
|
||||
# revoke new certificate
|
||||
self._add_and_revoke_cert()
|
||||
|
||||
# search cert by revocation reason
|
||||
self.navigate_to_entity(ENTITY)
|
||||
self.select('select[name=search_option]', 'revocation_reason')
|
||||
search_pkey(self, '1')
|
||||
rows = self.get_rows()
|
||||
@@ -248,7 +243,10 @@ class test_cert(UI_driver):
|
||||
"""
|
||||
today = date.today()
|
||||
self.init_app()
|
||||
self.navigate_to_entity(ENTITY)
|
||||
|
||||
# revoke new certificate
|
||||
self._add_and_revoke_cert()
|
||||
|
||||
self.select('select[name=search_option]', 'validnotafter_from')
|
||||
search_pkey(self, str(today))
|
||||
rows = self.get_rows()
|
||||
@@ -284,7 +282,10 @@ class test_cert(UI_driver):
|
||||
"""
|
||||
today = date.today()
|
||||
self.init_app()
|
||||
self.navigate_to_entity(ENTITY)
|
||||
|
||||
# revoke new certificate
|
||||
self._add_and_revoke_cert()
|
||||
|
||||
self.select('select[name=search_option]', 'validnotafter_to')
|
||||
search_pkey(self, str(today + timedelta(weeks=52 * 30)))
|
||||
rows = self.get_rows()
|
||||
@@ -320,7 +321,10 @@ class test_cert(UI_driver):
|
||||
"""
|
||||
today = date.today()
|
||||
self.init_app()
|
||||
self.navigate_to_entity(ENTITY)
|
||||
|
||||
# revoke new certificate
|
||||
self._add_and_revoke_cert()
|
||||
|
||||
self.select('select[name=search_option]', 'validnotbefore_from')
|
||||
search_pkey(self, str(today))
|
||||
rows = self.get_rows()
|
||||
@@ -356,7 +360,10 @@ class test_cert(UI_driver):
|
||||
"""
|
||||
today = date.today()
|
||||
self.init_app()
|
||||
self.navigate_to_entity(ENTITY)
|
||||
|
||||
# revoke new certificate
|
||||
self._add_and_revoke_cert()
|
||||
|
||||
self.select('select[name=search_option]', 'validnotbefore_to')
|
||||
search_pkey(self, str(today + timedelta(weeks=52 * 30)))
|
||||
rows = self.get_rows()
|
||||
@@ -392,7 +399,10 @@ class test_cert(UI_driver):
|
||||
"""
|
||||
today = date.today()
|
||||
self.init_app()
|
||||
self.navigate_to_entity(ENTITY)
|
||||
|
||||
# revoke new certificate
|
||||
self._add_and_revoke_cert()
|
||||
|
||||
self.select('select[name=search_option]', 'issuedon_from')
|
||||
search_pkey(self, str(today))
|
||||
rows = self.get_rows()
|
||||
@@ -424,7 +434,10 @@ class test_cert(UI_driver):
|
||||
"""
|
||||
today = date.today()
|
||||
self.init_app()
|
||||
self.navigate_to_entity(ENTITY)
|
||||
|
||||
# revoke new certificate
|
||||
self._add_and_revoke_cert()
|
||||
|
||||
self.select('select[name=search_option]', 'issuedon_to')
|
||||
search_pkey(self, str(today))
|
||||
rows = self.get_rows()
|
||||
@@ -456,7 +469,10 @@ class test_cert(UI_driver):
|
||||
"""
|
||||
today = date.today()
|
||||
self.init_app()
|
||||
self.navigate_to_entity(ENTITY)
|
||||
|
||||
# revoke new certificate
|
||||
self._add_and_revoke_cert()
|
||||
|
||||
self.select('select[name=search_option]', 'revokedon_from')
|
||||
search_pkey(self, str(today))
|
||||
rows = self.get_rows()
|
||||
@@ -488,7 +504,10 @@ class test_cert(UI_driver):
|
||||
"""
|
||||
today = date.today()
|
||||
self.init_app()
|
||||
self.navigate_to_entity(ENTITY)
|
||||
|
||||
# revoke new certificate
|
||||
self._add_and_revoke_cert()
|
||||
|
||||
self.select('select[name=search_option]', 'revokedon_to')
|
||||
search_pkey(self, str(today))
|
||||
rows = self.get_rows()
|
||||
|
||||
@@ -164,6 +164,32 @@ class test_config(UI_driver):
|
||||
self.fill_input(size_limit_s, def_val)
|
||||
self.facet_button_click('save')
|
||||
|
||||
@screenshot
|
||||
def test_size_limit_notification(self):
|
||||
"""
|
||||
Test if no notification is shown when size limit exceeded
|
||||
"""
|
||||
self.init_app()
|
||||
self.navigate_to_entity(config_data.ENTITY)
|
||||
|
||||
size_limit_s = 'ipasearchrecordslimit'
|
||||
def_val = self.get_field_value(size_limit_s)
|
||||
|
||||
self.fill_input(size_limit_s, '10')
|
||||
self.facet_button_click('save')
|
||||
|
||||
self.navigate_to_entity('cert')
|
||||
# wait for a half sec for notification to appear
|
||||
self.wait(0.5)
|
||||
warning = self.find_by_selector('div.notification-area .alert-warning')
|
||||
try:
|
||||
assert not warning, "Warning present: {}".format(warning.text)
|
||||
finally:
|
||||
# restore previous value
|
||||
self.navigate_to_entity(config_data.ENTITY)
|
||||
self.fill_input(size_limit_s, def_val)
|
||||
self.facet_button_click('save')
|
||||
|
||||
@screenshot
|
||||
def test_time_limits(self):
|
||||
"""
|
||||
|
||||
@@ -35,9 +35,8 @@ import pytest
|
||||
@pytest.mark.tier1
|
||||
class test_dns(UI_driver):
|
||||
|
||||
def setup(self, *args, **kwargs):
|
||||
super(test_dns, self).setup(*args, **kwargs)
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def dns_setup(self, ui_driver_fsetup):
|
||||
if not self.has_dns():
|
||||
self.skip('DNS not configured')
|
||||
|
||||
|
||||
@@ -356,3 +356,63 @@ class test_group(UI_driver):
|
||||
self.delete(rbac.ROLE_ENTITY, [rbac.ROLE_DATA])
|
||||
self.delete(hbac.RULE_ENTITY, [hbac.RULE_DATA])
|
||||
self.delete(sudo.RULE_ENTITY, [sudo.RULE_DATA])
|
||||
|
||||
@screenshot
|
||||
def test_member_manager_user(self):
|
||||
"""
|
||||
Test member manager user has permissions to add and remove group
|
||||
members
|
||||
"""
|
||||
self.init_app()
|
||||
|
||||
self.add_record(user.ENTITY, [user.DATA_MEMBER_MANAGER, user.DATA])
|
||||
self.add_record(group.ENTITY, group.DATA2)
|
||||
|
||||
self.navigate_to_record(group.PKEY2)
|
||||
self.add_associations([user.PKEY_MEMBER_MANAGER],
|
||||
facet='membermanager_user')
|
||||
|
||||
# try to add user to group with member manager permissions
|
||||
self.logout()
|
||||
self.login(user.PKEY_MEMBER_MANAGER, user.PASSWD_MEMBER_MANAGER)
|
||||
|
||||
self.navigate_to_record(group.PKEY2, entity=group.ENTITY)
|
||||
self.add_associations([user.PKEY], delete=True)
|
||||
|
||||
# re-login as admin and clean up data
|
||||
self.logout()
|
||||
self.init_app()
|
||||
|
||||
self.delete(user.ENTITY, [user.DATA_MEMBER_MANAGER, user.DATA])
|
||||
self.delete(group.ENTITY, [group.DATA2])
|
||||
|
||||
@screenshot
|
||||
def test_member_manager_group(self):
|
||||
"""
|
||||
Test member managers group has permissions to add and remove group
|
||||
members
|
||||
"""
|
||||
self.init_app()
|
||||
|
||||
self.add_record(user.ENTITY, [user.DATA_MEMBER_MANAGER, user.DATA])
|
||||
self.add_record(group.ENTITY, [group.DATA2, group.DATA3])
|
||||
|
||||
self.navigate_to_record(group.PKEY2)
|
||||
self.add_associations([user.PKEY_MEMBER_MANAGER], facet='member_user')
|
||||
|
||||
self.navigate_to_record(group.PKEY3, entity=group.ENTITY)
|
||||
self.add_associations([group.PKEY2], facet='membermanager_group')
|
||||
|
||||
# try to add host to group with member manager permissions
|
||||
self.logout()
|
||||
self.login(user.PKEY_MEMBER_MANAGER, user.PASSWD_MEMBER_MANAGER)
|
||||
|
||||
self.navigate_to_record(group.PKEY3, entity=group.ENTITY)
|
||||
self.add_associations([user.PKEY], delete=True)
|
||||
|
||||
# re-login as admin and clean up data
|
||||
self.logout()
|
||||
self.init_app()
|
||||
|
||||
self.delete(user.ENTITY, [user.DATA_MEMBER_MANAGER, user.DATA])
|
||||
self.delete(group.ENTITY, [group.DATA2, group.DATA3])
|
||||
|
||||
@@ -154,10 +154,6 @@ class test_hbac(UI_driver):
|
||||
self.button_click('run_test')
|
||||
self.assert_dialog('message_dialog')
|
||||
|
||||
__hbac_ui_click_on_run_test(self)
|
||||
self.click_on_link('User name')
|
||||
self.assert_facet('hbactest', 'user')
|
||||
|
||||
__hbac_ui_click_on_run_test(self)
|
||||
self.click_on_link('Target host')
|
||||
self.assert_facet('hbactest', 'targethost')
|
||||
|
||||
@@ -24,7 +24,7 @@ Host tests
|
||||
import uuid
|
||||
from random import randint
|
||||
|
||||
from ipatests.test_webui.crypto_utils import generate_csr
|
||||
from ipatests.test_webui.crypto_utils import generate_certificate, generate_csr
|
||||
from ipatests.test_webui.ui_driver import UI_driver
|
||||
from ipatests.test_webui.ui_driver import screenshot
|
||||
import ipatests.test_webui.data_hostgroup as hostgroup
|
||||
@@ -48,8 +48,8 @@ ENTITY = 'host'
|
||||
@pytest.mark.tier1
|
||||
class host_tasks(UI_driver):
|
||||
|
||||
def setup(self, *args, **kwargs):
|
||||
super(host_tasks, self).setup(*args, **kwargs)
|
||||
@pytest.fixture(autouse=True)
|
||||
def hosttasks_setup(self, ui_driver_fsetup):
|
||||
self.prep_data()
|
||||
self.prep_data2()
|
||||
self.prep_data3()
|
||||
@@ -182,6 +182,8 @@ class test_host(host_tasks):
|
||||
self.wait_for_request(n=2, d=3)
|
||||
self.assert_visible(cert_widget_sel)
|
||||
|
||||
widget = self.find(cert_widget_sel, By.CSS_SELECTOR)
|
||||
|
||||
# cert view
|
||||
self.action_list_action('view', confirm=False,
|
||||
parents_css_sel=cert_widget_sel)
|
||||
@@ -216,7 +218,8 @@ class test_host(host_tasks):
|
||||
self.wait()
|
||||
self.select('select', '6')
|
||||
self.dialog_button_click('ok')
|
||||
self.wait_for_request(n=2, d=3)
|
||||
self.wait_while_working(widget)
|
||||
|
||||
self.assert_visible(cert_widget_sel + " div.watermark")
|
||||
|
||||
# check that revoke action is not enabled
|
||||
@@ -234,7 +237,7 @@ class test_host(host_tasks):
|
||||
parents_css_sel=cert_widget_sel)
|
||||
self.wait()
|
||||
self.dialog_button_click('ok')
|
||||
self.wait_for_request(n=2)
|
||||
self.wait_while_working(widget)
|
||||
|
||||
# check that revoke action is enabled
|
||||
self.assert_action_list_action('revoke',
|
||||
@@ -254,15 +257,8 @@ class test_host(host_tasks):
|
||||
def test_arbitrary_certificates(self):
|
||||
"""
|
||||
Test managing host arbitrary certificate.
|
||||
|
||||
Requires to have 'arbitrary_cert_path' configuration set.
|
||||
"""
|
||||
cert_path = self.config.get('arbitrary_cert_path')
|
||||
if not cert_path:
|
||||
self.skip('Arbitrary certificate file is not configured')
|
||||
|
||||
self.init_app()
|
||||
cert = self.load_file(cert_path)
|
||||
self.add_record(ENTITY, self.data)
|
||||
|
||||
self.navigate_to_record(self.pkey)
|
||||
@@ -272,9 +268,11 @@ class test_host(host_tasks):
|
||||
|
||||
# add certificate
|
||||
self.button_click('add', parents_css_sel="div[name='certificate']")
|
||||
self.assert_dialog()
|
||||
self.assert_dialog('cert-add-dialog')
|
||||
|
||||
cert = generate_certificate(self.pkey)
|
||||
self.fill_textarea('new_cert', cert)
|
||||
self.dialog_button_click('add')
|
||||
self.dialog_button_click('ok')
|
||||
|
||||
self.assert_visible("div.certificate-widget")
|
||||
|
||||
|
||||
@@ -23,11 +23,13 @@ Hostgroup tests
|
||||
|
||||
from ipatests.test_webui.ui_driver import UI_driver
|
||||
from ipatests.test_webui.ui_driver import screenshot
|
||||
import ipatests.test_webui.data_group as group
|
||||
import ipatests.test_webui.data_hostgroup as hostgroup
|
||||
from ipatests.test_webui.test_host import host_tasks, ENTITY as HOST_ENTITY
|
||||
import ipatests.test_webui.data_netgroup as netgroup
|
||||
import ipatests.test_webui.data_hbac as hbac
|
||||
import ipatests.test_webui.data_sudo as sudo
|
||||
import ipatests.test_webui.data_user as user
|
||||
import pytest
|
||||
|
||||
|
||||
@@ -152,8 +154,8 @@ class test_hostgroup(UI_driver):
|
||||
self.assert_indirect_record(hbac.RULE_PKEY, hostgroup.ENTITY, 'memberof_hbacrule')
|
||||
self.assert_indirect_record(sudo.RULE_PKEY, hostgroup.ENTITY, 'memberof_sudorule')
|
||||
|
||||
## cleanup
|
||||
## -------
|
||||
# cleanup
|
||||
# -------
|
||||
self.delete(hostgroup.ENTITY, [hostgroup.DATA, hostgroup.DATA2,
|
||||
hostgroup.DATA3, hostgroup.DATA4,
|
||||
hostgroup.DATA5])
|
||||
@@ -161,6 +163,83 @@ class test_hostgroup(UI_driver):
|
||||
self.delete(hbac.RULE_ENTITY, [hbac.RULE_DATA])
|
||||
self.delete(sudo.RULE_ENTITY, [sudo.RULE_DATA])
|
||||
|
||||
@screenshot
|
||||
def test_member_manager_user(self):
|
||||
"""
|
||||
Test member manager user has permissions to add and remove host group
|
||||
members
|
||||
"""
|
||||
self.init_app()
|
||||
host = host_tasks()
|
||||
host.driver = self.driver
|
||||
host.config = self.config
|
||||
host.prep_data2()
|
||||
|
||||
self.add_record(user.ENTITY, [user.DATA_MEMBER_MANAGER])
|
||||
|
||||
self.add_record(HOST_ENTITY, host.data2)
|
||||
self.add_record(hostgroup.ENTITY, hostgroup.DATA)
|
||||
|
||||
self.navigate_to_record(hostgroup.PKEY)
|
||||
self.add_associations([user.PKEY_MEMBER_MANAGER],
|
||||
facet='membermanager_user')
|
||||
|
||||
# try to add host to group with member manager permissions
|
||||
self.logout()
|
||||
self.login(user.PKEY_MEMBER_MANAGER, user.PASSWD_MEMBER_MANAGER)
|
||||
|
||||
self.navigate_to_record(hostgroup.PKEY, entity=hostgroup.ENTITY)
|
||||
self.add_associations([host.pkey2], delete=True)
|
||||
|
||||
# re-login as admin and clean up data
|
||||
self.logout()
|
||||
self.init_app()
|
||||
|
||||
self.delete(HOST_ENTITY, [host.data2])
|
||||
self.delete(user.ENTITY, [user.DATA_MEMBER_MANAGER])
|
||||
self.delete(hostgroup.ENTITY, [hostgroup.DATA])
|
||||
|
||||
@screenshot
|
||||
def test_member_manager_group(self):
|
||||
"""
|
||||
Test member managers group has permissions to add and remove host group
|
||||
members
|
||||
"""
|
||||
self.init_app()
|
||||
host = host_tasks()
|
||||
host.driver = self.driver
|
||||
host.config = self.config
|
||||
host.prep_data2()
|
||||
|
||||
self.add_record(user.ENTITY, user.DATA_MEMBER_MANAGER)
|
||||
self.add_record(group.ENTITY, [group.DATA2])
|
||||
|
||||
self.navigate_to_record(group.PKEY2)
|
||||
self.add_associations([user.PKEY_MEMBER_MANAGER], facet='member_user')
|
||||
|
||||
self.add_record(HOST_ENTITY, host.data2)
|
||||
self.add_record(hostgroup.ENTITY, hostgroup.DATA)
|
||||
|
||||
self.navigate_to_record(hostgroup.PKEY)
|
||||
self.add_associations([group.PKEY2], facet='membermanager_group')
|
||||
|
||||
# try to add host to group with member manager permissions
|
||||
self.logout()
|
||||
self.login(user.PKEY_MEMBER_MANAGER, user.PASSWD_MEMBER_MANAGER)
|
||||
|
||||
self.navigate_to_record(hostgroup.PKEY, entity=hostgroup.ENTITY)
|
||||
self.add_associations([host.pkey2], delete=True)
|
||||
|
||||
# re-login as admin and clean up data
|
||||
self.logout()
|
||||
self.init_app()
|
||||
|
||||
self.delete(HOST_ENTITY, [host.data2])
|
||||
self.delete(user.ENTITY, [user.DATA_MEMBER_MANAGER])
|
||||
self.delete(group.ENTITY, [group.DATA2])
|
||||
self.delete(hostgroup.ENTITY, [hostgroup.DATA])
|
||||
|
||||
@screenshot
|
||||
def test_names_and_button(self):
|
||||
"""
|
||||
Hostgroup names and buttons
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"""
|
||||
Test LoginScreen widget and all it's views
|
||||
"""
|
||||
import urllib
|
||||
|
||||
from ipatests.test_webui.ui_driver import UI_driver
|
||||
from ipatests.test_webui.ui_driver import screenshot
|
||||
@@ -18,28 +19,27 @@ except ImportError:
|
||||
pass
|
||||
|
||||
import pytest
|
||||
from six.moves import urllib
|
||||
|
||||
|
||||
@pytest.mark.tier1
|
||||
class TestLoginScreen(UI_driver):
|
||||
|
||||
def setup(self, *args, **kwargs):
|
||||
super(TestLoginScreen, self).setup(*args, **kwargs)
|
||||
@pytest.fixture(autouse=True)
|
||||
def loginscreen_setup(self, request, ui_driver_fsetup):
|
||||
self.init_app()
|
||||
self.add_test_user()
|
||||
self.logout()
|
||||
|
||||
def teardown(self, *args, **kwargs):
|
||||
# log out first
|
||||
if (self.logged_in()):
|
||||
self.logout()
|
||||
else:
|
||||
self.load_url(self.get_base_url())
|
||||
# log in as administrator
|
||||
self.login()
|
||||
self.delete_test_user()
|
||||
super(TestLoginScreen, self).teardown(*args, **kwargs)
|
||||
def fin():
|
||||
# log out first
|
||||
if (self.logged_in()):
|
||||
self.logout()
|
||||
else:
|
||||
self.load_url(self.get_base_url())
|
||||
# log in as administrator
|
||||
self.login()
|
||||
self.delete_test_user()
|
||||
request.addfinalizer(fin)
|
||||
|
||||
def delete_test_user(self):
|
||||
"""
|
||||
@@ -124,6 +124,7 @@ class TestLoginScreen(UI_driver):
|
||||
WebDriverWait(self.driver, 10).until(
|
||||
lambda d: runner.files_loaded()
|
||||
)
|
||||
self.wait()
|
||||
|
||||
def relogin_with_new_password(self):
|
||||
"""
|
||||
@@ -157,7 +158,7 @@ class TestLoginScreen(UI_driver):
|
||||
new_pass_field.send_keys(new_password)
|
||||
verify_pass_field.send_keys(new_password)
|
||||
verify_pass_field.send_keys(Keys.RETURN)
|
||||
self.wait()
|
||||
self.wait(0.5)
|
||||
self.assert_notification(assert_text='Password change complete',
|
||||
link_text=link_text, link_url=link_url)
|
||||
|
||||
@@ -369,6 +370,7 @@ class TestLoginScreen(UI_driver):
|
||||
# check click on 'Cancel' button
|
||||
self.check_cancel()
|
||||
self.button_click_on_login_screen('login')
|
||||
self.wait_for_request()
|
||||
|
||||
# check if user is not logged
|
||||
assert not self.logged_in()
|
||||
|
||||
@@ -21,10 +21,15 @@
|
||||
Range tests
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
import ipatests.test_webui.test_trust as trust_mod
|
||||
from ipatests.test_webui.ui_driver import screenshot
|
||||
from ipatests.test_webui.task_range import range_tasks
|
||||
import pytest
|
||||
from ipatests.test_webui.task_range import (
|
||||
range_tasks,
|
||||
LOCAL_ID_RANGE,
|
||||
TRUSTED_ID_RANGE,
|
||||
)
|
||||
|
||||
ENTITY = 'idrange'
|
||||
PKEY = 'itest-range'
|
||||
@@ -33,13 +38,20 @@ PKEY = 'itest-range'
|
||||
@pytest.mark.tier1
|
||||
class test_range(range_tasks):
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def range_setup(self, ui_driver_fsetup):
|
||||
self.init_app()
|
||||
self.get_shifts()
|
||||
|
||||
self.range_types = [LOCAL_ID_RANGE]
|
||||
if self.has_trusts():
|
||||
self.range_types.append(TRUSTED_ID_RANGE)
|
||||
|
||||
@screenshot
|
||||
def test_crud(self):
|
||||
"""
|
||||
Basic CRUD: range
|
||||
"""
|
||||
self.init_app()
|
||||
self.get_shifts()
|
||||
self.basic_crud(ENTITY, self.get_data(PKEY), mod=False)
|
||||
|
||||
@screenshot
|
||||
@@ -47,17 +59,9 @@ class test_range(range_tasks):
|
||||
"""
|
||||
Test mod operating in a new range
|
||||
"""
|
||||
data = self.get_data(PKEY)
|
||||
|
||||
self.init_app()
|
||||
self.navigate_to_entity(ENTITY)
|
||||
self.get_shifts()
|
||||
|
||||
add = self.get_add_data(PKEY)
|
||||
data = self.get_data(PKEY, add_data=add)
|
||||
|
||||
self.add_record(ENTITY, data, facet='search', navigate=False,
|
||||
facet_btn='add', dialog_name='add',
|
||||
dialog_btn='add')
|
||||
self.add_record(ENTITY, data)
|
||||
self.navigate_to_record(PKEY)
|
||||
|
||||
# changes idrange and tries to save it
|
||||
@@ -88,15 +92,12 @@ class test_range(range_tasks):
|
||||
- 'ipa-ad-winsync' and 'ipa-ipa-trust' and are not supported yet
|
||||
https://fedorahosted.org/freeipa/ticket/4323
|
||||
"""
|
||||
self.init_app()
|
||||
self.get_shifts()
|
||||
|
||||
pkey_local = 'itest-local'
|
||||
pkey_ad = 'itest-ad'
|
||||
column = 'iparangetype'
|
||||
|
||||
add = self.get_add_data(pkey_local)
|
||||
data = self.get_data(pkey_local, add_data=add)
|
||||
data = self.get_data(pkey_local)
|
||||
self.add_record(ENTITY, data)
|
||||
self.assert_record_value('local domain range', pkey_local, column)
|
||||
|
||||
@@ -111,13 +112,251 @@ class test_range(range_tasks):
|
||||
|
||||
self.navigate_to_entity(ENTITY)
|
||||
|
||||
add = self.get_add_data(pkey_ad, range_type='ipa-ad-trust', domain=domain)
|
||||
data = self.get_data(pkey_ad, add_data=add)
|
||||
data = self.get_data(pkey_ad, range_type=TRUSTED_ID_RANGE,
|
||||
domain=domain)
|
||||
self.add_record(ENTITY, data, navigate=False)
|
||||
self.assert_record_value('Active Directory domain range', pkey_ad, column)
|
||||
self.assert_record_value('Active Directory domain range', pkey_ad,
|
||||
column)
|
||||
|
||||
self.delete(trust_mod.ENTITY, [trust_data])
|
||||
self.navigate_to_entity(ENTITY)
|
||||
self.delete_record(pkey_ad)
|
||||
|
||||
self.delete_record(pkey_local)
|
||||
|
||||
@screenshot
|
||||
def test_add_range_with_special_characters_in_name(self):
|
||||
"""
|
||||
Test creating ID Range with special characters in name
|
||||
"""
|
||||
data = self.get_data('itest-range-!@#$%^&*')
|
||||
self.add_record(ENTITY, data, delete=True)
|
||||
|
||||
@screenshot
|
||||
def test_add_range_with_existing_name(self):
|
||||
"""
|
||||
Test creating ID Range with existing range name
|
||||
"""
|
||||
for range_type in self.range_types:
|
||||
pkey = 'itest-range-{}'.format(range_type)
|
||||
data = self.get_data(pkey, range_type=range_type)
|
||||
|
||||
self.add_record(ENTITY, data)
|
||||
self.add_record(ENTITY, data, navigate=False, negative=True,
|
||||
pre_delete=False)
|
||||
|
||||
dialog = self.get_last_error_dialog()
|
||||
|
||||
try:
|
||||
assert ('range with name "{}" already exists'.format(pkey)
|
||||
in dialog.text)
|
||||
finally:
|
||||
self.delete_record(pkey)
|
||||
|
||||
@screenshot
|
||||
def test_add_range_with_existing_base_id(self):
|
||||
"""
|
||||
Test creating ID Range with existing base ID
|
||||
"""
|
||||
for range_type in self.range_types:
|
||||
pkey = 'itest-range-original'
|
||||
form_data = self.get_add_form_data(pkey)
|
||||
data = self.get_data(pkey, form_data=form_data)
|
||||
form_data.range_type = range_type
|
||||
duplicated_data = self.get_data(form_data=form_data)
|
||||
|
||||
self.add_record(ENTITY, data)
|
||||
self.add_record(ENTITY, duplicated_data, navigate=False,
|
||||
negative=True, pre_delete=False)
|
||||
|
||||
dialog = self.get_last_error_dialog()
|
||||
|
||||
try:
|
||||
assert self.BASE_RANGE_OVERLAPS_ERROR in dialog.text
|
||||
finally:
|
||||
self.delete_record(pkey)
|
||||
|
||||
@screenshot
|
||||
def test_add_range_overlaps_with_existing(self):
|
||||
"""
|
||||
Test creating ID Range with overlapping of existing range
|
||||
"""
|
||||
for range_type in self.range_types:
|
||||
pkey = 'itest-range'
|
||||
pkey_overlaps = 'itest-range-overlaps'
|
||||
|
||||
form_data = self.get_add_form_data(pkey)
|
||||
data = self.get_data(pkey, form_data=form_data)
|
||||
form_data_overlaps = self.get_add_form_data(
|
||||
pkey_overlaps,
|
||||
base_id=form_data.base_id + form_data.size - 1,
|
||||
range_type=range_type
|
||||
)
|
||||
data_overlaps = self.get_data(form_data=form_data_overlaps)
|
||||
|
||||
self.add_record(ENTITY, data)
|
||||
self.add_record(ENTITY, data_overlaps, navigate=False,
|
||||
negative=True, pre_delete=False)
|
||||
|
||||
dialog = self.get_last_error_dialog()
|
||||
|
||||
try:
|
||||
assert self.BASE_RANGE_OVERLAPS_ERROR in dialog.text
|
||||
finally:
|
||||
self.delete_record(pkey)
|
||||
|
||||
@screenshot
|
||||
def test_add_range_with_overlapping_primary_and_secondary_rid(self):
|
||||
"""
|
||||
Test creating ID Range with overlapping of primary and secondary RID
|
||||
"""
|
||||
form_data = self.get_add_form_data(PKEY)
|
||||
form_data.secondary_base_rid = form_data.base_rid
|
||||
data = self.get_data(PKEY, form_data=form_data)
|
||||
|
||||
self.add_record(ENTITY, data, negative=True)
|
||||
dialog = self.get_last_error_dialog()
|
||||
|
||||
try:
|
||||
assert self.PRIMARY_AND_SECONDARY_RID_OVERLAP_ERROR in dialog.text
|
||||
finally:
|
||||
self.delete_record(PKEY)
|
||||
|
||||
@screenshot
|
||||
def test_add_range_with_existing_base_rid(self):
|
||||
"""
|
||||
Test creating ID Range with existing primary RID base
|
||||
"""
|
||||
form_data = self.get_add_form_data(PKEY)
|
||||
data = self.get_data(PKEY, form_data=form_data)
|
||||
|
||||
# Get RID base from previous form
|
||||
duplicated_data = self.get_data(base_rid=form_data.base_rid)
|
||||
|
||||
self.add_record(ENTITY, data)
|
||||
self.add_record(ENTITY, duplicated_data, navigate=False, negative=True,
|
||||
pre_delete=False)
|
||||
|
||||
dialog = self.get_last_error_dialog()
|
||||
|
||||
try:
|
||||
assert self.PRIMARY_RID_RANGE_OVERLAPS_ERROR in dialog.text
|
||||
finally:
|
||||
self.delete_record(PKEY)
|
||||
|
||||
@screenshot
|
||||
def test_add_range_with_existing_secondary_rid(self):
|
||||
"""
|
||||
Test creating ID Range with existing secondary RID base
|
||||
"""
|
||||
form_data = self.get_add_form_data(PKEY)
|
||||
data = self.get_data(PKEY, form_data=form_data)
|
||||
# Get RID base from previous form
|
||||
duplicated_data = self.get_data(
|
||||
secondary_base_rid=form_data.secondary_base_rid
|
||||
)
|
||||
|
||||
self.add_record(ENTITY, data)
|
||||
self.add_record(ENTITY, duplicated_data, navigate=False, negative=True,
|
||||
pre_delete=False)
|
||||
|
||||
dialog = self.get_last_error_dialog()
|
||||
|
||||
try:
|
||||
assert self.SECONDARY_RID_RANGE_OVERLAPS_ERROR in dialog.text
|
||||
finally:
|
||||
self.delete_record(PKEY)
|
||||
|
||||
@screenshot
|
||||
def test_add_range_without_rid(self):
|
||||
"""
|
||||
Test creating ID Range without giving rid-base or/and
|
||||
secondary-rid-base values
|
||||
"""
|
||||
pkey = 'itest-range-without-rid'
|
||||
|
||||
# Without primary RID base
|
||||
data = self.get_data(pkey, base_rid='')
|
||||
self.add_record(ENTITY, data, negative=True)
|
||||
try:
|
||||
assert self.has_form_error('ipabaserid')
|
||||
finally:
|
||||
self.delete_record(pkey)
|
||||
|
||||
self.dialog_button_click('cancel')
|
||||
|
||||
# Without secondary RID base
|
||||
data = self.get_data(pkey, secondary_base_rid='')
|
||||
self.add_record(ENTITY, data, navigate=False, negative=True)
|
||||
try:
|
||||
assert self.has_form_error('ipasecondarybaserid')
|
||||
finally:
|
||||
self.delete_record(pkey)
|
||||
|
||||
self.dialog_button_click('cancel')
|
||||
|
||||
# Without primary and secondary RID bases
|
||||
data = self.get_data(pkey, base_rid='', secondary_base_rid='')
|
||||
self.add_record(ENTITY, data, navigate=False)
|
||||
self.delete_record(pkey)
|
||||
|
||||
@screenshot
|
||||
def test_modify_range_with_invalid_or_missing_values(self):
|
||||
"""
|
||||
Test modification ID range with empty values of options
|
||||
"""
|
||||
cases = [
|
||||
# Empty values
|
||||
{
|
||||
'base_id': '',
|
||||
'base_rid': '',
|
||||
'secondary_base_rid': '',
|
||||
'size': '',
|
||||
},
|
||||
# Out of range
|
||||
{'base_id': 2 ** 32},
|
||||
{'size': 2 ** 32},
|
||||
{'base_rid': 2 ** 32},
|
||||
{'secondary_base_rid': 2 ** 32},
|
||||
# Invalid value
|
||||
{'base_id': 1.1},
|
||||
{'size': 1.1},
|
||||
{'base_rid': 1.1},
|
||||
{'secondary_base_rid': 1.1},
|
||||
]
|
||||
data = self.get_data(PKEY)
|
||||
|
||||
self.add_record(ENTITY, data)
|
||||
self.navigate_to_record(PKEY)
|
||||
|
||||
for values in cases:
|
||||
form_data = self.get_mod_form_data(**values)
|
||||
|
||||
self.fill_fields(form_data.serialize(), undo=True)
|
||||
self.assert_facet_button_enabled('save')
|
||||
self.facet_button_click('save')
|
||||
|
||||
self.assert_notification(
|
||||
type='danger',
|
||||
assert_text='Input form contains invalid or missing values.'
|
||||
)
|
||||
self.close_notifications()
|
||||
self.facet_button_click('revert')
|
||||
|
||||
self.delete_record(PKEY)
|
||||
|
||||
@screenshot
|
||||
def test_delete_primary_local_range(self):
|
||||
"""
|
||||
Test deleting primary local ID range
|
||||
"""
|
||||
ipa_realm = self.config.get('ipa_realm')
|
||||
pkey = '{}_id_range'.format(ipa_realm)
|
||||
|
||||
self.navigate_to_entity(ENTITY)
|
||||
self.delete_record(pkey)
|
||||
|
||||
self.assert_last_error_dialog(
|
||||
self.DELETE_PRIMARY_LOCAL_RANGE_ERROR,
|
||||
details=True
|
||||
)
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
SELinux user map tests
|
||||
"""
|
||||
|
||||
from ipaplatform.constants import constants as platformconstants
|
||||
from ipatests.test_webui.ui_driver import UI_driver
|
||||
from ipatests.test_webui.ui_driver import screenshot
|
||||
import ipatests.test_webui.data_user as user
|
||||
@@ -41,13 +42,18 @@ RULE_ALR_EXIST = 'SELinux User Map rule with name "{}" already exists'
|
||||
RULE_UPDATED = 'SELinux User Map {} updated'
|
||||
RULE_ADDED = 'SELinux User Map successfully added'
|
||||
INVALID_SEUSER = 'SELinux user {} not found in ordering list (in config)'
|
||||
INVALID_MCS = ("invalid 'selinuxuser': Invalid MCS value, must match c[0-1023]"
|
||||
".c[0-1023] and/or c[0-1023]-c[0-c0123]")
|
||||
INVALID_MLS = ("invalid 'selinuxuser': Invalid MLS value, must match "
|
||||
"s[0-15](-s[0-15])")
|
||||
INVALID_MCS = ("invalid 'selinuxuser': Invalid MCS value, must match {}, "
|
||||
"where max category {}").format(
|
||||
platformconstants.SELINUX_MCS_REGEX,
|
||||
platformconstants.SELINUX_MCS_MAX)
|
||||
INVALID_MLS = ("invalid 'selinuxuser': Invalid MLS value, must match {}, "
|
||||
"where max level {}").format(
|
||||
platformconstants.SELINUX_MLS_REGEX,
|
||||
platformconstants.SELINUX_MLS_MAX)
|
||||
HBAC_DEL_ERR = ('{} cannot be deleted because SELinux User Map {} requires '
|
||||
'it')
|
||||
HBAC_MEMBER_ERR = 'HBAC rule and local members cannot both be set'
|
||||
BATCH_ERR = 'Some operations failed.'
|
||||
|
||||
|
||||
@pytest.mark.tier1
|
||||
@@ -180,7 +186,7 @@ class test_selinuxusermap(UI_driver):
|
||||
self.navigate_to_record(selinuxmap.PKEY, entity=selinuxmap.ENTITY)
|
||||
self.add_table_associations('memberuser_user', ['admin'],
|
||||
negative=True)
|
||||
self.assert_last_error_dialog(HBAC_MEMBER_ERR)
|
||||
self.assert_last_error_dialog(BATCH_ERR)
|
||||
self.close_all_dialogs()
|
||||
|
||||
# test adding HBAC rule together with user (should FAIL)
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
Service tests
|
||||
"""
|
||||
|
||||
from ipatests.test_webui.crypto_utils import generate_certificate, generate_csr
|
||||
from ipatests.test_webui.ui_driver import UI_driver
|
||||
from ipatests.test_webui.ui_driver import screenshot
|
||||
import pytest
|
||||
@@ -147,21 +148,17 @@ class test_service(sevice_tasks):
|
||||
"""
|
||||
Test service certificate actions
|
||||
|
||||
Requires to have CA installed and 'service_csr_path' configuration
|
||||
option set.
|
||||
Requires to have CA installed.
|
||||
"""
|
||||
|
||||
if not self.has_ca():
|
||||
self.skip('CA is not configured')
|
||||
|
||||
csr_path = self.config.get('service_csr_path')
|
||||
if not csr_path:
|
||||
self.skip('CSR file is not configured')
|
||||
|
||||
self.init_app()
|
||||
data = self.prep_data()
|
||||
pkey = data.get('pkey')
|
||||
csr = self.load_file(csr_path)
|
||||
hostname = self.config.get('ipa_server')
|
||||
csr = generate_csr(hostname)
|
||||
cert_widget_sel = "div.certificate-widget"
|
||||
|
||||
self.add_record(ENTITY, data)
|
||||
@@ -178,6 +175,8 @@ class test_service(sevice_tasks):
|
||||
self.wait_for_request(n=2, d=3)
|
||||
self.assert_visible(cert_widget_sel)
|
||||
|
||||
widget = self.find(cert_widget_sel, By.CSS_SELECTOR)
|
||||
|
||||
# cert view
|
||||
self.action_list_action('view', confirm=False,
|
||||
parents_css_sel=cert_widget_sel)
|
||||
@@ -218,7 +217,8 @@ class test_service(sevice_tasks):
|
||||
self.wait()
|
||||
self.select('select', '6')
|
||||
self.dialog_button_click('ok')
|
||||
self.wait_for_request(n=2, d=3)
|
||||
self.wait_while_working(widget)
|
||||
|
||||
self.assert_visible(cert_widget_sel + " div.watermark")
|
||||
|
||||
# check that revoke action is not enabled
|
||||
@@ -241,7 +241,7 @@ class test_service(sevice_tasks):
|
||||
parents_css_sel=cert_widget_sel)
|
||||
self.wait()
|
||||
self.dialog_button_click('ok')
|
||||
self.wait_for_request(n=2)
|
||||
self.wait_while_working(widget)
|
||||
|
||||
# check that revoke action is enabled
|
||||
self.assert_action_list_action('revoke',
|
||||
@@ -266,6 +266,8 @@ class test_service(sevice_tasks):
|
||||
self.wait()
|
||||
self.select('select', '1')
|
||||
self.dialog_button_click('ok')
|
||||
self.close_notifications()
|
||||
self.wait_while_working(widget)
|
||||
|
||||
# check that revoke action is not enabled
|
||||
self.assert_action_list_action('revoke', enabled=False,
|
||||
@@ -285,17 +287,12 @@ class test_service(sevice_tasks):
|
||||
def test_arbitrary_certificates(self):
|
||||
"""
|
||||
Test managing service arbitrary certificate.
|
||||
|
||||
Requires to have 'arbitrary_cert_path' configuration set.
|
||||
"""
|
||||
cert_path = self.config.get('arbitrary_cert_path')
|
||||
if not cert_path:
|
||||
self.skip('Arbitrary certificate file is not configured')
|
||||
|
||||
self.init_app()
|
||||
data = self.prep_data()
|
||||
pkey = data.get('pkey')
|
||||
cert = self.load_file(cert_path)
|
||||
hostname = self.config.get('ipa_server')
|
||||
cert = generate_certificate(hostname)
|
||||
cert_widget_sel = "div.certificate-widget"
|
||||
|
||||
self.add_record(ENTITY, data)
|
||||
@@ -306,9 +303,9 @@ class test_service(sevice_tasks):
|
||||
|
||||
# add certificate
|
||||
self.button_click('add', parents_css_sel="div[name='certificate']")
|
||||
self.assert_dialog()
|
||||
self.assert_dialog('cert-add-dialog')
|
||||
self.fill_textarea('new_cert', cert)
|
||||
self.dialog_button_click('add')
|
||||
self.dialog_button_click('ok')
|
||||
|
||||
self.assert_visible(cert_widget_sel)
|
||||
|
||||
@@ -626,11 +623,8 @@ class test_service(sevice_tasks):
|
||||
if not self.has_ca():
|
||||
self.skip('CA is not configured')
|
||||
|
||||
csr_path = self.config.get('service_csr_path')
|
||||
if not csr_path:
|
||||
self.skip('CSR file is not configured')
|
||||
|
||||
csr = self.load_file(csr_path)
|
||||
hostname = self.config.get('ipa_server')
|
||||
csr = generate_csr(hostname)
|
||||
|
||||
self.init_app()
|
||||
pkey = self.get_service_pkey('cifs')
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
Trust tests
|
||||
"""
|
||||
|
||||
import ipatests.test_webui.data_group as group
|
||||
import ipatests.test_webui.data_idviews as idview
|
||||
from ipatests.test_webui.ui_driver import UI_driver
|
||||
from ipatests.test_webui.ui_driver import screenshot
|
||||
from ipatests.test_webui.task_range import range_tasks
|
||||
@@ -29,6 +31,8 @@ import pytest
|
||||
ENTITY = 'trust'
|
||||
CONFIG_ENTITY = 'trustconfig'
|
||||
|
||||
DEFAULT_TRUST_VIEW = 'Default Trust View'
|
||||
|
||||
CONFIG_DATA = {
|
||||
'mod': [
|
||||
['combobox', 'ipantfallbackprimarygroup', 'admins'],
|
||||
@@ -45,6 +49,10 @@ CONFIG_DATA2 = {
|
||||
@pytest.mark.tier1
|
||||
class trust_tasks(UI_driver):
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def trusttasks_setup(self, ui_driver_fsetup):
|
||||
pass
|
||||
|
||||
def get_data(self, add_data=None):
|
||||
|
||||
domain = self.config.get('ad_domain')
|
||||
@@ -98,8 +106,10 @@ class trust_tasks(UI_driver):
|
||||
@pytest.mark.tier1
|
||||
class test_trust(trust_tasks):
|
||||
|
||||
def setup(self, *args, **kwargs):
|
||||
super(test_trust, self).setup(*args, **kwargs)
|
||||
request_timeout = 120
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def trust_setup(self, trusttasks_setup):
|
||||
if not self.has_trusts():
|
||||
self.skip('Trusts not configured')
|
||||
|
||||
@@ -127,9 +137,9 @@ class test_trust(trust_tasks):
|
||||
r_tasks.driver = self.driver
|
||||
r_tasks.config = self.config
|
||||
r_tasks.get_shifts()
|
||||
range_add = r_tasks.get_add_data('')
|
||||
base_id = range_add[2][2]
|
||||
range_size = range_add[3][2]
|
||||
range_form = r_tasks.get_add_form_data('')
|
||||
base_id = range_form.base_id
|
||||
range_size = range_form.size
|
||||
range_pkey = self.get_range_name()
|
||||
column = 'iparangetype'
|
||||
|
||||
@@ -143,7 +153,6 @@ class test_trust(trust_tasks):
|
||||
self.assert_record_value('Active Directory domain range', range_pkey, column)
|
||||
self.delete_record(range_pkey)
|
||||
|
||||
self.request_timeout = 60
|
||||
add = self.get_add_data('ipa-ad-trust-posix', base_id, range_size)
|
||||
data = self.get_data(add_data=add)
|
||||
self.add_record(ENTITY, data, delete=True)
|
||||
@@ -159,3 +168,40 @@ class test_trust(trust_tasks):
|
||||
|
||||
self.mod_record(CONFIG_ENTITY, CONFIG_DATA)
|
||||
self.mod_record(CONFIG_ENTITY, CONFIG_DATA2)
|
||||
|
||||
@screenshot
|
||||
def test_group_member_idoverrideuser(self):
|
||||
|
||||
self.init_app()
|
||||
|
||||
# Create new trust
|
||||
data = self.get_data()
|
||||
self.add_record(ENTITY, data)
|
||||
|
||||
# Create an user ID override
|
||||
ad_domain = self.config.get('ad_domain')
|
||||
ad_admin = self.config.get('ad_admin')
|
||||
idoverrideuser_pkey = '{}@{}'.format(ad_admin, ad_domain).lower()
|
||||
|
||||
self.navigate_to_record(DEFAULT_TRUST_VIEW, entity=idview.ENTITY)
|
||||
self.add_record(idview.ENTITY, {
|
||||
'pkey': idoverrideuser_pkey,
|
||||
'add': [
|
||||
('textbox', 'ipaanchoruuid_default', idoverrideuser_pkey),
|
||||
],
|
||||
}, facet='idoverrideuser')
|
||||
|
||||
# Create new group and add the user ID override there
|
||||
self.navigate_to_entity(group.ENTITY)
|
||||
self.add_record(group.ENTITY, group.DATA)
|
||||
self.navigate_to_record(group.PKEY)
|
||||
self.add_associations([idoverrideuser_pkey],
|
||||
facet='member_idoverrideuser', delete=True)
|
||||
|
||||
# Clean up data
|
||||
self.navigate_to_entity(group.ENTITY)
|
||||
self.delete_record(group.PKEY)
|
||||
self.navigate_to_record(DEFAULT_TRUST_VIEW, entity=idview.ENTITY)
|
||||
self.delete_record(idoverrideuser_pkey)
|
||||
self.navigate_to_entity(ENTITY)
|
||||
self.delete_record(ad_domain)
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
User tests
|
||||
"""
|
||||
|
||||
from ipatests.test_webui.crypto_utils import generate_csr
|
||||
from ipatests.test_webui.ui_driver import UI_driver
|
||||
from ipatests.test_webui.ui_driver import screenshot
|
||||
import ipatests.test_webui.data_user as user
|
||||
@@ -57,6 +58,7 @@ LONG_LOGIN = "invalid 'login': can be at most 32 characters"
|
||||
INV_PASSWD = ("invalid 'password': Leading and trailing spaces are "
|
||||
"not allowed")
|
||||
|
||||
|
||||
@pytest.mark.tier1
|
||||
class user_tasks(UI_driver):
|
||||
def load_file(self, path):
|
||||
@@ -122,11 +124,16 @@ class test_user(user_tasks):
|
||||
self.navigate_to_entity(user.ENTITY)
|
||||
self.navigate_to_record(user.PKEY)
|
||||
|
||||
self.add_associations([group.PKEY, 'editors'], facet='memberof_group', delete=True)
|
||||
self.add_associations([netgroup.PKEY], facet='memberof_netgroup', delete=True)
|
||||
self.add_associations([rbac.ROLE_PKEY], facet='memberof_role', delete=True)
|
||||
self.add_associations([hbac.RULE_PKEY], facet='memberof_hbacrule', delete=True)
|
||||
self.add_associations([sudo.RULE_PKEY], facet='memberof_sudorule', delete=True)
|
||||
self.add_associations([group.PKEY, 'editors'],
|
||||
facet='memberof_group', delete=True)
|
||||
self.add_associations([netgroup.PKEY],
|
||||
facet='memberof_netgroup', delete=True)
|
||||
self.add_associations([rbac.ROLE_PKEY],
|
||||
facet='memberof_role', delete=True)
|
||||
self.add_associations([hbac.RULE_PKEY],
|
||||
facet='memberof_hbacrule', delete=True)
|
||||
self.add_associations([sudo.RULE_PKEY],
|
||||
facet='memberof_sudorule', delete=True)
|
||||
|
||||
# cleanup
|
||||
# -------
|
||||
@@ -178,13 +185,17 @@ class test_user(user_tasks):
|
||||
self.navigate_to_record(user.PKEY)
|
||||
|
||||
self.assert_indirect_record(group.PKEY2, user.ENTITY, 'memberof_group')
|
||||
self.assert_indirect_record(netgroup.PKEY, user.ENTITY, 'memberof_netgroup')
|
||||
self.assert_indirect_record(rbac.ROLE_PKEY, user.ENTITY, 'memberof_role')
|
||||
self.assert_indirect_record(hbac.RULE_PKEY, user.ENTITY, 'memberof_hbacrule')
|
||||
self.assert_indirect_record(sudo.RULE_PKEY, user.ENTITY, 'memberof_sudorule')
|
||||
self.assert_indirect_record(netgroup.PKEY,
|
||||
user.ENTITY, 'memberof_netgroup')
|
||||
self.assert_indirect_record(rbac.ROLE_PKEY,
|
||||
user.ENTITY, 'memberof_role')
|
||||
self.assert_indirect_record(hbac.RULE_PKEY,
|
||||
user.ENTITY, 'memberof_hbacrule')
|
||||
self.assert_indirect_record(sudo.RULE_PKEY,
|
||||
user.ENTITY, 'memberof_sudorule')
|
||||
|
||||
## cleanup
|
||||
## -------
|
||||
# cleanup
|
||||
# -------
|
||||
self.delete(user.ENTITY, [user.DATA])
|
||||
self.delete(group.ENTITY, [group.DATA, group.DATA2])
|
||||
self.delete(netgroup.ENTITY, [netgroup.DATA])
|
||||
@@ -210,7 +221,8 @@ class test_user(user_tasks):
|
||||
self.reset_password_action(pwd)
|
||||
self.assert_text_field('has_password', '******')
|
||||
|
||||
self.action_list_action('unlock')
|
||||
# unlock option should be disabled for new user
|
||||
self.assert_action_list_action('unlock', enabled=False)
|
||||
|
||||
# delete
|
||||
self.delete_action(user.ENTITY, user.PKEY, action='delete_active_user')
|
||||
@@ -220,20 +232,13 @@ class test_user(user_tasks):
|
||||
"""
|
||||
Test user certificate actions
|
||||
|
||||
Requires to have CA installed and 'user_csr_path' configuration option
|
||||
set.
|
||||
Requires to have CA installed.
|
||||
"""
|
||||
|
||||
if not self.has_ca():
|
||||
self.skip('CA is not configured')
|
||||
|
||||
csr_path = self.config.get('user_csr_path')
|
||||
if not csr_path:
|
||||
self.skip('CSR file is not configured')
|
||||
|
||||
self.init_app()
|
||||
# ENHANCEMENT: generate csr dynamically
|
||||
csr = self.load_file(csr_path)
|
||||
cert_widget_sel = "div.certificate-widget"
|
||||
|
||||
self.add_record(user.ENTITY, user.DATA)
|
||||
@@ -242,7 +247,10 @@ class test_user(user_tasks):
|
||||
self.navigate_to_record(user.PKEY)
|
||||
|
||||
# cert request
|
||||
csr = generate_csr(user.PKEY, False)
|
||||
|
||||
self.action_list_action('request_cert', confirm=False)
|
||||
self.wait(seconds=2)
|
||||
self.assert_dialog()
|
||||
self.fill_text("textarea[name='csr']", csr)
|
||||
self.dialog_button_click('issue')
|
||||
@@ -355,7 +363,7 @@ class test_user(user_tasks):
|
||||
self.navigate_to_record(user.PKEY, entity=user.ENTITY)
|
||||
self.reset_password_action(pwd)
|
||||
|
||||
#re-login as new user
|
||||
# re-login as new user
|
||||
self.logout()
|
||||
self.init_app(user.PKEY, pwd)
|
||||
|
||||
@@ -653,6 +661,29 @@ class test_user(user_tasks):
|
||||
self.delete_record([user.PKEY, user.PKEY2, user.PKEY_NO_LOGIN,
|
||||
'nsurname10'])
|
||||
|
||||
@screenshot
|
||||
def test_menu_click_minimized_window(self):
|
||||
"""
|
||||
Test if menu is clickable when there is notification
|
||||
in minimized browser window.
|
||||
|
||||
related: https://pagure.io/freeipa/issue/8120
|
||||
"""
|
||||
self.init_app()
|
||||
|
||||
self.driver.set_window_size(570, 600)
|
||||
self.add_record(user.ENTITY, user.DATA2, negative=True)
|
||||
self.assert_notification(assert_text=USR_ADDED)
|
||||
menu_button = self.find('.navbar-toggle', By.CSS_SELECTOR)
|
||||
menu_button.click()
|
||||
self.assert_record(user.PKEY2)
|
||||
self.close_notifications()
|
||||
self.driver.maximize_window()
|
||||
|
||||
# cleanup
|
||||
self.delete(user.ENTITY, [user.DATA2])
|
||||
|
||||
|
||||
@pytest.mark.tier1
|
||||
class test_user_no_private_group(UI_driver):
|
||||
|
||||
|
||||
@@ -32,6 +32,10 @@ import pytest
|
||||
@pytest.mark.tier1
|
||||
class vault_tasks(UI_driver):
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def vault_tasks_setup(self, ui_driver_fsetup):
|
||||
pass
|
||||
|
||||
def prep_service_data(self):
|
||||
|
||||
host = self.config.get('ipa_server')
|
||||
@@ -64,8 +68,8 @@ class vault_tasks(UI_driver):
|
||||
@pytest.mark.tier1
|
||||
class test_vault(vault_tasks):
|
||||
|
||||
def setup(self, *args, **kwargs):
|
||||
super(test_vault, self).setup(*args, **kwargs)
|
||||
@pytest.fixture(autouse=True)
|
||||
def vault_setup(self, vault_tasks_setup):
|
||||
if not self.has_kra():
|
||||
self.skip('KRA not configured')
|
||||
|
||||
|
||||
@@ -28,25 +28,26 @@ from datetime import datetime
|
||||
import time
|
||||
import re
|
||||
import os
|
||||
from functools import wraps
|
||||
import unittest
|
||||
import paramiko
|
||||
|
||||
# pylint: disable=import-error
|
||||
from six.moves.urllib.error import URLError
|
||||
# pylint: enable=import-error
|
||||
from functools import wraps
|
||||
from urllib.error import URLError
|
||||
|
||||
import pytest
|
||||
|
||||
try:
|
||||
from selenium import webdriver
|
||||
from selenium.common.exceptions import NoSuchElementException
|
||||
from selenium.common.exceptions import InvalidElementStateException
|
||||
from selenium.common.exceptions import StaleElementReferenceException
|
||||
from selenium.common.exceptions import UnexpectedAlertPresentException
|
||||
from selenium.common.exceptions import WebDriverException
|
||||
from selenium.common.exceptions import ElementClickInterceptedException
|
||||
from selenium.webdriver.common.action_chains import ActionChains
|
||||
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.chrome.options import Options as ChromeOptions
|
||||
from selenium.webdriver.support.expected_conditions import alert_is_present
|
||||
from selenium.webdriver.support.wait import WebDriverWait
|
||||
from selenium.webdriver.support.ui import Select
|
||||
NO_SELENIUM = False
|
||||
@@ -57,8 +58,12 @@ try:
|
||||
NO_YAML = False
|
||||
except ImportError:
|
||||
NO_YAML = True
|
||||
|
||||
from ipaplatform.paths import paths
|
||||
|
||||
from ipatests.pytest_ipa.integration import tasks
|
||||
|
||||
|
||||
ENV_MAP = {
|
||||
'MASTER': 'ipa_server',
|
||||
'ADMINID': 'ipa_admin',
|
||||
@@ -95,11 +100,9 @@ def screenshot(fn):
|
||||
Should be applied on methods of UI_driver subclasses
|
||||
"""
|
||||
@wraps(fn)
|
||||
def screenshot_wrapper(*args):
|
||||
def screenshot_wrapper(*args, **kwargs):
|
||||
try:
|
||||
return fn(*args)
|
||||
except unittest.SkipTest:
|
||||
raise
|
||||
return fn(*args, **kwargs)
|
||||
except Exception:
|
||||
self = args[0]
|
||||
name = '%s_%s_%s' % (
|
||||
@@ -112,26 +115,68 @@ def screenshot(fn):
|
||||
return screenshot_wrapper
|
||||
|
||||
|
||||
class UI_driver(object):
|
||||
def dismiss_unexpected_alert(fn):
|
||||
"""
|
||||
Temporary fix for UnexpectedAlertPresentException.
|
||||
It is regression in Firefox 55
|
||||
Fixed in Firefox 65:
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1503015
|
||||
"""
|
||||
@wraps(fn)
|
||||
def wrapped(*args, **kwargs):
|
||||
self = args[0]
|
||||
try:
|
||||
return fn(*args, **kwargs)
|
||||
except UnexpectedAlertPresentException:
|
||||
if alert_is_present()(self.driver):
|
||||
self.driver.switch_to.alert.dismiss()
|
||||
# One retry is enough for now.
|
||||
# But in the case of catching two alerts at the same time
|
||||
# loop or recursive call should be used.
|
||||
return fn(*args, **kwargs)
|
||||
return wrapped
|
||||
|
||||
|
||||
def repeat_on_stale_parent_reference(fn):
|
||||
"""
|
||||
The decorator repeats a function once when StaleElementReferenceException
|
||||
is caught.
|
||||
It is not applicable if a parent reference is created outside a function.
|
||||
"""
|
||||
@wraps(fn)
|
||||
def wrapped(*args, **kwargs):
|
||||
if ('parent' in kwargs) and kwargs['parent'] is None:
|
||||
try:
|
||||
return fn(*args, **kwargs)
|
||||
except StaleElementReferenceException:
|
||||
pass
|
||||
return fn(*args, **kwargs)
|
||||
return wrapped
|
||||
|
||||
|
||||
class UI_driver:
|
||||
"""
|
||||
Base class for all UI integration tests
|
||||
"""
|
||||
|
||||
request_timeout = 60
|
||||
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
@pytest.fixture(autouse=True, scope="class")
|
||||
def ui_driver_setup(self, request):
|
||||
cls = request.cls
|
||||
if NO_SELENIUM:
|
||||
raise unittest.SkipTest('Selenium not installed')
|
||||
pytest.skip('Selenium not installed')
|
||||
cls.load_config()
|
||||
|
||||
def setup(self):
|
||||
@pytest.fixture(autouse=True)
|
||||
def ui_driver_fsetup(self, request):
|
||||
self.driver = self.get_driver()
|
||||
self.driver.maximize_window()
|
||||
|
||||
def teardown(self):
|
||||
self.driver.delete_all_cookies()
|
||||
self.driver.quit()
|
||||
def fin():
|
||||
self.driver.delete_all_cookies()
|
||||
self.driver.quit()
|
||||
request.addfinalizer(fin)
|
||||
|
||||
@classmethod
|
||||
def load_config(cls):
|
||||
@@ -149,9 +194,9 @@ class UI_driver(object):
|
||||
with open(path, 'r') as conf:
|
||||
cls.config = yaml.load(conf)
|
||||
except yaml.YAMLError as e:
|
||||
raise unittest.SkipTest("Invalid Web UI config.\n%s" % e)
|
||||
pytest.skip("Invalid Web UI config.\n%s" % e)
|
||||
except IOError as e:
|
||||
raise unittest.SkipTest(
|
||||
pytest.skip(
|
||||
"Can't load Web UI test config: %s" % e
|
||||
)
|
||||
else:
|
||||
@@ -190,7 +235,7 @@ class UI_driver(object):
|
||||
|
||||
if driver_type == 'remote':
|
||||
if 'host' not in cls.config:
|
||||
raise unittest.SkipTest('Selenium server host not configured')
|
||||
pytest.skip('Selenium server host not configured')
|
||||
host = cls.config["host"]
|
||||
|
||||
if browser == 'chrome':
|
||||
@@ -206,11 +251,11 @@ class UI_driver(object):
|
||||
command_executor='http://%s:%d/wd/hub' % (host, port),
|
||||
desired_capabilities=capabilities)
|
||||
except URLError as e:
|
||||
raise unittest.SkipTest(
|
||||
pytest.skip(
|
||||
'Error connecting to selenium server: %s' % e
|
||||
)
|
||||
except RuntimeError as e:
|
||||
raise unittest.SkipTest(
|
||||
pytest.skip(
|
||||
'Error while establishing webdriver: %s' % e
|
||||
)
|
||||
else:
|
||||
@@ -226,16 +271,17 @@ class UI_driver(object):
|
||||
ff_log_path = cls.config.get("geckodriver_log_path")
|
||||
driver = webdriver.Firefox(fp, log_path=ff_log_path)
|
||||
except URLError as e:
|
||||
raise unittest.SkipTest(
|
||||
pytest.skip(
|
||||
'Error connecting to selenium server: %s' % e
|
||||
)
|
||||
except RuntimeError as e:
|
||||
raise unittest.SkipTest(
|
||||
pytest.skip(
|
||||
'Error while establishing webdriver: %s' % e
|
||||
)
|
||||
|
||||
return driver
|
||||
|
||||
@dismiss_unexpected_alert
|
||||
def find(self, expression, by='id', context=None, many=False, strict=False):
|
||||
"""
|
||||
Helper which calls selenium find_element_by_xxx methods.
|
||||
@@ -270,6 +316,10 @@ class UI_driver(object):
|
||||
|
||||
return result
|
||||
|
||||
def find_by_selector(self, expression, context=None, **kwargs):
|
||||
return self.find(expression, by=By.CSS_SELECTOR, context=context,
|
||||
**kwargs)
|
||||
|
||||
def files_loaded(self):
|
||||
"""
|
||||
Test if dependencies were loaded. (Checks if UI has been rendered)
|
||||
@@ -333,6 +383,19 @@ class UI_driver(object):
|
||||
self.wait()
|
||||
self.wait(d)
|
||||
|
||||
def wait_while_working(self, widget, implicit=0.2):
|
||||
"""
|
||||
Wait while working widget active
|
||||
"""
|
||||
|
||||
working_widget = self.find('.working-widget', By.CSS_SELECTOR, widget)
|
||||
|
||||
self.wait(implicit)
|
||||
WebDriverWait(self.driver, self.request_timeout).until_not(
|
||||
lambda d: working_widget.is_displayed()
|
||||
)
|
||||
self.wait(0.5)
|
||||
|
||||
def xpath_has_val(self, attr, val):
|
||||
"""
|
||||
Create xpath expression for matching a presence of item in attribute
|
||||
@@ -453,7 +516,7 @@ class UI_driver(object):
|
||||
parent = parts[0:-1]
|
||||
self.navigate_by_menu('/'.join(parent), complete)
|
||||
|
||||
s = ".navbar a[href='#%s']" % item
|
||||
s = ".navbar li[data-name='%s'] a" % item
|
||||
link = self.find(s, By.CSS_SELECTOR, strict=True)
|
||||
assert link.is_displayed(), 'Navigation link is not displayed: %s' % item
|
||||
link.click()
|
||||
@@ -687,9 +750,16 @@ class UI_driver(object):
|
||||
|
||||
def _button_click(self, selector, parent, name=''):
|
||||
btn = self.find(selector, By.CSS_SELECTOR, parent, strict=True)
|
||||
|
||||
# The small timeout (up to 5 seconds) allows to prevent exceptions when
|
||||
# driver attempts to click a button before it is rendered.
|
||||
WebDriverWait(self.driver, 5, 0.2).until(
|
||||
lambda d: btn.is_displayed(),
|
||||
'Button is not displayed: %s' % (name or selector)
|
||||
)
|
||||
self.move_to_element_in_page(btn)
|
||||
|
||||
disabled = btn.get_attribute("disabled")
|
||||
assert btn.is_displayed(), 'Button is not displayed: %s' % name
|
||||
assert not disabled, 'Invalid button state: disabled. Button: %s' % name
|
||||
btn.click()
|
||||
self.wait_for_request()
|
||||
@@ -769,7 +839,8 @@ class UI_driver(object):
|
||||
parent = self.get_form()
|
||||
tb = self.find(selector, By.CSS_SELECTOR, parent, strict=True)
|
||||
try:
|
||||
tb.clear()
|
||||
tb.send_keys(Keys.CONTROL + 'a')
|
||||
tb.send_keys(Keys.DELETE)
|
||||
tb.send_keys(value)
|
||||
except InvalidElementStateException as e:
|
||||
msg = "Invalid Element State, el: %s, value: %s, error: %s" % (selector, value, e)
|
||||
@@ -807,6 +878,11 @@ class UI_driver(object):
|
||||
parent = self.get_form()
|
||||
self.fill_text(search_field_s, value, parent)
|
||||
|
||||
def apply_search_filter(self, value):
|
||||
self.fill_search_filter(value)
|
||||
actions = ActionChains(self.driver)
|
||||
actions.send_keys(Keys.ENTER).perform()
|
||||
|
||||
def add_multivalued(self, name, value, parent=None):
|
||||
"""
|
||||
Add new value to multivalued textbox
|
||||
@@ -955,7 +1031,8 @@ class UI_driver(object):
|
||||
self.wait_for_request()
|
||||
|
||||
list_cnt = self.find('.combobox-widget-list', By.CSS_SELECTOR, cb, strict=True)
|
||||
opt_s = "select[name=list] option[value='%s']" % value
|
||||
opt_s = 'select[name=list] option'
|
||||
opt_s += "[value='%s']" % value if value else ':not([value])'
|
||||
option = self.find(opt_s, By.CSS_SELECTOR, cb)
|
||||
|
||||
if combobox_input:
|
||||
@@ -1138,6 +1215,7 @@ class UI_driver(object):
|
||||
val = el.text
|
||||
return val
|
||||
|
||||
@repeat_on_stale_parent_reference
|
||||
def has_record(self, pkey, parent=None, table_name=None):
|
||||
"""
|
||||
Check if table contains specific record.
|
||||
@@ -1541,7 +1619,7 @@ class UI_driver(object):
|
||||
self.delete_record(pkey, data.get('del'))
|
||||
self.close_notifications()
|
||||
|
||||
def add_table_record(self, name, data, parent=None):
|
||||
def add_table_record(self, name, data, parent=None, add_another=False):
|
||||
"""
|
||||
Add record to dnsrecord table, association table and similar
|
||||
"""
|
||||
@@ -1554,9 +1632,25 @@ class UI_driver(object):
|
||||
btn.click()
|
||||
self.wait()
|
||||
self.fill_fields(data['fields'])
|
||||
self.dialog_button_click('add')
|
||||
|
||||
if not add_another:
|
||||
self.dialog_button_click('add')
|
||||
self.wait_for_request()
|
||||
|
||||
def add_another_table_record(self, data, add_another=False):
|
||||
"""
|
||||
Add table record after creating previous one in the same dialog
|
||||
TODO: Create class to manipulate such type of dialog
|
||||
"""
|
||||
self.dialog_button_click('add_and_add_another')
|
||||
self.wait_for_request()
|
||||
|
||||
self.fill_fields(data['fields'])
|
||||
|
||||
if not add_another:
|
||||
self.dialog_button_click('add')
|
||||
self.wait_for_request()
|
||||
|
||||
def prepare_associations(
|
||||
self, pkeys, facet=None, facet_btn='add', member_pkeys=None,
|
||||
confirm_btn='add', search=False):
|
||||
@@ -1884,20 +1978,13 @@ class UI_driver(object):
|
||||
hostname = self.config.get('ipa_server')
|
||||
password = self.config.get('ipa_password')
|
||||
|
||||
try:
|
||||
ssh = paramiko.SSHClient()
|
||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
ssh.connect(hostname=hostname, username=login, password=password)
|
||||
ssh.exec_command(cmd)
|
||||
except paramiko.AuthenticationException:
|
||||
self.skip('Authentication to server {} failed'.format(hostname))
|
||||
except paramiko.SSHException as e:
|
||||
self.skip('Unable to establish SSH connection: {}'.format(e))
|
||||
except Exception as e:
|
||||
self.skip('Unable to proceed: {}'.format(e))
|
||||
finally:
|
||||
ssh.close()
|
||||
tasks.run_ssh_cmd(
|
||||
to_host=hostname, username=login,
|
||||
auth_method="password", password=password,
|
||||
cmd=cmd
|
||||
)
|
||||
|
||||
@dismiss_unexpected_alert
|
||||
def has_class(self, el, cls):
|
||||
"""
|
||||
Check if el has CSS class
|
||||
@@ -1905,11 +1992,23 @@ class UI_driver(object):
|
||||
class_attr = el.get_attribute("class")
|
||||
return bool(class_attr) and cls in class_attr.split()
|
||||
|
||||
def has_form_error(self, name):
|
||||
"""
|
||||
Check if form field has error
|
||||
TODO: Move to some mixin class
|
||||
"""
|
||||
form_group = self.find(
|
||||
'//input[@name="{}"]/ancestor'
|
||||
'::div[contains(@class, "form-group")]'.format(name),
|
||||
By.XPATH
|
||||
)
|
||||
return self.has_class(form_group, 'has-error')
|
||||
|
||||
def skip(self, reason):
|
||||
"""
|
||||
Skip tests
|
||||
"""
|
||||
raise unittest.SkipTest(reason)
|
||||
pytest.skip(reason)
|
||||
|
||||
def assert_text(self, selector, value, parent=None):
|
||||
"""
|
||||
@@ -2001,7 +2100,7 @@ class UI_driver(object):
|
||||
Assert that current facet is correct
|
||||
"""
|
||||
info = self.get_facet_info()
|
||||
if not facet is None:
|
||||
if facet is not None:
|
||||
assert info["name"] == facet, "Invalid facet. Expected: %s, Got: %s " % (facet, info["name"])
|
||||
assert info["entity"] == entity, "Invalid entity. Expected: %s, Got: %s " % (entity, info["entity"])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user