Imported Upstream version 4.8.10

This commit is contained in:
Mario Fetka
2021-10-03 11:06:28 +02:00
parent 10dfc9587b
commit 03a8170b15
2361 changed files with 1883897 additions and 338759 deletions

View File

@@ -0,0 +1,47 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR Red Hat
# This file is distributed under the same license as the ipa package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: ipa\n"
"Report-Msgid-Bugs-To: https://fedorahosted.org/freeipa/newticket\n"
"POT-Creation-Date: 2016-08-29 10:39+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
#: ipaclient/frontend.py:28 ipaclient/frontend.py:85
#: ipaserver/plugins/baseldap.py:52
msgid "Failed members"
msgstr ""
#: ipaclient/frontend.py:32 ipaserver/plugins/baseldap.py:169
msgid "Failed source hosts/hostgroups"
msgstr ""
#: ipaclient/frontend.py:36 ipaserver/plugins/baseldap.py:172
msgid "Failed hosts/hostgroups"
msgstr ""
#: ipaclient/frontend.py:40 ipaserver/plugins/baseldap.py:175
msgid "Failed users/groups"
msgstr ""
#: ipaclient/plugins/dns.py:249
#, python-format
msgid ""
"%(count)d %(type)s record skipped. Only one value per DNS record type can be "
"modified at one time."
msgid_plural ""
"%(count)d %(type)s records skipped. Only one value per DNS record type can "
"be modified at one time."
msgstr[0] ""
msgstr[1] ""

View File

@@ -21,13 +21,18 @@
"""
Test the `ipalib.aci` module.
"""
from __future__ import print_function
from ipalib.aci import ACI
import pytest
pytestmark = pytest.mark.tier0
def check_aci_parsing(source, expected):
a = ACI(source)
print 'ACI was: ', a
print 'Expected:', expected
print('ACI was: ', a)
print('Expected:', expected)
assert str(ACI(source)) == expected
def test_aci_parsing_1():
@@ -40,7 +45,7 @@ def test_aci_parsing_1_with_aci_keyword():
def test_aci_parsing_2():
check_aci_parsing('(target="ldap:///uid=bjensen,dc=example,dc=com")(targetattr=*) (version 3.0;acl "aci1";allow (write) userdn="ldap:///self";)',
'(targetattr = "*")(target = "ldap:///uid=bjensen,dc=example,dc=com")(version 3.0;acl "aci1";allow (write) userdn = "ldap:///self";)')
'(target = "ldap:///uid=bjensen,dc=example,dc=com")(targetattr = "*")(version 3.0;acl "aci1";allow (write) userdn = "ldap:///self";)')
def test_aci_parsing_3():
check_aci_parsing(' (targetattr = "givenName || sn || cn || displayName || title || initials || loginShell || gecos || homePhone || mobile || pager || facsimileTelephoneNumber || telephoneNumber || street || roomNumber || l || st || postalCode || manager || secretary || description || carLicense || labeledURI || inetUserHTTPURL || seeAlso || employeeType || businessCategory || ou")(version 3.0;acl "Self service";allow (write) userdn = "ldap:///self";)',
@@ -52,11 +57,11 @@ def test_aci_parsing_4():
def test_aci_parsing_5():
check_aci_parsing('(targetattr=member)(target="ldap:///cn=ipausers,cn=groups,cn=accounts,dc=example,dc=com")(version 3.0;acl "add_user_to_default_group";allow (write) groupdn="ldap:///cn=add_user_to_default_group,cn=taskgroups,dc=example,dc=com";)',
'(targetattr = "member")(target = "ldap:///cn=ipausers,cn=groups,cn=accounts,dc=example,dc=com")(version 3.0;acl "add_user_to_default_group";allow (write) groupdn = "ldap:///cn=add_user_to_default_group,cn=taskgroups,dc=example,dc=com";)')
'(target = "ldap:///cn=ipausers,cn=groups,cn=accounts,dc=example,dc=com")(targetattr = "member")(version 3.0;acl "add_user_to_default_group";allow (write) groupdn = "ldap:///cn=add_user_to_default_group,cn=taskgroups,dc=example,dc=com";)')
def test_aci_parsing_6():
check_aci_parsing('(targetattr!=member)(targe="ldap:///cn=ipausers,cn=groups,cn=accounts,dc=example,dc=com")(version 3.0;acl "add_user_to_default_group";allow (write) groupdn="ldap:///cn=add_user_to_default_group,cn=taskgroups,dc=example,dc=com";)',
'(targetattr != "member")(targe = "ldap:///cn=ipausers,cn=groups,cn=accounts,dc=example,dc=com")(version 3.0;acl "add_user_to_default_group";allow (write) groupdn = "ldap:///cn=add_user_to_default_group,cn=taskgroups,dc=example,dc=com";)')
'(targe = "ldap:///cn=ipausers,cn=groups,cn=accounts,dc=example,dc=com")(targetattr != "member")(version 3.0;acl "add_user_to_default_group";allow (write) groupdn = "ldap:///cn=add_user_to_default_group,cn=taskgroups,dc=example,dc=com";)')
def test_aci_parsing_7():
check_aci_parsing('(targetattr = "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory")(version 3.0; acl "change_password"; allow (write) groupdn = "ldap:///cn=change_password,cn=taskgroups,dc=example,dc=com";)',
@@ -76,7 +81,7 @@ def make_test_aci():
def test_aci_equality():
a = make_test_aci()
print a
print(a)
b = ACI()
b.name ="foo"
@@ -85,17 +90,17 @@ def test_aci_equality():
b.set_bindrule_operator("=")
b.set_bindrule_expression("\"ldap:///cn=foo,cn=groups,cn=accounts,dc=example,dc=com\"")
b.permissions = ['add','read','write']
print b
print(b)
assert a.isequal(b)
assert a == b
assert not a != b
assert not a != b # pylint: disable=unneeded-not
def check_aci_inequality(b):
a = make_test_aci()
print a
print b
print(a)
print(b)
assert not a.isequal(b)
assert not a == b
@@ -157,3 +162,15 @@ def test_aci_parsing_8():
def test_aci_parsing_9():
check_aci_parsing('(targetfilter = "(|(objectClass=person)(objectClass=krbPrincipalAux)(objectClass=posixAccount)(objectClass=groupOfNames)(objectClass=posixGroup))")(targetattr != "aci || userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory")(version 3.0; acl "Account Admins can manage Users and Groups"; allow (add, delete, read, write) groupdn = "ldap:///cn=admins,cn=groups,cn=accounts,dc=greyoak,dc=com";)',
'(targetattr != "aci || userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory")(targetfilter = "(|(objectClass=person)(objectClass=krbPrincipalAux)(objectClass=posixAccount)(objectClass=groupOfNames)(objectClass=posixGroup))")(version 3.0;acl "Account Admins can manage Users and Groups";allow (add,delete,read,write) groupdn = "ldap:///cn=admins,cn=groups,cn=accounts,dc=greyoak,dc=com";)')
def test_aci_parsing_10():
"""test subtypes"""
check_aci_parsing('(targetattr="ipaProtectedOperation;read_keys")'
'(version 3.0; acl "Allow trust agents to retrieve '
'keytab keys for cross realm principals"; allow(read) '
'userattr="ipaAllowedToPerform;read_keys#GROUPDN";)',
'(targetattr = "ipaProtectedOperation;read || keys")'
'(version 3.0;acl "Allow trust agents to retrieve '
'keytab keys for cross realm principals";allow (read) '
'userattr = "ipaAllowedToPerform;read_keys#GROUPDN";)')

View File

@@ -20,16 +20,19 @@
"""
Test the `ipalib.backend` module.
"""
from __future__ import print_function
import threading
from ipatests.util import ClassChecker, raises, create_test_api
from ipatests.data import unicode_str
from ipalib.request import context, Connection
from ipalib.frontend import Command
from ipalib import backend, plugable, errors, base
from ipalib import backend, plugable, errors
from ipapython.version import API_VERSION
import pytest
pytestmark = pytest.mark.tier0
class test_Backend(ClassChecker):
"""
@@ -42,7 +45,7 @@ class test_Backend(ClassChecker):
assert self.cls.__bases__ == (plugable.Plugin,)
class Disconnect(object):
class Disconnect:
called = False
def __init__(self, id=None):
@@ -67,12 +70,13 @@ class test_Connectible(ClassChecker):
Test the `ipalib.backend.Connectible.connect` method.
"""
# Test that connection is created:
api = 'the api instance'
class example(self.cls):
def create_connection(self, *args, **kw):
object.__setattr__(self, 'args', args)
object.__setattr__(self, 'kw', kw)
return 'The connection.'
o = example()
o = example(api, shared_instance=True)
args = ('Arg1', 'Arg2', 'Arg3')
kw = dict(key1='Val1', key2='Val2', key3='Val3')
assert not hasattr(context, 'example')
@@ -84,10 +88,11 @@ class test_Connectible(ClassChecker):
assert conn.conn == 'The connection.'
assert conn.disconnect == o.disconnect
# Test that StandardError is raised if already connected:
m = "connect: 'context.%s' already exists in thread %r"
e = raises(StandardError, o.connect, *args, **kw)
assert str(e) == m % ('example', threading.currentThread().getName())
# Test that Exception is raised if already connected:
m = "{0} is already connected ({1} in {2})"
e = raises(Exception, o.connect, *args, **kw)
assert str(e) == m.format(
'example', o.id, threading.currentThread().getName())
# Double check that it works after deleting context.example:
del context.example
@@ -97,10 +102,11 @@ class test_Connectible(ClassChecker):
"""
Test the `ipalib.backend.Connectible.create_connection` method.
"""
api = 'the api instance'
class example(self.cls):
pass
for klass in (self.cls, example):
o = klass()
o = klass(api, shared_instance=True)
e = raises(NotImplementedError, o.create_connection)
assert str(e) == '%s.create_connection()' % klass.__name__
@@ -108,13 +114,15 @@ class test_Connectible(ClassChecker):
"""
Test the `ipalib.backend.Connectible.disconnect` method.
"""
api = 'the api instance'
class example(self.cls):
destroy_connection = Disconnect()
o = example()
o = example(api, shared_instance=True)
m = "disconnect: 'context.%s' does not exist in thread %r"
e = raises(StandardError, o.disconnect)
assert str(e) == m % ('example', threading.currentThread().getName())
m = "{0} is not connected ({1} in {2})"
e = raises(Exception, o.disconnect)
assert str(e) == m.format(
'example', o.id, threading.currentThread().getName())
context.example = 'The connection.'
assert o.disconnect() is None
@@ -124,10 +132,11 @@ class test_Connectible(ClassChecker):
"""
Test the `ipalib.backend.Connectible.destroy_connection` method.
"""
api = 'the api instance'
class example(self.cls):
pass
for klass in (self.cls, example):
o = klass()
o = klass(api, shared_instance=True)
e = raises(NotImplementedError, o.destroy_connection)
assert str(e) == '%s.destroy_connection()' % klass.__name__
@@ -135,10 +144,11 @@ class test_Connectible(ClassChecker):
"""
Test the `ipalib.backend.Connectible.isconnected` method.
"""
api = 'the api instance'
class example(self.cls):
pass
for klass in (self.cls, example):
o = klass()
o = klass(api, shared_instance=True)
assert o.isconnected() is False
conn = 'whatever'
setattr(context, klass.__name__, conn)
@@ -149,14 +159,15 @@ class test_Connectible(ClassChecker):
"""
Test the `ipalib.backend.Connectible.conn` property.
"""
msg = 'no context.%s in thread %r'
api = 'the api instance'
msg = '{0} is not connected ({1} in {2})'
class example(self.cls):
pass
for klass in (self.cls, example):
o = klass()
o = klass(api, shared_instance=True)
e = raises(AttributeError, getattr, o, 'conn')
assert str(e) == msg % (
klass.__name__, threading.currentThread().getName()
assert str(e) == msg.format(
klass.__name__, o.id, threading.currentThread().getName()
)
conn = Connection('The connection.', Disconnect())
setattr(context, klass.__name__, conn)
@@ -174,7 +185,7 @@ class test_Executioner(ClassChecker):
"""
Test the `ipalib.backend.Executioner.execute` method.
"""
(api, home) = create_test_api(in_server=True)
api, _home = create_test_api(in_server=True)
class echo(Command):
takes_args = ('arg1', 'arg2+')
@@ -182,7 +193,7 @@ class test_Executioner(ClassChecker):
def execute(self, *args, **options):
assert type(args[1]) is tuple
return dict(result=args + (options,))
api.register(echo)
api.add_plugin(echo)
class good(Command):
def execute(self, **options):
@@ -190,12 +201,12 @@ class test_Executioner(ClassChecker):
name='nurse',
error=u'Not naughty!',
)
api.register(good)
api.add_plugin(good)
class bad(Command):
def execute(self, **options):
raise ValueError('This is private.')
api.register(bad)
api.add_plugin(bad)
class with_name(Command):
"""
@@ -204,22 +215,21 @@ class test_Executioner(ClassChecker):
takes_options = 'name'
def execute(self, **options):
return dict(result=options['name'].upper())
api.register(with_name)
api.add_plugin(with_name)
api.finalize()
o = self.cls()
o.set_api(api)
o = self.cls(api)
o.finalize()
# Test that CommandError is raised:
conn = Connection('The connection.', Disconnect('someconn'))
context.someconn = conn
print str(context.__dict__.keys())
print(str(list(context.__dict__)))
e = raises(errors.CommandError, o.execute, 'nope')
assert e.name == 'nope'
assert conn.disconnect.called is True # Make sure destroy_context() was called
print str(context.__dict__.keys())
assert context.__dict__.keys() == []
print(str(list(context.__dict__)))
assert list(context.__dict__) == []
# Test with echo command:
arg1 = unicode_str
@@ -230,15 +240,15 @@ class test_Executioner(ClassChecker):
conn = Connection('The connection.', Disconnect('someconn'))
context.someconn = conn
print o.execute('echo', arg1, arg2, **options)
print dict(
print(o.execute('echo', arg1, arg2, **options))
print(dict(
result=(arg1, arg2, options)
)
))
assert o.execute('echo', arg1, arg2, **options) == dict(
result=(arg1, arg2, options)
)
assert conn.disconnect.called is True # Make sure destroy_context() was called
assert context.__dict__.keys() == []
assert list(context.__dict__) == []
conn = Connection('The connection.', Disconnect('someconn'))
context.someconn = conn
@@ -246,7 +256,7 @@ class test_Executioner(ClassChecker):
result=(arg1, arg2, options)
)
assert conn.disconnect.called is True # Make sure destroy_context() was called
assert context.__dict__.keys() == []
assert list(context.__dict__) == []
# Test with good command:
conn = Connection('The connection.', Disconnect('someconn'))
@@ -255,14 +265,14 @@ class test_Executioner(ClassChecker):
assert e.name == 'nurse'
assert e.error == u'Not naughty!'
assert conn.disconnect.called is True # Make sure destroy_context() was called
assert context.__dict__.keys() == []
assert list(context.__dict__) == []
# Test with bad command:
conn = Connection('The connection.', Disconnect('someconn'))
context.someconn = conn
e = raises(errors.InternalError, o.execute, 'bad')
assert conn.disconnect.called is True # Make sure destroy_context() was called
assert context.__dict__.keys() == []
assert list(context.__dict__) == []
# Test with option 'name':
conn = Connection('The connection.', Disconnect('someconn'))

View File

@@ -21,11 +21,20 @@
Test the `ipalib.base` module.
"""
import six
import pytest
from ipatests.util import ClassChecker, raises
from ipalib.constants import NAME_REGEX, NAME_ERROR
from ipalib.constants import TYPE_ERROR, SET_ERROR, DEL_ERROR, OVERRIDE_ERROR
from ipalib import base
if six.PY3:
unicode = str
pytestmark = pytest.mark.tier0
class test_ReadOnly(ClassChecker):
"""
@@ -99,7 +108,7 @@ def test_lock():
assert str(e) == 'already locked: %r' % o
# Test with another class implemented locking protocol:
class Lockable(object):
class Lockable:
__locked = False
def __lock__(self):
self.__locked = True
@@ -113,7 +122,7 @@ def test_lock():
assert str(e) == 'already locked: %r' % o
# Test with a class incorrectly implementing the locking protocol:
class Broken(object):
class Broken:
def __lock__(self):
pass
def __islocked__(self):
@@ -136,7 +145,7 @@ def test_islocked():
assert f(o) is True
# Test with another class implemented locking protocol:
class Lockable(object):
class Lockable:
__locked = False
def __lock__(self):
self.__locked = True
@@ -148,7 +157,7 @@ def test_islocked():
assert f(o) is True
# Test with a class incorrectly implementing the locking protocol:
class Broken(object):
class Broken:
__lock__ = False
def __islocked__(self):
return False
@@ -178,8 +187,14 @@ def test_check_name():
]
for name in okay:
assert name is f(name)
e = raises(TypeError, f, unicode(name))
assert str(e) == TYPE_ERROR % ('name', str, unicode(name), unicode)
if six.PY2:
bad_type = unicode
bad_value = unicode(name)
else:
bad_type = bytes
bad_value = name.encode('ascii')
e = raises(TypeError, f, bad_value)
assert str(e) == TYPE_ERROR % ('name', str, bad_value, bad_type)
for name in nope:
e = raises(ValueError, f, name)
assert str(e) == NAME_ERROR % (NAME_REGEX, name)
@@ -192,7 +207,7 @@ def membername(i):
return 'member%03d' % i
class DummyMember(object):
class DummyMember:
def __init__(self, i):
self.i = i
self.name = self.__name__ = membername(i)
@@ -209,7 +224,7 @@ class test_NameSpace(ClassChecker):
_cls = base.NameSpace
def new(self, count, sort=True):
members = tuple(DummyMember(i) for i in xrange(count, 0, -1))
members = tuple(DummyMember(i) for i in range(count, 0, -1))
assert len(members) == count
o = self.cls(members, sort=sort)
return (o, members)
@@ -248,9 +263,9 @@ class test_NameSpace(ClassChecker):
Test the `ipalib.base.NameSpace.__len__` method.
"""
for count in (5, 18, 127):
(o, members) = self.new(count)
o, _members = self.new(count)
assert len(o) == count
(o, members) = self.new(count, sort=False)
o, _members = self.new(count, sort=False)
assert len(o) == count
def test_iter(self):
@@ -305,12 +320,12 @@ class test_NameSpace(ClassChecker):
e = raises(KeyError, o.__getitem__, 'nope')
# Test int indexes:
for i in xrange(cnt):
for i in range(cnt):
assert o[i] is members[i]
e = raises(IndexError, o.__getitem__, cnt)
# Test negative int indexes:
for i in xrange(1, cnt + 1):
for i in range(1, cnt + 1):
assert o[-i] is members[-i]
e = raises(IndexError, o.__getitem__, -(cnt + 1))
@@ -338,7 +353,7 @@ class test_NameSpace(ClassChecker):
"""
for cnt in (0, 1, 2):
for sort in (True, False):
(o, members) = self.new(cnt, sort=sort)
o, _members = self.new(cnt, sort=sort)
if cnt == 1:
assert repr(o) == \
'NameSpace(<%d member>, sort=%r)' % (cnt, sort)

View File

@@ -23,6 +23,9 @@ Test the `ipalib.errors` module.
from ipalib.capabilities import capabilities, client_has_capability
import pytest
pytestmark = pytest.mark.tier0
def test_client_has_capability():
assert capabilities['messages'] == u'2.52'

View File

@@ -21,9 +21,12 @@
Test the `ipalib.cli` module.
"""
from ipatests.util import raises, get_api, ClassChecker
from ipalib import cli, plugable, frontend, backend
from ipatests.util import raises, ClassChecker
from ipalib import cli, plugable
import pytest
pytestmark = pytest.mark.tier0
class test_textui(ClassChecker):
_cls = cli.textui
@@ -32,15 +35,16 @@ class test_textui(ClassChecker):
"""
Test the `ipalib.cli.textui.max_col_width` method.
"""
o = self.cls()
api = 'the api instance'
o = self.cls(api)
e = raises(TypeError, o.max_col_width, 'hello')
assert str(e) == 'rows: need %r or %r; got %r' % (list, tuple, 'hello')
rows = [
'hello',
'naughty',
'empathetic',
'nurse',
]
assert o.max_col_width(rows) == len('naughty')
assert o.max_col_width(rows) == len('empathetic')
rows = (
( 'a', 'bbb', 'ccccc'),
('aa', 'bbbb', 'cccccc'),
@@ -72,7 +76,7 @@ def get_cmd_name(i):
return 'cmd_%d' % i
class DummyCommand(object):
class DummyCommand:
def __init__(self, name):
self.__name = name
@@ -81,16 +85,16 @@ class DummyCommand(object):
name = property(__get_name)
class DummyAPI(object):
class DummyAPI:
def __init__(self, cnt):
self.__cmd = plugable.NameSpace(self.__cmd_iter(cnt))
self.__cmd = plugable.APINameSpace(self.__cmd_iter(cnt), DummyCommand)
def __get_cmd(self):
return self.__cmd
Command = property(__get_cmd)
def __cmd_iter(self, cnt):
for i in xrange(cnt):
for i in range(cnt):
yield DummyCommand(get_cmd_name(i))
def finalize(self):

View File

@@ -22,18 +22,20 @@
Test the `ipalib.config` module.
"""
import os
from os import path
import site
import sys
import socket
from ipatests.util import raises, setitem, delitem, ClassChecker
from ipatests.util import getitem, setitem, delitem
from ipatests.util import raises, delitem, ClassChecker
from ipatests.util import getitem
from ipatests.util import TempDir, TempHome
from ipalib.constants import TYPE_ERROR, OVERRIDE_ERROR, SET_ERROR, DEL_ERROR
from ipalib.constants import OVERRIDE_ERROR, SET_ERROR, DEL_ERROR
from ipalib.constants import NAME_REGEX, NAME_ERROR
from ipalib import config, constants, base
from ipaplatform.paths import paths
import pytest
pytestmark = pytest.mark.tier0
# Valid environment variables in (key, raw, value) tuples:
# key: the name of the environment variable
@@ -166,7 +168,7 @@ class test_Env(ClassChecker):
assert o.__islocked__() is False
o.__lock__()
assert o.__islocked__() is True
e = raises(StandardError, o.__lock__)
e = raises(Exception, o.__lock__)
assert str(e) == 'Env.__lock__() already called'
# Also test with base.lock() function:
@@ -298,7 +300,7 @@ class test_Env(ClassChecker):
"""
o = self.cls()
assert len(o) == 0
for i in xrange(1, 11):
for i in range(1, 11):
key = 'key%d' % i
value = u'value %d' % i
o[key] = value
@@ -345,7 +347,7 @@ class test_Env(ClassChecker):
expected.update(dict(group1))
assert list(o) == sorted(expected)
assert expected['key2'] == 'value 2' # And not 'Value 2'
for (key, value) in expected.iteritems():
for (key, value) in expected.items():
assert getattr(o, key) is value
assert o[key] is value
assert o._merge(**expected) == (0, 6)
@@ -390,7 +392,7 @@ class test_Env(ClassChecker):
for (k, v) in orig.items():
assert o[k] is v
assert list(o) == sorted(keys + ('key0', 'key1', 'key2', 'key3', 'config_loaded'))
for i in xrange(4):
for i in range(4):
assert o['key%d' % i] == ('var%d' % i)
keys = tuple(o)
@@ -429,7 +431,7 @@ class test_Env(ClassChecker):
assert o._isdone('_bootstrap') is False
o._bootstrap(**overrides)
assert o._isdone('_bootstrap') is True
e = raises(StandardError, o._bootstrap)
e = raises(Exception, o._bootstrap)
assert str(e) == 'Env._bootstrap() already called'
return (o, home)
@@ -447,26 +449,42 @@ class test_Env(ClassChecker):
assert o.bin == path.dirname(path.abspath(sys.argv[0]))
assert o.home == home.path
assert o.dot_ipa == home.join('.ipa')
assert o.in_tree is False
assert o.context == 'default'
assert o.confdir == paths.ETC_IPA
assert o.conf == paths.IPA_DEFAULT_CONF
assert o.conf_default == o.conf
if (
# venv site module doesn't have getsitepackages()
not hasattr(site, "getsitepackages")
or o.site_packages in site.getsitepackages()
):
assert o.in_tree is False
assert o.confdir == '/etc/ipa'
assert o.conf == '/etc/ipa/default.conf'
assert o.conf_default == o.conf
else:
assert o.in_tree is True
assert o.confdir == o.dot_ipa
assert o.conf == home.join('.ipa/default.conf')
assert o.conf_default == o.conf
# Test overriding values created by _bootstrap()
(o, home) = self.bootstrap(in_tree='True', context='server')
assert o.in_tree is True
assert o.context == 'server'
assert o.conf == home.join('.ipa', 'server.conf')
(o, home) = self.bootstrap(conf='/my/wacky/whatever.conf')
o, home = self.bootstrap(
conf='/my/wacky/whatever.conf', in_tree=False
)
assert o.in_tree is False
assert o.context == 'default'
assert o.conf == '/my/wacky/whatever.conf'
assert o.conf_default == paths.IPA_DEFAULT_CONF
(o, home) = self.bootstrap(conf_default='/my/wacky/default.conf')
assert o.conf_default == '/etc/ipa/default.conf'
o, home = self.bootstrap(
conf_default='/my/wacky/default.conf', in_tree=False
)
assert o.in_tree is False
assert o.context == 'default'
assert o.conf == paths.IPA_DEFAULT_CONF
assert o.conf == '/etc/ipa/default.conf'
assert o.conf_default == '/my/wacky/default.conf'
# Test various overrides and types conversion
@@ -512,7 +530,7 @@ class test_Env(ClassChecker):
assert key in o
# Check that it can't be called twice:
e = raises(StandardError, o._finalize_core)
e = raises(Exception, o._finalize_core)
assert str(e) == 'Env._finalize_core() already called'
return (o, home)
@@ -563,7 +581,8 @@ class test_Env(ClassChecker):
# Test using DEFAULT_CONFIG:
defaults = dict(constants.DEFAULT_CONFIG)
(o, home) = self.finalize_core(None, **defaults)
assert list(o) == sorted(defaults)
list_o = [key for key in o if key != 'fips_mode']
assert list_o == sorted(defaults)
for (key, value) in defaults.items():
if value is object:
continue
@@ -576,7 +595,7 @@ class test_Env(ClassChecker):
Test the `ipalib.config.Env._finalize` method.
"""
# Check that calls cascade up the chain:
(o, home) = self.new(in_tree=True)
o, _home = self.new(in_tree=True)
assert o._isdone('_bootstrap') is False
assert o._isdone('_finalize_core') is False
assert o._isdone('_finalize') is False
@@ -586,19 +605,19 @@ class test_Env(ClassChecker):
assert o._isdone('_finalize') is True
# Check that it can't be called twice:
e = raises(StandardError, o._finalize)
e = raises(Exception, o._finalize)
assert str(e) == 'Env._finalize() already called'
# Check that _finalize() calls __lock__()
(o, home) = self.new(in_tree=True)
o, _home = self.new(in_tree=True)
assert o.__islocked__() is False
o._finalize()
assert o.__islocked__() is True
e = raises(StandardError, o.__lock__)
e = raises(Exception, o.__lock__)
assert str(e) == 'Env.__lock__() already called'
# Check that **lastchance works
(o, home) = self.finalize_core(None)
o, _home = self.finalize_core(None)
key = 'just_one_more_key'
value = u'with one more value'
lastchance = {key: value}

View File

@@ -21,10 +21,13 @@
Test the `ipalib.crud` module.
"""
from ipatests.util import read_only, raises, get_api, ClassChecker
from ipalib import crud, frontend, plugable, config
from ipatests.util import raises, get_api, ClassChecker
from ipalib import crud, frontend
from ipalib.parameters import Str
import pytest
pytestmark = pytest.mark.tier0
class CrudChecker(ClassChecker):
"""
@@ -35,7 +38,7 @@ class CrudChecker(ClassChecker):
"""
Return a finalized `ipalib.plugable.API` instance.
"""
(api, home) = get_api()
api, _home = get_api()
class user(frontend.Object):
takes_params = (
'givenname',
@@ -47,8 +50,8 @@ class CrudChecker(ClassChecker):
class user_verb(self.cls):
takes_args = args
takes_options = options
api.register(user)
api.register(user_verb)
api.add_plugin(user)
api.add_plugin(user_verb)
api.finalize()
return api
@@ -202,10 +205,11 @@ class test_CrudBackend(ClassChecker):
return ldap
def check_method(self, name, *args):
o = self.cls()
api = 'the api instance'
o = self.cls(api)
e = raises(NotImplementedError, getattr(o, name), *args)
assert str(e) == 'CrudBackend.%s()' % name
sub = self.subcls()
sub = self.subcls(api)
e = raises(NotImplementedError, getattr(sub, name), *args)
assert str(e) == 'ldap.%s()' % name

View File

@@ -21,40 +21,50 @@
Test the `ipalib.errors` module.
"""
# FIXME: Pylint errors
# pylint: disable=no-member
import re
import inspect
import pytest
import six
from ipatests.util import assert_equal, raises
from ipalib import errors, text
from ipaplatform.paths import paths
from ipalib import errors
from ipalib.constants import TYPE_ERROR
if six.PY3:
unicode = str
class PrivateExceptionTester(object):
pytestmark = pytest.mark.tier0
class PrivateExceptionTester:
_klass = None
__klass = None
def __get_klass(self):
if self.__klass is None:
self.__klass = self._klass
assert issubclass(self.__klass, StandardError)
assert issubclass(self.__klass, Exception)
assert issubclass(self.__klass, errors.PrivateError)
assert not issubclass(self.__klass, errors.PublicError)
return self.__klass
klass = property(__get_klass)
def new(self, **kw):
for (key, value) in kw.iteritems():
for (key, value) in kw.items():
assert not hasattr(self.klass, key), key
inst = self.klass(**kw)
assert isinstance(inst, StandardError)
assert isinstance(inst, Exception)
assert isinstance(inst, errors.PrivateError)
assert isinstance(inst, self.klass)
assert not isinstance(inst, errors.PublicError)
for (key, value) in kw.iteritems():
for (key, value) in kw.items():
assert getattr(inst, key) is value
assert str(inst) == self.klass.format % kw
assert inst.message == str(inst)
return inst
@@ -103,11 +113,11 @@ class test_SubprocessError(PrivateExceptionTester):
"""
Test the `ipalib.errors.SubprocessError.__init__` method.
"""
inst = self.new(returncode=1, argv=(paths.BIN_FALSE,))
bin_false = '/bin/false'
inst = self.new(returncode=1, argv=(bin_false,))
assert inst.returncode == 1
assert inst.argv == (paths.BIN_FALSE,)
assert str(inst) == "return code 1 from ('/bin/false',)"
assert inst.message == str(inst)
assert inst.argv == (bin_false,)
assert str(inst) == "return code 1 from ('{}',)".format(bin_false)
class test_PluginSubclassError(PrivateExceptionTester):
@@ -126,7 +136,6 @@ class test_PluginSubclassError(PrivateExceptionTester):
assert inst.bases == ('base1', 'base2')
assert str(inst) == \
"'bad' not subclass of any base in ('base1', 'base2')"
assert inst.message == str(inst)
class test_PluginDuplicateError(PrivateExceptionTester):
@@ -143,7 +152,6 @@ class test_PluginDuplicateError(PrivateExceptionTester):
inst = self.new(plugin='my_plugin')
assert inst.plugin == 'my_plugin'
assert str(inst) == "'my_plugin' was already registered"
assert inst.message == str(inst)
class test_PluginOverrideError(PrivateExceptionTester):
@@ -162,7 +170,6 @@ class test_PluginOverrideError(PrivateExceptionTester):
assert inst.name == 'cmd'
assert inst.plugin == 'my_cmd'
assert str(inst) == "unexpected override of Base.cmd with 'my_cmd'"
assert inst.message == str(inst)
class test_PluginMissingOverrideError(PrivateExceptionTester):
@@ -181,21 +188,19 @@ class test_PluginMissingOverrideError(PrivateExceptionTester):
assert inst.name == 'cmd'
assert inst.plugin == 'my_cmd'
assert str(inst) == "Base.cmd not registered, cannot override with 'my_cmd'"
assert inst.message == str(inst)
##############################################################################
# Unit tests for public errors:
class PublicExceptionTester(object):
class PublicExceptionTester:
_klass = None
__klass = None
def __get_klass(self):
if self.__klass is None:
self.__klass = self._klass
assert issubclass(self.__klass, StandardError)
assert issubclass(self.__klass, Exception)
assert issubclass(self.__klass, errors.PublicError)
assert not issubclass(self.__klass, errors.PrivateError)
assert type(self.__klass.errno) is int
@@ -205,18 +210,18 @@ class PublicExceptionTester(object):
def new(self, format=None, message=None, **kw):
# Test that TypeError is raised if message isn't unicode:
e = raises(TypeError, self.klass, message='The message')
assert str(e) == TYPE_ERROR % ('message', unicode, 'The message', str)
e = raises(TypeError, self.klass, message=b'The message')
assert str(e) == TYPE_ERROR % ('message', unicode, b'The message', bytes)
# Test the instance:
for (key, value) in kw.iteritems():
for (key, value) in kw.items():
assert not hasattr(self.klass, key), key
inst = self.klass(format=format, message=message, **kw)
for required_class in self.required_classes:
assert isinstance(inst, required_class)
assert isinstance(inst, self.klass)
assert not isinstance(inst, errors.PrivateError)
for (key, value) in kw.iteritems():
for (key, value) in kw.items():
assert getattr(inst, key) is value
return inst
@@ -226,20 +231,19 @@ class test_PublicError(PublicExceptionTester):
Test the `ipalib.errors.PublicError` exception.
"""
_klass = errors.PublicError
required_classes = StandardError, errors.PublicError
required_classes = Exception, errors.PublicError
def test_init(self):
message = u'The translated, interpolated message'
format = 'key=%(key1)r and key2=%(key2)r'
uformat = u'Translated key=%(key1)r and key2=%(key2)r'
val1 = 'Value 1'
val2 = 'Value 2'
val1 = u'Value 1'
val2 = u'Value 2'
kw = dict(key1=val1, key2=val2)
# Test with format=str, message=None
inst = self.klass(format, **kw)
assert inst.format is format
assert_equal(inst.message, format % kw)
assert_equal(str(inst), format % kw)
assert inst.forwarded is False
assert inst.key1 is val1
assert inst.key2 is val2
@@ -247,15 +251,15 @@ class test_PublicError(PublicExceptionTester):
# Test with format=None, message=unicode
inst = self.klass(message=message, **kw)
assert inst.format is None
assert inst.message is message
assert str(inst) == message
assert inst.strerror is message
assert inst.forwarded is True
assert inst.key1 is val1
assert inst.key2 is val2
# Test with format=None, message=str
e = raises(TypeError, self.klass, message='the message', **kw)
assert str(e) == TYPE_ERROR % ('message', unicode, 'the message', str)
# Test with format=None, message=bytes
e = raises(TypeError, self.klass, message=b'the message', **kw)
assert str(e) == TYPE_ERROR % ('message', unicode, b'the message', bytes)
# Test with format=None, message=None
e = raises(ValueError, self.klass, **kw)
@@ -270,7 +274,7 @@ class test_PublicError(PublicExceptionTester):
inst = self.new(format, **kw)
assert isinstance(inst, self.klass)
assert inst.format is format
assert_equal(inst.message, format % kw)
assert_equal(str(inst), format % kw)
assert inst.forwarded is False
assert inst.key1 is val1
assert inst.key2 is val2
@@ -279,7 +283,7 @@ class test_PublicError(PublicExceptionTester):
inst = self.new(message=message, **kw)
assert isinstance(inst, self.klass)
assert inst.format is None
assert inst.message is message
assert str(inst) == message
assert inst.strerror is message
assert inst.forwarded is True
assert inst.key1 is val1
@@ -291,8 +295,7 @@ class test_PublicError(PublicExceptionTester):
class subclass(self.klass):
format = '%(true)r %(text)r %(number)r'
uformat = u'Translated %(true)r %(text)r %(number)r'
kw = dict(true=True, text='Hello!', number=18)
kw = dict(true=True, text=u'Hello!', number=18)
# Test with format=str, message=None
e = raises(ValueError, subclass, format, **kw)
@@ -302,7 +305,7 @@ class test_PublicError(PublicExceptionTester):
# Test with format=None, message=None:
inst = subclass(**kw)
assert inst.format is subclass.format
assert_equal(inst.message, subclass.format % kw)
assert_equal(str(inst), subclass.format % kw)
assert inst.forwarded is False
assert inst.true is True
assert inst.text is kw['text']
@@ -311,7 +314,7 @@ class test_PublicError(PublicExceptionTester):
# Test with format=None, message=unicode:
inst = subclass(message=message, **kw)
assert inst.format is subclass.format
assert inst.message is message
assert str(inst) == message
assert inst.strerror is message
assert inst.forwarded is True
assert inst.true is True
@@ -326,17 +329,16 @@ class test_PublicError(PublicExceptionTester):
# this expression checks if each word of instructions
# exists in a string as a separate line, with right order
regexp = re.compile('(?ims).*' +
''.join(map(lambda x: '(%s).*' % (x),
instructions)) +
''.join('(%s).*' % (x) for x in instructions) +
'$')
inst = subclass(instructions=instructions, **kw)
assert inst.format is subclass.format
assert_equal(inst.instructions, instructions)
assert_equal(inst.instructions, unicode(instructions))
inst_match = regexp.match(inst.strerror).groups()
assert_equal(list(inst_match),list(instructions))
class BaseMessagesTest(object):
class BaseMessagesTest:
"""Generic test for all of a module's errors or messages
"""
def test_public_messages(self):
@@ -365,10 +367,10 @@ class BaseMessagesTest(object):
pass
class test_PublicErrors(object):
class test_PublicErrors:
message_list = errors.public_errors
errno_range = xrange(900, 5999)
required_classes = (StandardError, errors.PublicError)
errno_range = list(range(900, 5999))
required_classes = (Exception, errors.PublicError)
texts = errors._texts
def extratest(self, cls):

View File

@@ -21,8 +21,11 @@
Test the `ipalib.frontend` module.
"""
from ipatests.util import raises, getitem, no_set, no_del, read_only
from ipatests.util import check_TypeError, ClassChecker, create_test_api
import pytest
import six
from ipatests.util import raises, read_only
from ipatests.util import ClassChecker, create_test_api
from ipatests.util import assert_equal
from ipalib.constants import TYPE_ERROR
from ipalib.base import NameSpace
@@ -31,6 +34,13 @@ from ipalib import output, messages
from ipalib.parameters import Str
from ipapython.version import API_VERSION
if six.PY3:
unicode = str
pytestmark = pytest.mark.tier0
def test_RULE_FLAG():
assert frontend.RULE_FLAG == 'validation_rule'
@@ -59,7 +69,7 @@ def test_is_rule():
is_rule = frontend.is_rule
flag = frontend.RULE_FLAG
class no_call(object):
class no_call:
def __init__(self, value):
if value is not None:
assert value in (True, False)
@@ -86,31 +96,32 @@ class test_HasParam(ClassChecker):
"""
Test the `ipalib.frontend.HasParam._get_param_iterable` method.
"""
api = 'the api instance'
class WithTuple(self.cls):
takes_stuff = ('one', 'two')
o = WithTuple()
o = WithTuple(api)
assert o._get_param_iterable('stuff') is WithTuple.takes_stuff
junk = ('three', 'four')
class WithCallable(self.cls):
def takes_stuff(self):
return junk
o = WithCallable()
o = WithCallable(api)
assert o._get_param_iterable('stuff') is junk
class WithParam(self.cls):
takes_stuff = parameters.Str('five')
o = WithParam()
o = WithParam(api)
assert o._get_param_iterable('stuff') == (WithParam.takes_stuff,)
class WithStr(self.cls):
takes_stuff = 'six'
o = WithStr()
o = WithStr(api)
assert o._get_param_iterable('stuff') == ('six',)
class Wrong(self.cls):
takes_stuff = ['seven', 'eight']
o = Wrong()
o = Wrong(api)
e = raises(TypeError, o._get_param_iterable, 'stuff')
assert str(e) == '%s.%s must be a tuple, callable, or spec; got %r' % (
'Wrong', 'takes_stuff', Wrong.takes_stuff
@@ -120,6 +131,7 @@ class test_HasParam(ClassChecker):
"""
Test the `ipalib.frontend.HasParam._filter_param_by_context` method.
"""
api = 'the api instance'
class Example(self.cls):
def get_stuff(self):
return (
@@ -129,7 +141,7 @@ class test_HasParam(ClassChecker):
parameters.Str('four', exclude='server'),
parameters.Str('five', exclude=['whatever', 'cli']),
)
o = Example()
o = Example(api)
# Test when env is None:
params = list(o._filter_param_by_context('stuff'))
@@ -158,7 +170,7 @@ class test_HasParam(ClassChecker):
# Test with no get_stuff:
class Missing(self.cls):
pass
o = Missing()
o = Missing(api)
gen = o._filter_param_by_context('stuff')
e = raises(NotImplementedError, list, gen)
assert str(e) == 'Missing.get_stuff()'
@@ -166,7 +178,7 @@ class test_HasParam(ClassChecker):
# Test when get_stuff is not callable:
class NotCallable(self.cls):
get_stuff = ('one', 'two')
o = NotCallable()
o = NotCallable(api)
gen = o._filter_param_by_context('stuff')
e = raises(TypeError, list, gen)
assert str(e) == '%s.%s must be a callable; got %r' % (
@@ -185,13 +197,15 @@ class test_Command(ClassChecker):
"""
Return a standard subclass of `ipalib.frontend.Command`.
"""
class Rule(object):
class Rule:
def __init__(self, name):
self.name = name
def __call__(self, _, value):
if value != self.name:
return _('must equal %r') % self.name
else:
return None
default_from = parameters.DefaultFrom(
lambda arg: arg,
@@ -216,10 +230,14 @@ class test_Command(ClassChecker):
"""
Helper method used to test args and options.
"""
class api:
@staticmethod
def is_production_mode():
return False
class example(self.cls):
takes_args = args
takes_options = options
o = example()
o = example(api)
o.finalize()
return o
@@ -234,7 +252,8 @@ class test_Command(ClassChecker):
"""
Test the `ipalib.frontend.Command.get_args` method.
"""
assert list(self.cls().get_args()) == []
api = 'the api instance'
assert list(self.cls(api).get_args()) == []
args = ('login', 'stuff')
o = self.get_instance(args=args)
assert tuple(o.get_args()) == args
@@ -243,7 +262,8 @@ class test_Command(ClassChecker):
"""
Test the `ipalib.frontend.Command.get_options` method.
"""
options = list(self.cls().get_options())
api = 'the api instance'
options = list(self.cls(api).get_options())
assert len(options) == 1
assert options[0].name == 'version'
options = ('verbose', 'debug')
@@ -256,14 +276,17 @@ class test_Command(ClassChecker):
"""
Test the ``ipalib.frontend.Command.args`` instance attribute.
"""
assert self.cls().args is None
o = self.cls()
class api:
@staticmethod
def is_production_mode():
return False
o = self.cls(api)
o.finalize()
assert type(o.args) is plugable.NameSpace
assert type(o.args) is NameSpace
assert len(o.args) == 0
args = ('destination', 'source?')
ns = self.get_instance(args=args).args
assert type(ns) is plugable.NameSpace
assert type(ns) is NameSpace
assert len(ns) == len(args)
assert list(ns) == ['destination', 'source']
assert type(ns.destination) is parameters.Str
@@ -274,9 +297,14 @@ class test_Command(ClassChecker):
assert ns.source.multivalue is False
# Test TypeError:
e = raises(TypeError, self.get_instance, args=(u'whatever',))
assert str(e) == TYPE_ERROR % (
'spec', (str, parameters.Param), u'whatever', unicode)
if six.PY2:
e = raises(TypeError, self.get_instance, args=(u'whatever',))
assert str(e) == TYPE_ERROR % (
'spec', (str, parameters.Param), u'whatever', unicode)
else:
e = raises(TypeError, self.get_instance, args=(b'whatever',))
assert str(e) == TYPE_ERROR % (
'spec', (str, parameters.Param), b'whatever', bytes)
# Test ValueError, required after optional:
e = raises(ValueError, self.get_instance, args=('arg1?', 'arg2'))
@@ -305,14 +333,17 @@ class test_Command(ClassChecker):
"""
Test the ``ipalib.frontend.Command.options`` instance attribute.
"""
assert self.cls().options is None
o = self.cls()
class api:
@staticmethod
def is_production_mode():
return False
o = self.cls(api)
o.finalize()
assert type(o.options) is plugable.NameSpace
assert type(o.options) is NameSpace
assert len(o.options) == 1
options = ('target', 'files*')
ns = self.get_instance(options=options).options
assert type(ns) is plugable.NameSpace
assert type(ns) is NameSpace
assert len(ns) == len(options) + 1
assert list(ns) == ['target', 'files', 'version']
assert type(ns.target) is parameters.Str
@@ -326,10 +357,13 @@ class test_Command(ClassChecker):
"""
Test the ``ipalib.frontend.Command.output`` instance attribute.
"""
inst = self.cls()
assert inst.output is None
class api:
@staticmethod
def is_production_mode():
return False
inst = self.cls(api)
inst.finalize()
assert type(inst.output) is plugable.NameSpace
assert type(inst.output) is NameSpace
assert list(inst.output) == ['result']
assert type(inst.output.result) is output.Output
@@ -337,9 +371,10 @@ class test_Command(ClassChecker):
"""
Test the ``ipalib.frontend.Command._iter_output`` instance attribute.
"""
api = 'the api instance'
class Example(self.cls):
pass
inst = Example()
inst = Example(api)
inst.has_output = tuple()
assert list(inst._iter_output()) == []
@@ -366,54 +401,37 @@ class test_Command(ClassChecker):
for o in items:
assert type(o) is output.Output
def test_soft_validate(self):
"""
Test the `ipalib.frontend.Command.soft_validate` method.
"""
class user_add(frontend.Command):
takes_args = parameters.Str('uid',
normalizer=lambda value: value.lower(),
default_from=lambda givenname, sn: givenname[0] + sn,
)
takes_options = ('givenname', 'sn')
cmd = user_add()
cmd.env = config.Env(context='cli')
cmd.finalize()
assert list(cmd.params) == ['givenname', 'sn', 'uid', 'version']
ret = cmd.soft_validate({})
assert sorted(ret['values']) == ['version']
assert sorted(ret['errors']) == ['givenname', 'sn', 'uid']
assert cmd.soft_validate(dict(givenname=u'First', sn=u'Last')) == dict(
values=dict(givenname=u'First', sn=u'Last', uid=u'flast',
version=None),
errors=dict(),
)
def test_convert(self):
"""
Test the `ipalib.frontend.Command.convert` method.
"""
class api:
@staticmethod
def is_production_mode():
return False
kw = dict(
option0=u'1.5',
option1=u'7',
)
o = self.subcls()
o = self.subcls(api)
o.finalize()
for (key, value) in o.convert(**kw).iteritems():
for (key, value) in o.convert(**kw).items():
assert_equal(unicode(kw[key]), value)
def test_normalize(self):
"""
Test the `ipalib.frontend.Command.normalize` method.
"""
class api:
@staticmethod
def is_production_mode():
return False
kw = dict(
option0=u'OPTION0',
option1=u'OPTION1',
)
norm = dict((k, v.lower()) for (k, v) in kw.items())
sub = self.subcls()
sub = self.subcls(api)
sub.finalize()
assert sub.normalize(**kw) == norm
@@ -439,24 +457,26 @@ class test_Command(ClassChecker):
kw = dict(option0=u'some value')
(api, home) = create_test_api()
api, _home = create_test_api()
api.finalize()
o = my_cmd()
o.set_api(api)
o = my_cmd(api)
o.finalize()
e = o(**kw)
e = o.get_default(**kw) # pylint: disable=not-callable
assert type(e) is dict
assert 'result' in e
assert 'option2' in e['result']
assert e['result']['option2'] == u'some value'
assert 'option2' in e
assert e['option2'] == u'some value'
def test_validate(self):
"""
Test the `ipalib.frontend.Command.validate` method.
"""
class api:
env = config.Env(context='cli')
@staticmethod
def is_production_mode():
return False
sub = self.subcls()
sub.env = config.Env(context='cli')
sub = self.subcls(api)
sub.finalize()
# Check with valid values
@@ -472,11 +492,8 @@ class test_Command(ClassChecker):
fail = dict(okay)
fail['option0'] = u'whatever'
e = raises(errors.ValidationError, sub.validate, **fail)
assert_equal(e.name, 'option0')
assert_equal(e.value, u'whatever')
assert_equal(e.name, u'option0')
assert_equal(e.error, u"must equal 'option0'")
assert e.rule.__class__.__name__ == 'Rule'
assert e.index is None
# Check with a missing required arg
fail = dict(okay)
@@ -488,7 +505,8 @@ class test_Command(ClassChecker):
"""
Test the `ipalib.frontend.Command.execute` method.
"""
o = self.cls()
api = 'the api instance'
o = self.cls(api)
e = raises(NotImplementedError, o.execute)
assert str(e) == 'Command.execute()'
@@ -525,7 +543,7 @@ class test_Command(ClassChecker):
o = self.get_instance(args=('one', 'two'), options=('three', 'four'))
e = raises(errors.OverlapError, o.args_options_2_params,
1, 2, three=3, two=2, four=4, one=1)
assert e.names == ['one', 'two']
assert e.names == "['one', 'two']"
# Test the permutations:
o = self.get_instance(args=('one', 'two*'), options=('three', 'four'))
@@ -563,10 +581,9 @@ class test_Command(ClassChecker):
args = ('one', 'two')
kw = dict(three=('three1', 'three2'), four='four')
(api, home) = create_test_api()
api, _home = create_test_api()
api.finalize()
o = my_cmd()
o.set_api(api)
o = my_cmd(api)
o.finalize()
e = o.run(*args, **kw)
assert type(e) is dict
@@ -583,9 +600,9 @@ class test_Command(ClassChecker):
Test the `ipalib.frontend.Command.params_2_args_options` method.
"""
o = self.get_instance(args='one', options='two')
assert o.params_2_args_options() == ((None,), {})
assert o.params_2_args_options() == ((), {})
assert o.params_2_args_options(one=1) == ((1,), {})
assert o.params_2_args_options(two=2) == ((None,), dict(two=2))
assert o.params_2_args_options(two=2) == ((), dict(two=2))
assert o.params_2_args_options(two=2, one=1) == ((1,), dict(two=2))
def test_run(self):
@@ -603,20 +620,24 @@ class test_Command(ClassChecker):
kw = dict(how_are='you', on_this='fine day?', version=API_VERSION)
# Test in server context:
(api, home) = create_test_api(in_server=True)
api, _home = create_test_api(in_server=True)
api.finalize()
o = my_cmd()
o.set_api(api)
assert o.run.im_func is self.cls.run.im_func
o = my_cmd(api)
if six.PY2:
assert o.run.__func__ is self.cls.run.__func__
else:
assert o.run.__func__ is self.cls.run
out = o.run(*args, **kw)
assert ('execute', args, kw) == out
# Test in non-server context
(api, home) = create_test_api(in_server=False)
api, _home = create_test_api(in_server=False)
api.finalize()
o = my_cmd()
o.set_api(api)
assert o.run.im_func is self.cls.run.im_func
o = my_cmd(api)
if six.PY2:
assert o.run.__func__ is self.cls.run.__func__
else:
assert o.run.__func__ is self.cls.run
assert ('forward', args, kw) == o.run(*args, **kw)
def test_messages(self):
@@ -645,29 +666,37 @@ class test_Command(ClassChecker):
expected = [TestMessage().to_dict()]
# Test in server context:
(api, home) = create_test_api(in_server=True)
api, _home = create_test_api(in_server=True)
api.finalize()
o = my_cmd()
o.set_api(api)
assert o.run.im_func is self.cls.run.im_func
o = my_cmd(api)
if six.PY2:
assert o.run.__func__ is self.cls.run.__func__
else:
assert o.run.__func__ is self.cls.run
assert {'name': 'execute', 'messages': expected} == o.run(*args, **kw)
# Test in non-server context
(api, home) = create_test_api(in_server=False)
api, _home = create_test_api(in_server=False)
api.finalize()
o = my_cmd()
o.set_api(api)
assert o.run.im_func is self.cls.run.im_func
o = my_cmd(api)
if six.PY2:
assert o.run.__func__ is self.cls.run.__func__
else:
assert o.run.__func__ is self.cls.run
assert {'name': 'forward', 'messages': expected} == o.run(*args, **kw)
def test_validate_output_basic(self):
"""
Test the `ipalib.frontend.Command.validate_output` method.
"""
class api:
@staticmethod
def is_production_mode():
return False
class Example(self.cls):
has_output = ('foo', 'bar', 'baz')
inst = Example()
inst = Example(api)
inst.finalize()
# Test with wrong type:
@@ -702,24 +731,28 @@ class test_Command(ClassChecker):
"""
Test `ipalib.frontend.Command.validate_output` per-type validation.
"""
class api:
@staticmethod
def is_production_mode():
return False
class Complex(self.cls):
has_output = (
output.Output('foo', int),
output.Output('bar', list),
)
inst = Complex()
inst = Complex(api)
inst.finalize()
wrong = dict(foo=17.9, bar=[18])
e = raises(TypeError, inst.validate_output, wrong)
assert str(e) == '%s:\n output[%r]: need %r; got %r: %r' % (
assert str(e) == '%s:\n output[%r]: need (%r,); got %r: %r' % (
'Complex.validate_output()', 'foo', int, float, 17.9
)
wrong = dict(foo=18, bar=17)
e = raises(TypeError, inst.validate_output, wrong)
assert str(e) == '%s:\n output[%r]: need %r; got %r: %r' % (
assert str(e) == '%s:\n output[%r]: need (%r,); got %r: %r' % (
'Complex.validate_output()', 'bar', list, int, 17
)
@@ -727,6 +760,10 @@ class test_Command(ClassChecker):
"""
Test `ipalib.frontend.Command.validate_output` nested validation.
"""
class api:
@staticmethod
def is_production_mode():
return False
class Subclass(output.ListOfEntries):
pass
@@ -737,7 +774,7 @@ class test_Command(ClassChecker):
output.Output('hello', int),
Subclass('world'),
)
inst = nested()
inst = nested(api)
inst.finalize()
okay = dict(foo='bar')
nope = ('aye', 'bee')
@@ -758,6 +795,10 @@ class test_Command(ClassChecker):
"""
Test the `ipalib.frontend.Command.get_output_params` method.
"""
class api:
@staticmethod
def is_production_mode():
return False
class example(self.cls):
has_output_params = (
'one',
@@ -768,17 +809,16 @@ class test_Command(ClassChecker):
'foo',
)
takes_options = (
Str('bar', flags='no_output'),
Str('bar'),
'baz',
)
inst = example()
assert list(inst.get_output_params()) == ['one', 'two', 'three']
inst = example(api)
inst.finalize()
assert list(inst.get_output_params()) == [
'one', 'two', 'three', inst.params.foo, inst.params.baz
'one', 'two', 'three'
]
assert list(inst.output_params) == ['one', 'two', 'three', 'foo', 'baz']
assert list(inst.output_params) == ['one', 'two', 'three']
class test_LocalOrRemote(ClassChecker):
@@ -791,7 +831,11 @@ class test_LocalOrRemote(ClassChecker):
"""
Test the `ipalib.frontend.LocalOrRemote.__init__` method.
"""
o = self.cls()
class api:
@staticmethod
def is_production_mode():
return False
o = self.cls(api)
o.finalize()
assert list(o.args) == []
assert list(o.options) == ['server', 'version']
@@ -813,36 +857,36 @@ class test_LocalOrRemote(ClassChecker):
return dict(result=('execute', args, options))
# Test when in_server=False:
(api, home) = create_test_api(in_server=False)
api.register(example)
api, _home = create_test_api(in_server=False)
api.add_plugin(example)
api.finalize()
cmd = api.Command.example
assert cmd(version=u'2.47') == dict(
result=('execute', (None,), dict(version=u'2.47', server=False))
result=('execute', (), dict(version=u'2.47'))
)
assert cmd(u'var', version=u'2.47') == dict(
result=('execute', (u'var',), dict(version=u'2.47', server=False))
result=('execute', (u'var',), dict(version=u'2.47'))
)
assert cmd(server=True, version=u'2.47') == dict(
result=('forward', (None,), dict(version=u'2.47', server=True))
result=('forward', (), dict(version=u'2.47', server=True))
)
assert cmd(u'var', server=True, version=u'2.47') == dict(
result=('forward', (u'var',), dict(version=u'2.47', server=True))
)
# Test when in_server=True (should always call execute):
(api, home) = create_test_api(in_server=True)
api.register(example)
api, _home = create_test_api(in_server=True)
api.add_plugin(example)
api.finalize()
cmd = api.Command.example
assert cmd(version=u'2.47') == dict(
result=('execute', (None,), dict(version=u'2.47', server=False))
result=('execute', (), dict(version=u'2.47', server=False))
)
assert cmd(u'var', version=u'2.47') == dict(
result=('execute', (u'var',), dict(version=u'2.47', server=False))
)
assert cmd(server=True, version=u'2.47') == dict(
result=('execute', (None,), dict(version=u'2.47', server=True))
result=('execute', (), dict(version=u'2.47', server=True))
)
assert cmd(u'var', server=True, version=u'2.47') == dict(
result=('execute', (u'var',), dict(version=u'2.47', server=True))
@@ -869,18 +913,8 @@ class test_Object(ClassChecker):
"""
Test the `ipalib.frontend.Object.__init__` method.
"""
o = self.cls()
assert o.backend is None
assert o.methods is None
assert o.params is None
assert o.params_minus_pk is None
def test_set_api(self):
"""
Test the `ipalib.frontend.Object.set_api` method.
"""
# Setup for test:
class DummyAttribute(object):
class DummyAttribute:
def __init__(self, obj_name, attr_name, name=None):
self.obj_name = obj_name
self.attr_name = attr_name
@@ -888,6 +922,9 @@ class test_Object(ClassChecker):
self.name = '%s_%s' % (obj_name, attr_name)
else:
self.name = name
self.bases = (DummyAttribute,)
self.version = '1'
self.full_name = '{}/{}'.format(self.name, self.version)
self.param = frontend.create_param(attr_name)
def __clone__(self, attr_name):
@@ -899,33 +936,40 @@ class test_Object(ClassChecker):
def get_attributes(cnt, format):
for name in ['other', 'user', 'another']:
for i in xrange(cnt):
for i in range(cnt):
yield DummyAttribute(name, format % i)
cnt = 10
methods_format = 'method_%d'
_d = dict(
Method=plugable.NameSpace(
get_attributes(cnt, methods_format)
),
)
api = plugable.MagicDict(_d)
class FakeAPI:
def __init__(self):
self._API__plugins = get_attributes(cnt, methods_format)
self._API__default_map = {}
self.Method = plugable.APINameSpace(self, DummyAttribute)
def __contains__(self, key):
return hasattr(self, key)
def __getitem__(self, key):
return getattr(self, key)
def is_production_mode(self):
return False
def _get(self, plugin):
return plugin
api = FakeAPI()
assert len(api.Method) == cnt * 3
class user(self.cls):
pass
# Actually perform test:
o = user()
o.set_api(api)
o = user(api)
assert read_only(o, 'api') is api
namespace = o.methods
assert isinstance(namespace, plugable.NameSpace)
assert isinstance(namespace, NameSpace)
assert len(namespace) == cnt
f = methods_format
for i in xrange(cnt):
for i in range(cnt):
attr_name = f % i
attr = namespace[attr_name]
assert isinstance(attr, DummyAttribute)
@@ -935,17 +979,15 @@ class test_Object(ClassChecker):
assert attr.name == '%s_%s' % ('user', attr_name)
# Test params instance attribute
o = self.cls()
o.set_api(api)
o = self.cls(api)
ns = o.params
assert type(ns) is plugable.NameSpace
assert type(ns) is NameSpace
assert len(ns) == 0
class example(self.cls):
takes_params = ('banana', 'apple')
o = example()
o.set_api(api)
o = example(api)
ns = o.params
assert type(ns) is plugable.NameSpace
assert type(ns) is NameSpace
assert len(ns) == 2, repr(ns)
assert list(ns) == ['banana', 'apple']
for p in ns():
@@ -957,7 +999,7 @@ class test_Object(ClassChecker):
"""
Test the `ipalib.frontend.Object.primary_key` attribute.
"""
(api, home) = create_test_api()
api, _home = create_test_api()
api.finalize()
# Test with no primary keys:
@@ -966,8 +1008,7 @@ class test_Object(ClassChecker):
'one',
'two',
)
o = example1()
o.set_api(api)
o = example1(api)
assert o.primary_key is None
# Test with 1 primary key:
@@ -978,14 +1019,13 @@ class test_Object(ClassChecker):
parameters.Str('three', primary_key=True),
'four',
)
o = example2()
o.set_api(api)
o = example2(api)
pk = o.primary_key
assert type(pk) is parameters.Str
assert pk.name == 'three'
assert pk.primary_key is True
assert o.params[2] is o.primary_key
assert isinstance(o.params_minus_pk, plugable.NameSpace)
assert isinstance(o.params_minus_pk, NameSpace)
assert list(o.params_minus_pk) == ['one', 'two', 'four']
# Test with multiple primary_key:
@@ -996,8 +1036,7 @@ class test_Object(ClassChecker):
'three',
parameters.Str('four', primary_key=True),
)
o = example3()
o.set_api(api)
o = example3(api)
e = raises(ValueError, o.finalize)
assert str(e) == \
'example3 (Object) has multiple primary keys: one, two, four'
@@ -1006,13 +1045,13 @@ class test_Object(ClassChecker):
"""
Test the `ipalib.frontend.Object.backend` attribute.
"""
(api, home) = create_test_api()
api, _home = create_test_api()
class ldap(backend.Backend):
whatever = 'It worked!'
api.register(ldap)
api.add_plugin(ldap)
class user(frontend.Object):
backend_name = 'ldap'
api.register(user)
api.add_plugin(user)
api.finalize()
b = api.Object.user.backend
assert isinstance(b, ldap)
@@ -1022,12 +1061,13 @@ class test_Object(ClassChecker):
"""
Test the `ipalib.frontend.Object.get_dn` method.
"""
o = self.cls()
api = 'the api instance'
o = self.cls(api)
e = raises(NotImplementedError, o.get_dn, 'primary key')
assert str(e) == 'Object.get_dn()'
class user(self.cls):
pass
o = user()
o = user(api)
e = raises(NotImplementedError, o.get_dn, 'primary key')
assert str(e) == 'user.get_dn()'
@@ -1037,9 +1077,9 @@ class test_Object(ClassChecker):
"""
class example(self.cls):
takes_params = ('one', 'two', 'three', 'four')
o = example()
(api, home) = create_test_api()
o.set_api(api)
api, _home = create_test_api()
api.finalize()
o = example(api)
p = o.params
assert tuple(o.params_minus()) == tuple(p())
assert tuple(o.params_minus([])) == tuple(p())
@@ -1070,28 +1110,22 @@ class test_Attribute(ClassChecker):
"""
Test the `ipalib.frontend.Attribute.__init__` method.
"""
class user_add(self.cls):
pass
o = user_add()
assert read_only(o, 'obj') is None
assert read_only(o, 'obj_name') == 'user'
assert read_only(o, 'attr_name') == 'add'
def test_set_api(self):
"""
Test the `ipalib.frontend.Attribute.set_api` method.
"""
user_obj = 'The user frontend.Object instance'
class api(object):
Object = dict(user=user_obj)
class api:
Object = {("user", "1"): user_obj}
@staticmethod
def is_production_mode():
return False
class user_add(self.cls):
pass
o = user_add()
assert read_only(o, 'api') is None
assert read_only(o, 'obj') is None
o.set_api(api)
o = user_add(api)
assert read_only(o, 'api') is api
assert read_only(o, 'obj') is user_obj
assert read_only(o, 'obj_name') == 'user'
assert read_only(o, 'attr_name') == 'add'
class test_Method(ClassChecker):
@@ -1104,7 +1138,7 @@ class test_Method(ClassChecker):
"""
Return a finalized `ipalib.plugable.API` instance.
"""
(api, home) = create_test_api()
api, _home = create_test_api()
class user(frontend.Object):
takes_params = (
'givenname',
@@ -1115,8 +1149,8 @@ class test_Method(ClassChecker):
class user_verb(self.cls):
takes_args = args
takes_options = options
api.register(user)
api.register(user_verb)
api.add_plugin(user)
api.add_plugin(user_verb)
api.finalize()
return api
@@ -1130,9 +1164,10 @@ class test_Method(ClassChecker):
"""
Test the `ipalib.frontend.Method.__init__` method.
"""
api = 'the api instance'
class user_add(self.cls):
pass
o = user_add()
o = user_add(api)
assert o.name == 'user_add'
assert o.obj_name == 'user'
assert o.attr_name == 'add'

View File

@@ -25,6 +25,9 @@ from ipalib import messages
from ipalib.capabilities import capabilities
from ipatests.test_ipalib import test_errors
import pytest
pytestmark = pytest.mark.tier0
class HelloMessage(messages.PublicMessage):
type = 'info'
@@ -41,7 +44,7 @@ class test_PublicMessage(test_errors.test_PublicError):
class test_PublicMessages(test_errors.BaseMessagesTest):
message_list = messages.public_messages
errno_range = xrange(10000, 19999)
errno_range = list(range(10000, 19999))
required_classes = (UserWarning, messages.PublicMessage)
texts = messages._texts
@@ -52,10 +55,11 @@ class test_PublicMessages(test_errors.BaseMessagesTest):
def test_to_dict():
expected = dict(
name='HelloMessage',
type='info',
message='Hello, world!',
name=u'HelloMessage',
type=u'info',
message=u'Hello, world!',
code=1234,
data={'greeting': 'Hello', 'object': 'world'},
)
assert HelloMessage(greeting='Hello', object='world').to_dict() == expected
@@ -75,15 +79,17 @@ def test_add_message():
assert result == {'messages': [
dict(
name='HelloMessage',
type='info',
message='Hello, world!',
name=u'HelloMessage',
type=u'info',
message=u'Hello, world!',
code=1234,
data={'greeting': 'Hello', 'object': 'world'},
),
dict(
name='HelloMessage',
type='info',
message='Hi, version!',
name=u'HelloMessage',
type=u'info',
message=u'Hi, version!',
code=1234,
data={'greeting': 'Hi', 'object': 'version'},
)
]}

View File

@@ -24,9 +24,12 @@ Test the `ipalib.output` module.
from ipatests.util import raises, ClassChecker
from ipalib import output
from ipalib.frontend import Command
from ipalib import _
from ipapython.version import API_VERSION
import pytest
pytestmark = pytest.mark.tier0
class test_Output(ClassChecker):
"""
Test the `ipalib.output.Output` class.
@@ -48,16 +51,20 @@ class test_Output(ClassChecker):
Test the `ipalib.output.Output.__repr__` method.
"""
o = self.cls('aye')
assert repr(o) == "Output('aye', None, None)"
assert repr(o) == "Output('aye')"
o = self.cls('aye', type=int, doc='An A, aye?')
assert repr(o) == "Output('aye', %r, 'An A, aye?')" % int
assert repr(o) == (
"Output('aye', type=[<type 'int'>], doc='An A, aye?')"
)
class Entry(self.cls):
pass
o = Entry('aye')
assert repr(o) == "Entry('aye', None, None)"
assert repr(o) == "Entry('aye')"
o = Entry('aye', type=int, doc='An A, aye?')
assert repr(o) == "Entry('aye', %r, 'An A, aye?')" % int
assert repr(o) == (
"Entry('aye', type=[<type 'int'>], doc='An A, aye?')"
)
class test_ListOfEntries(ClassChecker):
@@ -71,9 +78,10 @@ class test_ListOfEntries(ClassChecker):
"""
Test the `ipalib.output.ListOfEntries.validate` method.
"""
api = 'the api instance'
class example(Command):
pass
cmd = example()
cmd = example(api)
inst = self.cls('stuff')
okay = dict(foo='bar')

View File

@@ -22,22 +22,39 @@
Test the `ipalib.parameters` module.
"""
# FIXME: Pylint errors
# pylint: disable=no-member
import base64
import datetime
import re
import sys
from types import NoneType
from decimal import Decimal
from inspect import isclass
from xmlrpc.client import MAXINT, MININT
import pytest
import six
from cryptography import x509 as crypto_x509
from cryptography.hazmat.backends import default_backend
from ipatests.util import raises, ClassChecker, read_only
from ipatests.util import dummy_ugettext, assert_equal
from ipatests.data import binary_bytes, utf8_bytes, unicode_str
from ipalib import parameters, text, errors, config
from ipalib import parameters, text, errors, config, x509
from ipalib.constants import TYPE_ERROR, CALLABLE_ERROR
from ipalib.errors import ValidationError, ConversionError
from ipalib import _
from xmlrpclib import MAXINT, MININT
NULLS = (None, '', u'', tuple(), [])
if six.PY3:
unicode = str
long = int
NULLS = (None, b'', u'', tuple(), [])
pytestmark = pytest.mark.tier0
class test_DefaultFrom(ClassChecker):
"""
@@ -85,18 +102,18 @@ class test_DefaultFrom(ClassChecker):
pass
o = self.cls(stuff)
assert repr(o) == "DefaultFrom(stuff, 'one', 'two')"
assert repr(o) == "DefaultFrom('one', 'two')"
o = self.cls(stuff, 'aye', 'bee', 'see')
assert repr(o) == "DefaultFrom(stuff, 'aye', 'bee', 'see')"
assert repr(o) == "DefaultFrom('aye', 'bee', 'see')"
cb = lambda first, last: first[0] + last
o = self.cls(cb)
assert repr(o) == "DefaultFrom(<lambda>, 'first', 'last')"
assert repr(o) == "DefaultFrom('first', 'last')"
o = self.cls(cb, 'aye', 'bee', 'see')
assert repr(o) == "DefaultFrom(<lambda>, 'aye', 'bee', 'see')"
assert repr(o) == "DefaultFrom('aye', 'bee', 'see')"
def test_call(self):
"""
@@ -147,11 +164,15 @@ def test_parse_param_spec():
assert f('name^') == ('name^', dict(required=True, multivalue=False))
# Test that TypeError is raised if spec isn't an str:
e = raises(TypeError, f, u'name?')
assert str(e) == TYPE_ERROR % ('spec', str, u'name?', unicode)
if six.PY2:
bad_value = u'name?'
else:
bad_value = b'name?'
e = raises(TypeError, f, bad_value)
assert str(e) == TYPE_ERROR % ('spec', str, bad_value, type(bad_value))
class DummyRule(object):
class DummyRule:
def __init__(self, error=None):
assert error is None or type(error) is unicode
self.error = error
@@ -205,7 +226,6 @@ class test_Param(ClassChecker):
assert o.exclude is None
assert o.flags == frozenset()
assert o.sortorder == 2
assert o.csv is False
# Test that doc defaults from label:
o = self.cls('my_param', doc=_('Hello world'))
@@ -219,12 +239,12 @@ class test_Param(ClassChecker):
# Test that ValueError is raised when a kwarg from a subclass
# conflicts with an attribute:
class Subclass(self.cls):
class Subclass1(self.cls):
kwargs = self.cls.kwargs + (
('convert', callable, None),
)
e = raises(ValueError, Subclass, name)
assert str(e) == "kwarg 'convert' conflicts with attribute on Subclass"
e = raises(ValueError, Subclass1, name)
assert str(e) == "kwarg 'convert' conflicts with attribute on Subclass1"
# Test type validation of keyword arguments:
class Subclass(self.cls):
@@ -235,7 +255,7 @@ class test_Param(ClassChecker):
('extra4', callable, lambda whatever: whatever + 7),
)
o = Subclass('my_param') # Test with no **kw:
for (key, kind, default) in o.kwargs:
for key, kind, _default in o.kwargs:
# Test with a type invalid for all:
value = object()
kw = {key: value}
@@ -271,10 +291,6 @@ class test_Param(ClassChecker):
'exclude', frozenset(['client', 'bar']),
)
# Test that ValueError is raised if csv is set and multivalue is not set:
e = raises(ValueError, self.cls, 'my_param', csv=True)
assert str(e) == '%s: cannot have csv without multivalue' % "Param('my_param')"
# Test that default_from gets set:
call = lambda first, last: first[0] + last
o = self.cls('my_param', default_from=call)
@@ -289,9 +305,9 @@ class test_Param(ClassChecker):
o = self.cls(name)
assert repr(o) == 'Param(%r)' % name
o = self.cls('name', required=False)
assert repr(o) == "Param('name', required=False)"
assert repr(o) == "Param('name?')"
o = self.cls('name', multivalue=True)
assert repr(o) == "Param('name', multivalue=True)"
assert repr(o) == "Param('name+')"
def test_use_in_context(self):
"""
@@ -339,18 +355,18 @@ class test_Param(ClassChecker):
assert clone is not orig
assert type(clone) is self.cls
assert clone.name is orig.name
for (key, kind, default) in self.cls.kwargs:
assert getattr(clone, key) is getattr(orig, key)
for key, _kind, _default in self.cls.kwargs:
assert getattr(clone, key) == getattr(orig, key)
# Test with a param spec:
orig = self.cls('my_param*')
assert orig.param_spec == 'my_param*'
clone = orig.clone()
assert clone.param_spec == 'my_param'
assert clone.param_spec == 'my_param*'
assert clone is not orig
assert type(clone) is self.cls
for (key, kind, default) in self.cls.kwargs:
assert getattr(clone, key) is getattr(orig, key)
for key, _kind, _default in self.cls.kwargs:
assert getattr(clone, key) == getattr(orig, key)
# Test with overrides:
orig = self.cls('my_param*')
@@ -361,7 +377,7 @@ class test_Param(ClassChecker):
assert type(clone) is self.cls
assert clone.required is True
assert clone.multivalue is True
assert clone.param_spec == 'my_param'
assert clone.param_spec == 'my_param+'
assert clone.name == 'my_param'
def test_clone_rename(self):
@@ -376,7 +392,9 @@ class test_Param(ClassChecker):
assert clone is not orig
assert type(clone) is self.cls
assert clone.name == new_name
for (key, kind, default) in self.cls.kwargs:
for key, _kind, _default in self.cls.kwargs:
if key in ('cli_name', 'label', 'doc', 'cli_metavar'):
continue
assert getattr(clone, key) is getattr(orig, key)
# Test with overrides:
@@ -388,7 +406,7 @@ class test_Param(ClassChecker):
assert type(clone) is self.cls
assert clone.required is True
assert clone.multivalue is True
assert clone.param_spec == new_name
assert clone.param_spec == "{0}+".format(new_name)
assert clone.name == new_name
@@ -433,7 +451,7 @@ class test_Param(ClassChecker):
assert o._convert_scalar(None) is None
assert dummy.called() is False
# Test with incorrect type
e = raises(errors.ConversionError, o._convert_scalar, 'hello', index=17)
raises(errors.ConversionError, o._convert_scalar, 'hello')
def test_validate(self):
"""
@@ -442,39 +460,33 @@ class test_Param(ClassChecker):
# Test in default state (with no rules, no kwarg):
o = self.cls('my_param')
e = raises(errors.RequirementError, o.validate, None, 'cli')
e = raises(errors.RequirementError, o.validate, None)
assert e.name == 'my_param'
# Test in default state that cli_name gets returned in the exception
# when context == 'cli'
o = self.cls('my_param', cli_name='short')
e = raises(errors.RequirementError, o.validate, None, 'cli')
assert e.name == 'short'
# Test with required=False
o = self.cls('my_param', required=False)
assert o.required is False
assert o.validate(None, 'cli') is None
assert o.validate(None) is None
# Test with query=True:
o = self.cls('my_param', query=True)
assert o.query is True
e = raises(errors.RequirementError, o.validate, None, 'cli')
assert_equal(e.name, 'my_param')
e = raises(errors.RequirementError, o.validate, None)
assert_equal(e.name, u'my_param')
# Test with multivalue=True:
o = self.cls('my_param', multivalue=True)
e = raises(TypeError, o.validate, [], 'cli')
e = raises(TypeError, o.validate, [])
assert str(e) == TYPE_ERROR % ('value', tuple, [], list)
e = raises(ValueError, o.validate, tuple(), 'cli')
e = raises(ValueError, o.validate, tuple())
assert str(e) == 'value: empty tuple must be converted to None'
# Test with wrong (scalar) type:
e = raises(TypeError, o.validate, (None, None, 42, None), 'cli')
assert str(e) == TYPE_ERROR % ('my_param', NoneType, 42, int)
e = raises(TypeError, o.validate, (None, None, 42, None))
assert str(e) == TYPE_ERROR % ('my_param', type(None), 42, int)
o = self.cls('my_param')
e = raises(TypeError, o.validate, 'Hello', 'cli')
assert str(e) == TYPE_ERROR % ('my_param', NoneType, 'Hello', str)
e = raises(TypeError, o.validate, 'Hello')
assert str(e) == TYPE_ERROR % ('my_param', type(None), 'Hello', str)
class Example(self.cls):
type = int
@@ -485,16 +497,15 @@ class test_Param(ClassChecker):
fail = DummyRule(u'no good')
o = Example('example', pass1, pass2)
assert o.multivalue is False
assert o.validate(11, 'cli') is None
assert o.validate(11) is None
assert pass1.calls == [(text.ugettext, 11)]
assert pass2.calls == [(text.ugettext, 11)]
pass1.reset()
pass2.reset()
o = Example('example', pass1, pass2, fail)
e = raises(errors.ValidationError, o.validate, 42, 'cli')
e = raises(errors.ValidationError, o.validate, 42)
assert e.name == 'example'
assert e.error == u'no good'
assert e.index is None
assert pass1.calls == [(text.ugettext, 42)]
assert pass2.calls == [(text.ugettext, 42)]
assert fail.calls == [(text.ugettext, 42)]
@@ -505,7 +516,7 @@ class test_Param(ClassChecker):
fail = DummyRule(u'this one is not good')
o = Example('example', pass1, pass2, multivalue=True)
assert o.multivalue is True
assert o.validate((3, 9), 'cli') is None
assert o.validate((3, 9)) is None
assert pass1.calls == [
(text.ugettext, 3),
(text.ugettext, 9),
@@ -518,10 +529,9 @@ class test_Param(ClassChecker):
pass2.reset()
o = Example('multi_example', pass1, pass2, fail, multivalue=True)
assert o.multivalue is True
e = raises(errors.ValidationError, o.validate, (3, 9), 'cli')
e = raises(errors.ValidationError, o.validate, (3, 9))
assert e.name == 'multi_example'
assert e.error == u'this one is not good'
assert e.index == 0
assert pass1.calls == [(text.ugettext, 3)]
assert pass2.calls == [(text.ugettext, 3)]
assert fail.calls == [(text.ugettext, 3)]
@@ -538,14 +548,10 @@ class test_Param(ClassChecker):
# Test that TypeError is appropriately raised:
e = raises(TypeError, o._validate_scalar, 0)
assert str(e) == TYPE_ERROR % ('my_param', bool, 0, int)
e = raises(TypeError, o._validate_scalar, 'Hi', index=4)
assert str(e) == TYPE_ERROR % ('my_param', bool, 'Hi', str)
e = raises(TypeError, o._validate_scalar, True, index=3.0)
assert str(e) == TYPE_ERROR % ('index', int, 3.0, float)
# Test with passing rule:
assert o._validate_scalar(True, index=None) is None
assert o._validate_scalar(False, index=None) is None
assert o._validate_scalar(True) is None
assert o._validate_scalar(False) is None
assert okay.calls == [
(text.ugettext, True),
(text.ugettext, False),
@@ -558,11 +564,9 @@ class test_Param(ClassChecker):
e = raises(errors.ValidationError, o._validate_scalar, True)
assert e.name == 'my_param'
assert e.error == u'this describes the error'
assert e.index is None
e = raises(errors.ValidationError, o._validate_scalar, False, index=2)
e = raises(errors.ValidationError, o._validate_scalar, False)
assert e.name == 'my_param'
assert e.error == u'this describes the error'
assert e.index == 2
assert okay.calls == [
(text.ugettext, True),
(text.ugettext, False),
@@ -576,7 +580,7 @@ class test_Param(ClassChecker):
"""
Test the `ipalib.parameters.Param.get_default` method.
"""
class PassThrough(object):
class PassThrough:
value = None
def __call__(self, value):
@@ -593,6 +597,8 @@ class test_Param(ClassChecker):
type = unicode
def __init__(self, name, **kw):
# (Pylint complains because the superclass is unknowm)
# pylint: disable=bad-super-call, super-on-old-class
self._convert_scalar = PassThrough()
super(Str, self).__init__(name, **kw)
@@ -639,7 +645,7 @@ class test_Flag(ClassChecker):
# Test that TypeError is raise if default is not a bool:
e = raises(TypeError, self.cls, 'my_flag', default=None)
assert str(e) == TYPE_ERROR % ('default', bool, None, NoneType)
assert str(e) == TYPE_ERROR % ('default', bool, None, type(None))
# Test with autofill=False, default=True
o = self.cls('my_flag', autofill=False, default=True)
@@ -674,7 +680,7 @@ class test_Data(ClassChecker):
Test the `ipalib.parameters.Data.__init__` method.
"""
o = self.cls('my_data')
assert o.type is NoneType
assert o.type is type(None) # noqa
assert o.password is False
assert o.rules == tuple()
assert o.class_rules == tuple()
@@ -682,7 +688,6 @@ class test_Data(ClassChecker):
assert o.minlength is None
assert o.maxlength is None
assert o.length is None
assert o.pattern is None
# Test mixing length with minlength or maxlength:
o = self.cls('my_data', length=5)
@@ -694,7 +699,7 @@ class test_Data(ClassChecker):
]
for kw in permutations:
o = self.cls('my_data', **kw)
for (key, value) in kw.iteritems():
for (key, value) in kw.items():
assert getattr(o, key) == value
e = raises(ValueError, self.cls, 'my_data', length=5, **kw)
assert str(e) == \
@@ -728,7 +733,7 @@ class test_Bytes(ClassChecker):
Test the `ipalib.parameters.Bytes.__init__` method.
"""
o = self.cls('my_bytes')
assert o.type is str
assert o.type is bytes
assert o.password is False
assert o.rules == tuple()
assert o.class_rules == tuple()
@@ -755,7 +760,7 @@ class test_Bytes(ClassChecker):
assert len(o.class_rules) == len(kw)
assert len(o.rules) == 0
assert len(o.all_rules) == len(kw)
for (key, value) in kw.iteritems():
for (key, value) in kw.items():
assert getattr(o, key) == value
e = raises(ValueError, self.cls, 'my_bytes', length=5, **kw)
assert str(e) == \
@@ -789,12 +794,12 @@ class test_Bytes(ClassChecker):
assert dummy.translation is translation
# Test with passing values:
for value in ('abc', 'four', '12345'):
for value in (b'abc', b'four', b'12345'):
assert rule(dummy, value) is None
assert dummy.called() is False
# Test with failing values:
for value in ('', 'a', '12'):
for value in (b'', b'a', b'12'):
assert_equal(
rule(dummy, value),
translation % dict(minlength=3)
@@ -815,12 +820,12 @@ class test_Bytes(ClassChecker):
assert dummy.translation is translation
# Test with passing values:
for value in ('ab', '123', 'four'):
for value in (b'ab', b'123', b'four'):
assert rule(dummy, value) is None
assert dummy.called() is False
# Test with failing values:
for value in ('12345', 'sixsix'):
for value in (b'12345', b'sixsix'):
assert_equal(
rule(dummy, value),
translation % dict(maxlength=4)
@@ -841,12 +846,12 @@ class test_Bytes(ClassChecker):
assert dummy.translation is translation
# Test with passing values:
for value in ('1234', 'four'):
for value in (b'1234', b'four'):
assert rule(dummy, value) is None
assert dummy.called() is False
# Test with failing values:
for value in ('ab', '123', '12345', 'sixsix'):
for value in (b'ab', b'123', b'12345', b'sixsix'):
assert_equal(
rule(dummy, value),
translation % dict(length=4),
@@ -860,9 +865,9 @@ class test_Bytes(ClassChecker):
Test the `ipalib.parameters.Bytes._rule_pattern` method.
"""
# Test our assumptions about Python re module and Unicode:
pat = '\w+$'
pat = br'\w+$'
r = re.compile(pat)
assert r.match('Hello_World') is not None
assert r.match(b'Hello_World') is not None
assert r.match(utf8_bytes) is None
assert r.match(binary_bytes) is None
@@ -874,12 +879,12 @@ class test_Bytes(ClassChecker):
dummy = dummy_ugettext(translation)
# Test with passing values:
for value in ('HELLO', 'hello', 'Hello_World'):
for value in (b'HELLO', b'hello', b'Hello_World'):
assert rule(dummy, value) is None
assert dummy.called() is False
# Test with failing values:
for value in ('Hello!', 'Hello World', utf8_bytes, binary_bytes):
for value in (b'Hello!', b'Hello World', utf8_bytes, binary_bytes):
assert_equal(
rule(dummy, value),
translation % dict(pattern=pat),
@@ -915,21 +920,15 @@ class test_Str(ClassChecker):
mthd = o._convert_scalar
for value in (u'Hello', 42, 1.2, unicode_str):
assert mthd(value) == unicode(value)
bad = [True, 'Hello', dict(one=1), utf8_bytes]
bad = [True, b'Hello', dict(one=1), utf8_bytes]
for value in bad:
e = raises(errors.ConversionError, mthd, value)
assert e.name == 'my_str'
assert e.index is None
assert_equal(unicode(e.error), u'must be Unicode text')
e = raises(errors.ConversionError, mthd, value, index=18)
assert e.name == 'my_str'
assert e.index == 18
assert_equal(unicode(e.error), u'must be Unicode text')
bad = [(u'Hello',), [42.3]]
for value in bad:
e = raises(errors.ConversionError, mthd, value)
assert e.name == 'my_str'
assert e.index is None
assert_equal(unicode(e.error), u'Only one value is allowed')
assert o.convert(None) is None
@@ -1016,10 +1015,13 @@ class test_Str(ClassChecker):
Test the `ipalib.parameters.Str._rule_pattern` method.
"""
# Test our assumptions about Python re module and Unicode:
pat = '\w{5}$'
pat = r'\w{5}$'
r1 = re.compile(pat)
r2 = re.compile(pat, re.UNICODE)
assert r1.match(unicode_str) is None
if six.PY2:
assert r1.match(unicode_str) is None
else:
assert r1.match(unicode_str) is not None
assert r2.match(unicode_str) is not None
# Create instance:
@@ -1070,7 +1072,6 @@ class test_Password(ClassChecker):
o = self.cls('my_password')
e = raises(errors.PasswordMismatch, o._convert_scalar, [u'one', u'two'])
assert e.name == 'my_password'
assert e.index is None
assert o._convert_scalar([u'one', u'one']) == u'one'
assert o._convert_scalar(u'one') == u'one'
@@ -1154,10 +1155,10 @@ class test_StrEnum(EnumChecker):
_cls = parameters.StrEnum
_name = 'my_strenum'
_datatype = unicode
_test_values = u'Hello', u'naughty', u'nurse!'
_bad_type_values = u'Hello', 'naughty', u'nurse!'
_bad_type = str
_translation = u"values='Hello', 'naughty', 'nurse!'"
_test_values = u'Hello', u'tall', u'nurse!'
_bad_type_values = u'Hello', 1, u'nurse!'
_bad_type = int
_translation = u"values='Hello', 'tall', 'nurse!'"
_bad_values = u'Howdy', u'quiet', u'library!'
_single_value_translation = u"value='Hello'"
@@ -1173,11 +1174,10 @@ def check_int_scalar_conversions(o):
for bad in ['hello', u'hello', True, None, u'', u'.', 8j, ()]:
e = raises(errors.ConversionError, o._convert_scalar, bad)
assert e.name == 'my_number'
assert e.index is None
# Assure large magnitude values are handled correctly
assert type(o._convert_scalar(sys.maxint * 2)) == long
assert o._convert_scalar(sys.maxint * 2) == sys.maxint * 2
assert o._convert_scalar(unicode(sys.maxint * 2)) == sys.maxint * 2
assert type(o._convert_scalar(sys.maxsize * 2)) == long
assert o._convert_scalar(sys.maxsize * 2) == sys.maxsize * 2
assert o._convert_scalar(unicode(sys.maxsize * 2)) == sys.maxsize * 2
assert o._convert_scalar(long(16)) == 16
# Assure normal conversions produce expected result
assert o._convert_scalar(u'16.99') == 16
@@ -1185,6 +1185,7 @@ def check_int_scalar_conversions(o):
assert o._convert_scalar(u'16') == 16
assert o._convert_scalar(u'0x10') == 16
assert o._convert_scalar(u'020') == 16
assert o._convert_scalar(u'0o20') == 16
class test_IntEnum(EnumChecker):
@@ -1220,7 +1221,7 @@ class test_Number(ClassChecker):
Test the `ipalib.parameters.Number.__init__` method.
"""
o = self.cls('my_number')
assert o.type is NoneType
assert o.type is type(None) # noqa
assert o.password is False
assert o.rules == tuple()
assert o.class_rules == tuple()
@@ -1241,7 +1242,7 @@ class test_Int(ClassChecker):
# Test with no kwargs:
o = self.cls('my_number')
assert o.type == int
assert o.allowed_types == (int, long)
assert o.allowed_types == (int,)
assert isinstance(o, parameters.Int)
assert o.minvalue == int(MININT)
assert o.maxvalue == int(MAXINT)
@@ -1419,8 +1420,7 @@ class test_Decimal(ClassChecker):
param = self.cls('my_number', precision=1)
e = raises(ConversionError, param, '123456789012345678901234567890')
assert str(e) == \
"invalid 'my_number': quantize result has too many digits for current context"
assert str(e).startswith("invalid 'my_number': ")
def test_exponential(self):
"""
@@ -1513,7 +1513,7 @@ class test_AccessTime(ClassChecker):
u'periodical yearly month 4 day 1-31 0800-1400',
u'periodic weekly day 8 0800-1400',
):
e = raises(ValidationError, o._rule_required, None, value)
raises(ValidationError, o._rule_required, None, value)
def test_create_param():
"""
@@ -1535,13 +1535,17 @@ def test_create_param():
for spec in ('one?', 'two+', 'three*', 'four'):
(name, kw) = parameters.parse_param_spec(spec)
p = f(spec)
assert p.param_spec is spec
assert p.param_spec == spec
assert p.name == name
assert p.required is kw['required']
assert p.multivalue is kw['multivalue']
# Test that TypeError is raised when spec is neither a Param nor a str:
for spec in (u'one', 42, parameters.Param, parameters.Str):
if six.PY2:
bad_value = u'one'
else:
bad_value = b'one'
for spec in (bad_value, 42, parameters.Param, parameters.Str):
e = raises(TypeError, f, spec)
assert str(e) == \
TYPE_ERROR % ('spec', (str, parameters.Param), spec, type(spec))
@@ -1579,8 +1583,10 @@ class test_IA5Str(ClassChecker):
for value in bad:
e = raises(errors.ConversionError, mthd, value)
assert e.name == 'my_str'
assert e.index is None
assert_equal(e.error, "The character '\\xc3' is not allowed.")
if six.PY2:
assert_equal(e.error, u"The character '\\xc3' is not allowed.")
else:
assert_equal(e.error, u"The character 'á' is not allowed.")
class test_DateTime(ClassChecker):
@@ -1625,4 +1631,122 @@ class test_DateTime(ClassChecker):
u'1991-31-12Z',
u'1991-12-07T25:30:05Z',
):
raises(ConversionError, o.convert, value)
raises(ConversionError, o.convert, value)
class test_CertificateSigningRequest(ClassChecker):
"""
Test the `ipalib.parameters.CertificateSigningRequest` class
"""
_cls = parameters.CertificateSigningRequest
sample_csr = (
b'-----BEGIN CERTIFICATE REQUEST-----\n'
b'MIIBjjCB+AIBADBPMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEQ\n'
b'MA4GA1UEChMHRXhhbXBsZTEZMBcGA1UEAxMQdGVzdC5leGFtcGxlLmNvbTCBnzAN\n'
b'BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAyxsN5dmvyKiw+5nyrcO3a61sivZRg+ja\n'
b'kyNIyUo+tIUiYwTdpPESAHTWRlk0XhydauAkWfOIN7pR3a5Z+kQw8W7F+DuZze2M\n'
b'6wRNmN+NTrTlqnKOiMHBXhIM0Qxrx68GDctYqtnKTVT94FvvLl9XYVdUEi2ePTc2\n'
b'Nyfr1z66+W0CAwEAAaAAMA0GCSqGSIb3DQEBBQUAA4GBAIf3r+Y6WHrFnttUqDow\n'
b'9/UCHtCeQlQoJqjjxi5wcjbkGwTgHbx/BPOd/8OVaHElboMXLGaZx+L/eFO6E9Yg\n'
b'mDOYv3OsibDFGaEhJrU8EnfuFZKnbrGeSC9Hkqrq+3OjqacaPla5N7MHKbfLY377\n'
b'ddbOHKzR0sURZ+ro4z3fATW2\n'
b'-----END CERTIFICATE REQUEST-----\n'
)
# certmonger <= 0.79.5 (most probably in higher versions, too) will be
# sending us just base64-encoded DER certs without the information it's
# base64-encoded bytes (__base64__: in the JSON request), we need to
# support that too, unfortunately
sample_base64 = (
"MIICETCCAXoCAQAwTzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWEx"
"EDAOBgNVBAoTB0V4YW1wbGUxGTAXBgNVBAMTEHRlc3QuZXhhbXBsZS5jb20wgZ8w"
"DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOXfP8LeiU7g6wLCclgkT1lVskK+Lxm1"
"6ijE4LmEQBk5nn2P46im+E/UOgTddbDo5cdJlkoCnqXkO4RkqJckXYDxfI34KL3C"
"CRFPvOa5Sg02m1x5Rg3boZfS6NciP62lRp0SI+0TCt3F16wYZxMahVIOXjbJ6Lu5"
"mGjNn7XaWJhFAgMBAAGggYEwfwYJKoZIhvcNAQkOMXIwcDAeBgNVHREEFzAVghN0"
"ZXN0bG93LmV4YW1wbGUuY29tME4GA1UdHwRHMEUwQ6BBoD+GHGh0dHA6Ly9jYS5l"
"eGFtcGxlLmNvbS9teS5jcmyGH2h0dHA6Ly9vdGhlci5leGFtcGxlLmNvbS9teS5j"
"cmwwDQYJKoZIhvcNAQEFBQADgYEAkv8pppcgGhX7erJmvg9r2UHrRriuKaOYgKZQ"
"lf/eBt2N0L2mV4QvCY82H7HWuE+7T3mra9ikfvz0nYkPJQe2gntjZzECE0Jt5LWR"
"UZOFwX8N6wrX11U2xu0NlvsbjU6siWd6OZjZ1p5/V330lzut/q3CNzaAcW1Fx3wL"
"sV5SXSw="
)
sample_der_csr = (
b'0\x82\x02\x110\x82\x01z\x02\x01\x000O1\x0b0\t\x06\x03U\x04\x06\x13'
b'\x02US1\x130\x11\x06\x03U\x04\x08\x13\nCalifornia1\x100\x0e\x06\x03U'
b'\x04\n\x13\x07Example1\x190\x17\x06\x03U\x04\x03\x13\x10test.example'
b'.com0\x81\x9f0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x81'
b'\x8d\x000\x81\x89\x02\x81\x81\x00\xe5\xdf?\xc2\xde\x89N\xe0\xeb\x02'
b'\xc2rX$OYU\xb2B\xbe/\x19\xb5\xea(\xc4\xe0\xb9\x84@\x199\x9e}\x8f\xe3'
b'\xa8\xa6\xf8O\xd4:\x04\xddu\xb0\xe8\xe5\xc7I\x96J\x02\x9e\xa5\xe4;'
b'\x84d\xa8\x97$]\x80\xf1|\x8d\xf8(\xbd\xc2\t\x11O\xbc\xe6\xb9J\r6\x9b'
b'\\yF\r\xdb\xa1\x97\xd2\xe8\xd7"?\xad\xa5F\x9d\x12#\xed\x13\n\xdd\xc5'
b'\xd7\xac\x18g\x13\x1a\x85R\x0e^6\xc9\xe8\xbb\xb9\x98h\xcd\x9f\xb5'
b'\xdaX\x98E\x02\x03\x01\x00\x01\xa0\x81\x810\x7f\x06\t*\x86H\x86\xf7'
b'\r\x01\t\x0e1r0p0\x1e\x06\x03U\x1d\x11\x04\x170\x15\x82\x13'
b'testlow.example.com0N\x06\x03U\x1d\x1f\x04G0E0C\xa0A\xa0?\x86'
b'\x1chttp://ca.example.com/my.crl\x86\x1fhttp://other.example.com/'
b'my.crl0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x05\x05\x00\x03\x81\x81\x00'
b'\x92\xff)\xa6\x97 \x1a\x15\xfbz\xb2f\xbe\x0fk\xd9A\xebF\xb8\xae)\xa3'
b'\x98\x80\xa6P\x95\xff\xde\x06\xdd\x8d\xd0\xbd\xa6W\x84/\t\x8f6\x1f'
b'\xb1\xd6\xb8O\xbbOy\xabk\xd8\xa4~\xfc\xf4\x9d\x89\x0f%\x07\xb6\x82'
b'{cg1\x02\x13Bm\xe4\xb5\x91Q\x93\x85\xc1\x7f\r\xeb\n\xd7\xd7U6\xc6'
b'\xed\r\x96\xfb\x1b\x8dN\xac\x89gz9\x98\xd9\xd6\x9e\x7fW}\xf4\x97;'
b'\xad\xfe\xad\xc276\x80qmE\xc7|\x0b\xb1^R],'
)
malformed_csr = (
b'-----BEGIN CERTIFICATE REQUEST-----\n'
b'VGhpcyBpcyBhbiBpbnZhbGlkIENTUg==\n'
b'-----END CERTIFICATE REQUEST-----\n'
)
def test_init(self):
# create the parameter
o = self.cls('csr')
assert o.type is crypto_x509.CertificateSigningRequest
assert isinstance(o, parameters.CertificateSigningRequest)
assert o.multivalue is False
def test_convert(self):
o = self.cls('csr')
# test that we're able to handle PEM CSR input equally as bytes, string
# and cryptography x509._CertificateSigningRequest object
for prep_input in (
lambda x: x,
lambda x: x.decode('utf-8'),
lambda x: crypto_x509.load_pem_x509_csr(x, default_backend())
):
# test that input is correctly converted to python-crytography
# object representation
csr_object = o.convert(prep_input(self.sample_csr))
assert isinstance(csr_object,
crypto_x509.CertificateSigningRequest)
assert (csr_object.public_bytes(x509.Encoding.PEM) ==
self.sample_csr)
# test that we fail the same with malformed CSR as bytes or str
for prep_input in (
lambda x: x,
lambda x: x.decode('utf-8'),
):
# test that malformed CSRs won't be accepted
raises(errors.CertificateOperationError,
o.convert,
prep_input(self.malformed_csr))
# test DER as an input to the convert method
csr_object = o.convert(self.sample_der_csr)
assert isinstance(csr_object, crypto_x509.CertificateSigningRequest)
assert (csr_object.public_bytes(x509.Encoding.DER) ==
self.sample_der_csr)
# test base64-encoded DER as an input to the convert method
csr_object = o.convert(self.sample_base64)
assert isinstance(csr_object, crypto_x509.CertificateSigningRequest)
assert (csr_object.public_bytes(x509.Encoding.DER) ==
base64.b64decode(self.sample_base64))
# test that wrong type will not be accepted
bad_value = datetime.date.today()
raises(ConversionError, o.convert, bad_value)

View File

@@ -21,182 +21,20 @@
Test the `ipalib.plugable` module.
"""
import inspect
from ipatests.util import raises, no_set, no_del, read_only
from ipatests.util import getitem, setitem, delitem
from ipatests.util import ClassChecker, create_test_api
from ipalib import plugable, errors, text
from ipaplatform.paths import paths
# FIXME: Pylint errors
# pylint: disable=no-member
import os
import sys
import textwrap
class test_SetProxy(ClassChecker):
"""
Test the `ipalib.plugable.SetProxy` class.
"""
_cls = plugable.SetProxy
from ipalib import plugable, errors, create_api
from ipatests.util import raises, read_only
from ipatests.util import ClassChecker, create_test_api, TempHome
def test_class(self):
"""
Test the `ipalib.plugable.SetProxy` class.
"""
assert self.cls.__bases__ == (plugable.ReadOnly,)
import pytest
def test_init(self):
"""
Test the `ipalib.plugable.SetProxy.__init__` method.
"""
okay = (set, frozenset, dict)
fail = (list, tuple)
for t in okay:
self.cls(t())
raises(TypeError, self.cls, t)
for t in fail:
raises(TypeError, self.cls, t())
raises(TypeError, self.cls, t)
def test_SetProxy(self):
"""
Test container emulation of `ipalib.plugable.SetProxy` class.
"""
def get_key(i):
return 'key_%d' % i
cnt = 10
target = set()
proxy = self.cls(target)
for i in xrange(cnt):
key = get_key(i)
# Check initial state
assert len(proxy) == len(target)
assert list(proxy) == sorted(target)
assert key not in proxy
assert key not in target
# Add and test again
target.add(key)
assert len(proxy) == len(target)
assert list(proxy) == sorted(target)
assert key in proxy
assert key in target
class test_DictProxy(ClassChecker):
"""
Test the `ipalib.plugable.DictProxy` class.
"""
_cls = plugable.DictProxy
def test_class(self):
"""
Test the `ipalib.plugable.DictProxy` class.
"""
assert self.cls.__bases__ == (plugable.SetProxy,)
def test_init(self):
"""
Test the `ipalib.plugable.DictProxy.__init__` method.
"""
self.cls(dict())
raises(TypeError, self.cls, dict)
fail = (set, frozenset, list, tuple)
for t in fail:
raises(TypeError, self.cls, t())
raises(TypeError, self.cls, t)
def test_DictProxy(self):
"""
Test container emulation of `ipalib.plugable.DictProxy` class.
"""
def get_kv(i):
return (
'key_%d' % i,
'val_%d' % i,
)
cnt = 10
target = dict()
proxy = self.cls(target)
for i in xrange(cnt):
(key, val) = get_kv(i)
# Check initial state
assert len(proxy) == len(target)
assert list(proxy) == sorted(target)
assert list(proxy()) == [target[k] for k in sorted(target)]
assert key not in proxy
raises(KeyError, getitem, proxy, key)
# Add and test again
target[key] = val
assert len(proxy) == len(target)
assert list(proxy) == sorted(target)
assert list(proxy()) == [target[k] for k in sorted(target)]
# Verify TypeError is raised trying to set/del via proxy
raises(TypeError, setitem, proxy, key, val)
raises(TypeError, delitem, proxy, key)
class test_MagicDict(ClassChecker):
"""
Test the `ipalib.plugable.MagicDict` class.
"""
_cls = plugable.MagicDict
def test_class(self):
"""
Test the `ipalib.plugable.MagicDict` class.
"""
assert self.cls.__bases__ == (plugable.DictProxy,)
for non_dict in ('hello', 69, object):
raises(TypeError, self.cls, non_dict)
def test_MagicDict(self):
"""
Test container emulation of `ipalib.plugable.MagicDict` class.
"""
cnt = 10
keys = []
d = dict()
dictproxy = self.cls(d)
for i in xrange(cnt):
key = 'key_%d' % i
val = 'val_%d' % i
keys.append(key)
# Test thet key does not yet exist
assert len(dictproxy) == i
assert key not in dictproxy
assert not hasattr(dictproxy, key)
raises(KeyError, getitem, dictproxy, key)
raises(AttributeError, getattr, dictproxy, key)
# Test that items/attributes cannot be set on dictproxy:
raises(TypeError, setitem, dictproxy, key, val)
raises(AttributeError, setattr, dictproxy, key, val)
# Test that additions in d are reflected in dictproxy:
d[key] = val
assert len(dictproxy) == i + 1
assert key in dictproxy
assert hasattr(dictproxy, key)
assert dictproxy[key] is val
assert read_only(dictproxy, key) is val
# Test __iter__
assert list(dictproxy) == keys
for key in keys:
# Test that items cannot be deleted through dictproxy:
raises(TypeError, delitem, dictproxy, key)
raises(AttributeError, delattr, dictproxy, key)
# Test that deletions in d are reflected in dictproxy
del d[key]
assert len(dictproxy) == len(d)
assert key not in dictproxy
raises(KeyError, getitem, dictproxy, key)
raises(AttributeError, getattr, dictproxy, key)
pytestmark = pytest.mark.tier0
class test_Plugin(ClassChecker):
@@ -216,11 +54,10 @@ class test_Plugin(ClassChecker):
"""
Test the `ipalib.plugable.Plugin.__init__` method.
"""
o = self.cls()
api = 'the api instance'
o = self.cls(api)
assert o.name == 'Plugin'
assert o.module == 'ipalib.plugable'
assert o.fullname == 'ipalib.plugable.Plugin'
assert isinstance(o.doc, text.Gettext)
assert isinstance(o.doc, str)
class some_subclass(self.cls):
"""
Do sub-classy things.
@@ -231,172 +68,89 @@ class test_Plugin(ClassChecker):
One more paragraph.
"""
o = some_subclass()
o = some_subclass(api)
assert o.name == 'some_subclass'
assert o.module == __name__
assert o.fullname == '%s.some_subclass' % __name__
assert o.summary == 'Do sub-classy things.'
assert isinstance(o.doc, text.Gettext)
assert isinstance(o.doc, str)
class another_subclass(self.cls):
pass
o = another_subclass()
assert o.summary == '<%s>' % o.fullname
# Test that Plugin makes sure the subclass hasn't defined attributes
# whose names conflict with the logger methods set in Plugin.__init__():
class check(self.cls):
info = 'whatever'
e = raises(StandardError, check)
assert str(e) == \
"info is already bound to ipatests.test_ipalib.test_plugable.check()"
def test_set_api(self):
"""
Test the `ipalib.plugable.Plugin.set_api` method.
"""
api = 'the api instance'
o = self.cls()
assert o.api is None
e = raises(AssertionError, o.set_api, None)
assert str(e) == 'set_api() argument cannot be None'
o.set_api(api)
assert o.api is api
e = raises(AssertionError, o.set_api, api)
assert str(e) == 'set_api() can only be called once'
o = another_subclass(api)
assert o.summary == u'<%s.%s>' % (another_subclass.__module__,
another_subclass.__name__)
def test_finalize(self):
"""
Test the `ipalib.plugable.Plugin.finalize` method.
"""
o = self.cls()
class api:
@staticmethod
def is_production_mode():
return False
o = self.cls(api)
assert not o.__islocked__()
o.finalize()
assert o.__islocked__()
def test_call(self):
"""
Test the `ipalib.plugable.Plugin.call` method.
"""
o = self.cls()
o.call(paths.BIN_TRUE) is None
e = raises(errors.SubprocessError, o.call, paths.BIN_FALSE)
assert e.returncode == 1
assert e.argv == (paths.BIN_FALSE,)
def test_Registry():
"""
Test the `ipalib.plugable.Registry` class
"""
class Base1:
pass
def test_Registrar():
"""
Test the `ipalib.plugable.Registrar` class
"""
class Base1(object):
pass
class Base2(object):
pass
class Base3(object):
class Base2:
pass
class plugin1(Base1):
pass
class plugin2(Base2):
pass
class plugin3(Base3):
pass
# Test creation of Registrar:
r = plugable.Registrar(Base1, Base2)
# Test __iter__:
assert list(r) == ['Base1', 'Base2']
# Test __hasitem__, __getitem__:
for base in [Base1, Base2]:
name = base.__name__
assert name in r
assert r[name] is base
magic = getattr(r, name)
assert type(magic) is plugable.MagicDict
assert len(magic) == 0
# Test creation of Registry:
r = plugable.Registry()
# Check that TypeError is raised trying to register something that isn't
# a class:
p = plugin1()
e = raises(TypeError, r, p)
assert str(e) == 'plugin must be a class; got %r' % p
# Check that SubclassError is raised trying to register a class that is
# not a subclass of an allowed base:
e = raises(errors.PluginSubclassError, r, plugin3)
assert e.plugin is plugin3
e = raises(TypeError, r(), p)
assert str(e) == 'plugin must be callable; got %r' % p
# Check that registration works
r(plugin1)
assert len(r.Base1) == 1
assert r.Base1['plugin1'] is plugin1
assert r.Base1.plugin1 is plugin1
r()(plugin1)
# Check that DuplicateError is raised trying to register exact class
# again:
e = raises(errors.PluginDuplicateError, r, plugin1)
assert e.plugin is plugin1
# Check that OverrideError is raised trying to register class with same
# name and same base:
orig1 = plugin1
class base1_extended(Base1):
pass
class plugin1(base1_extended):
pass
e = raises(errors.PluginOverrideError, r, plugin1)
assert e.base == 'Base1'
assert e.name == 'plugin1'
e = raises(errors.PluginDuplicateError, r(), plugin1)
assert e.plugin is plugin1
# Check that overriding works
r(plugin1, override=True)
assert len(r.Base1) == 1
assert r.Base1.plugin1 is plugin1
assert r.Base1.plugin1 is not orig1
# Check that MissingOverrideError is raised trying to override a name
# not yet registerd:
e = raises(errors.PluginMissingOverrideError, r, plugin2, override=True)
assert e.base == 'Base2'
assert e.name == 'plugin2'
assert e.plugin is plugin2
class base1_extended(Base1):
pass
class plugin1(base1_extended): # pylint: disable=function-redefined
pass
r(override=True)(plugin1)
# Test that another plugin can be registered:
assert len(r.Base2) == 0
r(plugin2)
assert len(r.Base2) == 1
assert r.Base2.plugin2 is plugin2
r()(plugin2)
# Setup to test more registration:
class plugin1a(Base1):
pass
r(plugin1a)
r()(plugin1a)
class plugin1b(Base1):
pass
r(plugin1b)
r()(plugin1b)
class plugin2a(Base2):
pass
r(plugin2a)
r()(plugin2a)
class plugin2b(Base2):
pass
r(plugin2b)
# Again test __hasitem__, __getitem__:
for base in [Base1, Base2]:
name = base.__name__
assert name in r
assert r[name] is base
magic = getattr(r, name)
assert len(magic) == 3
for key in magic:
klass = magic[key]
assert getattr(magic, key) is klass
assert issubclass(klass, base)
r()(plugin2b)
class test_API(ClassChecker):
@@ -421,12 +175,14 @@ class test_API(ClassChecker):
def method(self, n):
return n + 1
api = plugable.API(base0, base1)
class API(plugable.API):
bases = (base0, base1)
modules = ()
api = API()
api.env.mode = 'unit_test'
api.env.in_tree = True
r = api.register
assert isinstance(r, plugable.Registrar)
assert read_only(api, 'register') is r
r = api.add_plugin
class base0_plugin0(base0):
pass
@@ -466,32 +222,31 @@ class test_API(ClassChecker):
def get_plugin_name(b, p):
return 'base%d_plugin%d' % (b, p)
for b in xrange(2):
for b in range(2):
base_name = get_base_name(b)
base = locals()[base_name]
ns = getattr(api, base_name)
assert isinstance(ns, plugable.NameSpace)
assert isinstance(ns, plugable.APINameSpace)
assert read_only(api, base_name) is ns
assert len(ns) == 3
for p in xrange(3):
for p in range(3):
plugin_name = get_plugin_name(b, p)
plugin = locals()[plugin_name]
inst = ns[plugin_name]
assert isinstance(inst, base)
assert isinstance(inst, plugin)
assert inst.name == plugin_name
assert read_only(ns, plugin_name) is inst
assert inst.method(7) == 7 + b
# Test that calling finilize again raises AssertionError:
e = raises(StandardError, api.finalize)
e = raises(Exception, api.finalize)
assert str(e) == 'API.finalize() already called', str(e)
def test_bootstrap(self):
"""
Test the `ipalib.plugable.API.bootstrap` method.
"""
(o, home) = create_test_api()
o, _home = create_test_api()
assert o.env._isdone('_bootstrap') is False
assert o.env._isdone('_finalize_core') is False
assert o.isdone('bootstrap') is False
@@ -500,18 +255,91 @@ class test_API(ClassChecker):
assert o.env._isdone('_bootstrap') is True
assert o.env._isdone('_finalize_core') is True
assert o.env.my_test_override == 'Hello, world!'
e = raises(StandardError, o.bootstrap)
e = raises(Exception, o.bootstrap)
assert str(e) == 'API.bootstrap() already called'
def test_load_plugins(self):
"""
Test the `ipalib.plugable.API.load_plugins` method.
"""
(o, home) = create_test_api()
o, _home = create_test_api()
assert o.isdone('bootstrap') is False
assert o.isdone('load_plugins') is False
o.load_plugins()
assert o.isdone('bootstrap') is True
assert o.isdone('load_plugins') is True
e = raises(StandardError, o.load_plugins)
e = raises(Exception, o.load_plugins)
assert str(e) == 'API.load_plugins() already called'
def test_ipaconf_env(self):
ipa_confdir = os.environ.get('IPA_CONFDIR', None)
try:
with TempHome() as home:
defaultconf = home.join('default.conf')
with open(defaultconf, 'w') as f:
f.write(textwrap.dedent("""
[global]
basedn = dc=ipa,dc=test
realm = IPA.TEST
domain = ipa.test
""")
)
os.environ['IPA_CONFDIR'] = home.path
api = create_api(mode='unit_test')
api.bootstrap()
api.finalize()
assert api.env.confdir == home.path
assert api.env.conf_default == defaultconf
assert api.env.realm == 'IPA.TEST'
assert api.env.domain == 'ipa.test'
os.environ['IPA_CONFDIR'] = home.join('invalid')
api = create_api(mode='unit_test')
with pytest.raises(errors.EnvironmentError):
api.bootstrap()
finally:
if ipa_confdir:
os.environ['IPA_CONFDIR'] = ipa_confdir
else:
os.environ.pop('IPA_CONFDIR')
class test_cli(ClassChecker):
"""
Test the `ipalib.plugable` global bootstrap.
"""
def test_no_args(self):
sys.argv = ['/usr/bin/ipa']
api = create_api(mode='unit_test')
(_options, argv) = api.bootstrap_with_global_options(
context='unit_test')
assert len(argv) == 0
assert _options.env is None
assert _options.conf is None
assert _options.debug is None
assert _options.delegate is None
assert _options.verbose is None
def test_one_arg(self):
sys.argv = ['/usr/bin/ipa', 'user-show']
api = create_api(mode='unit_test')
(_options, argv) = api.bootstrap_with_global_options(
context='unit_test')
assert argv == ['user-show']
assert _options.verbose is None
def test_args_valid_option(self):
sys.argv = ['/usr/bin/ipa', '-v', 'user-show']
api = create_api(mode='unit_test')
(_options, argv) = api.bootstrap_with_global_options(
context='unit_test')
assert argv == ['user-show']
assert _options.verbose == 1
def test_args_invalid_option(self):
sys.argv = ['/usr/bin/ipa', '-verbose', 'user-show']
api = create_api(mode='unit_test')
try:
api.bootstrap_with_global_options(context='unit_test')
except errors.OptionError as e:
assert e.msg == 'Unable to parse option rbose'

View File

@@ -20,23 +20,31 @@
"""
Test the `ipalib.rpc` module.
"""
from __future__ import print_function
from xmlrpclib import Binary, Fault, dumps, loads
from xmlrpc.client import Binary, Fault, dumps, loads
import urllib
import pytest
import six
import nose
from ipatests.util import raises, assert_equal, PluginTester, DummyClass
from ipatests.util import Fuzzy
from ipatests.data import binary_bytes, utf8_bytes, unicode_str
from ipalib.frontend import Command
from ipalib.request import context, Connection
from ipalib import rpc, errors, api, request
from ipalib import rpc, errors, api, request as ipa_request
from ipapython.version import API_VERSION
if six.PY3:
unicode = str
std_compound = (binary_bytes, utf8_bytes, unicode_str)
def dump_n_load(value):
(param, method) = loads(
param, _method = loads(
dumps((value,), allow_none=True)
)
return param[0]
@@ -52,32 +60,38 @@ def test_round_trip():
"""
Test `ipalib.rpc.xml_wrap` and `ipalib.rpc.xml_unwrap`.
This tests the two functions together with ``xmlrpclib.dumps()`` and
``xmlrpclib.loads()`` in a full wrap/dumps/loads/unwrap round trip.
This tests the two functions together with ``xmlrpc.client.dumps()`` and
``xmlrpc.client.loads()`` in a full wrap/dumps/loads/unwrap round trip.
"""
# We first test that our assumptions about xmlrpclib module in the Python
# We first test that our assumptions about xmlrpc.client module in the Python
# standard library are correct:
assert_equal(dump_n_load(utf8_bytes), unicode_str)
if six.PY2:
output_binary_type = bytes
else:
output_binary_type = Binary
if six.PY2:
assert_equal(dump_n_load(utf8_bytes), unicode_str)
assert_equal(dump_n_load(unicode_str), unicode_str)
assert_equal(dump_n_load(Binary(binary_bytes)).data, binary_bytes)
assert isinstance(dump_n_load(Binary(binary_bytes)), Binary)
assert type(dump_n_load('hello')) is str
assert type(dump_n_load(b'hello')) is output_binary_type
assert type(dump_n_load(u'hello')) is str
assert_equal(dump_n_load(''), '')
assert_equal(dump_n_load(u''), '')
assert_equal(dump_n_load(b''), output_binary_type(b''))
assert_equal(dump_n_load(u''), str())
assert dump_n_load(None) is None
# Now we test our wrap and unwrap methods in combination with dumps, loads:
# All str should come back str (because they get wrapped in
# xmlrpclib.Binary(). All unicode should come back unicode because str
# All bytes should come back bytes (because they get wrapped in
# xmlrpc.client.Binary(). All unicode should come back unicode because str
# explicity get decoded by rpc.xml_unwrap() if they weren't already
# decoded by xmlrpclib.loads().
# decoded by xmlrpc.client.loads().
assert_equal(round_trip(utf8_bytes), utf8_bytes)
assert_equal(round_trip(unicode_str), unicode_str)
assert_equal(round_trip(binary_bytes), binary_bytes)
assert type(round_trip('hello')) is str
assert type(round_trip(b'hello')) is bytes
assert type(round_trip(u'hello')) is unicode
assert_equal(round_trip(''), '')
assert_equal(round_trip(b''), b'')
assert_equal(round_trip(u''), u'')
assert round_trip(None) is None
compound = [utf8_bytes, None, binary_bytes, (None, unicode_str),
@@ -93,13 +107,13 @@ def test_xml_wrap():
f = rpc.xml_wrap
assert f([], API_VERSION) == tuple()
assert f({}, API_VERSION) == dict()
b = f('hello', API_VERSION)
b = f(b'hello', API_VERSION)
assert isinstance(b, Binary)
assert b.data == 'hello'
assert b.data == b'hello'
u = f(u'hello', API_VERSION)
assert type(u) is unicode
assert u == u'hello'
value = f([dict(one=False, two=u'hello'), None, 'hello'], API_VERSION)
f([dict(one=False, two=u'hello'), None, b'hello'], API_VERSION)
def test_xml_unwrap():
@@ -110,13 +124,13 @@ def test_xml_unwrap():
assert f([]) == tuple()
assert f({}) == dict()
value = f(Binary(utf8_bytes))
assert type(value) is str
assert type(value) is bytes
assert value == utf8_bytes
assert f(utf8_bytes) == unicode_str
assert f(unicode_str) == unicode_str
value = f([True, Binary('hello'), dict(one=1, two=utf8_bytes, three=None)])
assert value == (True, 'hello', dict(one=1, two=unicode_str, three=None))
assert type(value[1]) is str
value = f([True, Binary(b'hello'), dict(one=1, two=utf8_bytes, three=None)])
assert value == (True, b'hello', dict(one=1, two=unicode_str, three=None))
assert type(value[1]) is bytes
assert type(value[2]['two']) is unicode
@@ -195,15 +209,7 @@ class test_xmlclient(PluginTester):
class user_add(Command):
pass
# Test that ValueError is raised when forwarding a command that is not
# in api.Command:
(o, api, home) = self.instance('Backend', in_server=False)
e = raises(ValueError, o.forward, 'user_add')
assert str(e) == '%s.forward(): %r not in api.Command' % (
'xmlclient', 'user_add'
)
(o, api, home) = self.instance('Backend', user_add, in_server=False)
o, _api, _home = self.instance('Backend', user_add, in_server=False)
args = (binary_bytes, utf8_bytes, unicode_str)
kw = dict(one=binary_bytes, two=utf8_bytes, three=unicode_str)
params = [args, kw]
@@ -229,6 +235,9 @@ class test_xmlclient(PluginTester):
),
)
# Create connection for the current thread
setattr(context, o.id, Connection(conn, lambda: None))
context.xmlclient = Connection(conn, lambda: None)
# Test with a successful return value:
@@ -246,18 +255,20 @@ class test_xmlclient(PluginTester):
assert context.xmlclient.conn._calledall() is True
class test_xml_introspection(object):
@classmethod
def setUpClass(self):
@pytest.mark.skip_ipaclient_unittest
@pytest.mark.needs_ipaapi
class test_xml_introspection:
@pytest.fixture(autouse=True, scope="class")
def xml_introsp_setup(self, request):
try:
api.Backend.xmlclient.connect(fallback=False)
api.Backend.xmlclient.connect()
except (errors.NetworkError, IOError):
raise nose.SkipTest('%r: Server not available: %r' %
(__name__, api.env.xmlrpc_uri))
pytest.skip('%r: Server not available: %r' %
(__name__, api.env.xmlrpc_uri))
@classmethod
def tearDownClass(self):
request.destroy_context()
def fin():
ipa_request.destroy_context()
request.addfinalizer(fin)
def test_list_methods(self):
result = api.Backend.xmlclient.conn.system.listMethods()
@@ -270,9 +281,9 @@ class test_xml_introspection(object):
def test_list_methods_many_params(self):
try:
result = api.Backend.xmlclient.conn.system.listMethods('foo')
except Fault, f:
print f
api.Backend.xmlclient.conn.system.listMethods('foo')
except Fault as f:
print(f)
assert f.faultCode == 3003
assert f.faultString == (
"command 'system.listMethods' takes no arguments")
@@ -290,9 +301,9 @@ class test_xml_introspection(object):
def test_signature_no_params(self):
try:
result = api.Backend.xmlclient.conn.system.methodSignature()
except Fault, f:
print f
api.Backend.xmlclient.conn.system.methodSignature()
except Fault as f:
print(f)
assert f.faultCode == 3007
assert f.faultString == "'method name' is required"
else:
@@ -300,9 +311,9 @@ class test_xml_introspection(object):
def test_signature_many_params(self):
try:
result = api.Backend.xmlclient.conn.system.methodSignature('a', 'b')
except Fault, f:
print f
api.Backend.xmlclient.conn.system.methodSignature('a', 'b')
except Fault as f:
print(f)
assert f.faultCode == 3004
assert f.faultString == (
"command 'system.methodSignature' takes at most 1 argument")
@@ -311,9 +322,9 @@ class test_xml_introspection(object):
def test_help_no_params(self):
try:
result = api.Backend.xmlclient.conn.system.methodHelp()
except Fault, f:
print f
api.Backend.xmlclient.conn.system.methodHelp()
except Fault as f:
print(f)
assert f.faultCode == 3007
assert f.faultString == "'method name' is required"
else:
@@ -321,11 +332,65 @@ class test_xml_introspection(object):
def test_help_many_params(self):
try:
result = api.Backend.xmlclient.conn.system.methodHelp('a', 'b')
except Fault, f:
print f
api.Backend.xmlclient.conn.system.methodHelp('a', 'b')
except Fault as f:
print(f)
assert f.faultCode == 3004
assert f.faultString == (
"command 'system.methodHelp' takes at most 1 argument")
else:
raise AssertionError('did not raise')
@pytest.mark.skip_ipaclient_unittest
@pytest.mark.needs_ipaapi
class test_rpcclient_context(PluginTester):
"""
Test the context in `ipalib.rpc.rpcclient` plugin.
"""
@pytest.fixture(autouse=True)
def rpcclient_context_fsetup(self, request):
try:
api.Backend.rpcclient.connect(ca_certfile='foo')
except (errors.NetworkError, IOError):
pytest.skip('%r: Server not available: %r' %
(__name__, api.env.xmlrpc_uri))
def fin():
if api.Backend.rpcclient.isconnected():
api.Backend.rpcclient.disconnect()
request.addfinalizer(fin)
def test_context_cafile(self):
"""
Test that ca_certfile is set in `ipalib.rpc.rpcclient.connect`
"""
ca_certfile = getattr(context, 'ca_certfile', None)
assert_equal(ca_certfile, 'foo')
def test_context_principal(self):
"""
Test that principal is set in `ipalib.rpc.rpcclient.connect`
"""
principal = getattr(context, 'principal', None)
assert_equal(principal, 'admin@%s' % api.env.realm)
def test_context_request_url(self):
"""
Test that request_url is set in `ipalib.rpc.rpcclient.connect`
"""
request_url = getattr(context, 'request_url', None)
assert_equal(request_url, 'https://%s/ipa/session/json' % api.env.host)
def test_context_session_cookie(self):
"""
Test that session_cookie is set in `ipalib.rpc.rpcclient.connect`
"""
fuzzy_cookie = Fuzzy(
r'^ipa_session=MagBearerToken=[A-Za-z0-9+\/]+=*;$')
session_cookie = getattr(context, 'session_cookie', None)
# pylint-2 is incorrectly spewing Too many positional arguments
# pylint: disable=E1121
unquoted = urllib.parse.unquote(session_cookie)
assert(unquoted == fuzzy_cookie)

View File

@@ -20,19 +20,23 @@
"""
Test the `ipalib.text` module.
"""
from __future__ import print_function
import os
import shutil
import tempfile
import re
import nose
import locale
from ipatests.util import raises, assert_equal
import six
import pytest
from ipatests.i18n import create_po, po_file_iterate
from ipalib.request import context
from ipalib import request
from ipalib import text
from ipapython.ipautil import file_exists
if six.PY3:
unicode = str
pytestmark = pytest.mark.tier0
singular = '%(count)d goose makes a %(dish)s'
plural = '%(count)d geese make a %(dish)s'
@@ -45,22 +49,45 @@ def test_create_translation():
assert context.__dict__[key] is t
class test_TestLang(object):
def setUp(self):
self.tmp_dir = None
self.saved_lang = None
class test_TestLang:
lang_env_vars = {'LC_ALL', 'LC_MESSAGES', 'LANGUAGE', 'LANG'}
def setup_lang(self):
"""
Set all env variables used by gettext to localize translation files
to xh_ZA
"""
self.lang = 'xh_ZA'
self.domain = 'ipa'
self.saved_locale = {
k: v for k, v in os.environ.items() if k in self.lang_env_vars}
self.ipa_i18n_dir = os.path.join(os.path.dirname(__file__), '../../install/po')
os.environ.update(
{env_var: self.lang for env_var in self.lang_env_vars}
)
def teardown_lang(self):
"""
Revert the locale settings to original values. If the original env
variable was not set before, it will be popped off os.environ
"""
for env_var in self.lang_env_vars:
if env_var not in self.saved_locale:
os.environ.pop(env_var, None)
os.environ.update(self.saved_locale)
@pytest.fixture(autouse=True)
def testlang_setup(self, request):
self.tmp_dir = None
self.setup_lang()
self.domain = 'ipa'
self.pot_basename = '%s.pot' % self.domain
self.po_basename = '%s.po' % self.lang
self.mo_basename = '%s.mo' % self.domain
self.tmp_dir = tempfile.mkdtemp()
self.saved_lang = os.environ['LANG']
self.locale_dir = os.path.join(self.tmp_dir, 'test_locale')
self.msg_dir = os.path.join(self.locale_dir, self.lang, 'LC_MESSAGES')
@@ -68,39 +95,43 @@ class test_TestLang(object):
if not os.path.exists(self.msg_dir):
os.makedirs(self.msg_dir)
self.pot_file = os.path.join(self.ipa_i18n_dir, self.pot_basename)
self.pot_file = os.path.join(
os.path.dirname(__file__), 'data', self.pot_basename)
self.mo_file = os.path.join(self.msg_dir, self.mo_basename)
self.po_file = os.path.join(self.tmp_dir, self.po_basename)
result = create_po(self.pot_file, self.po_file, self.mo_file)
if result:
raise nose.SkipTest('Unable to create po file "%s" & mo file "%s" from pot file "%s"' %
(self.po_file, self.mo_file, self.pot_file))
pytest.skip(
'Unable to create po file "%s" & mo file "%s" from pot '
'file "%s"' % (self.po_file, self.mo_file, self.pot_file)
)
if not file_exists(self.po_file):
raise nose.SkipTest('Test po file unavailable, run "make test" in install/po')
if not os.path.isfile(self.po_file):
pytest.skip(
'Test po file unavailable: {}'.format(self.po_file))
if not file_exists(self.mo_file):
raise nose.SkipTest('Test mo file unavailable, run "make test" in install/po')
if not os.path.isfile(self.mo_file):
pytest.skip(
'Test mo file unavailable: {}'.format(self.mo_file))
self.po_file_iterate = po_file_iterate
def tearDown(self):
if self.saved_lang is not None:
os.environ['LANG'] = self.saved_lang
def fin():
self.teardown_lang()
if self.tmp_dir is not None:
shutil.rmtree(self.tmp_dir)
if self.tmp_dir is not None:
shutil.rmtree(self.tmp_dir)
request.addfinalizer(fin)
def test_test_lang(self):
print "test_test_lang"
print("test_test_lang")
# The test installs the test message catalog under the xh_ZA
# (e.g. Zambia Xhosa) language by default. It would be nice to
# use a dummy language not associated with any real language,
# but the setlocale function demands the locale be a valid
# known locale, Zambia Xhosa is a reasonable choice :)
os.environ['LANG'] = self.lang
# Create a gettext translation object specifying our domain as
# 'ipa' and the locale_dir as 'test_locale' (i.e. where to
@@ -118,7 +149,8 @@ class test_TestLang(object):
result = self.po_file_iterate(self.po_file, get_msgstr, get_msgstr_plural)
assert result == 0
class test_LazyText(object):
class test_LazyText:
klass = text.LazyText
@@ -129,7 +161,7 @@ class test_LazyText(object):
assert inst.key == ('foo', 'bar')
class test_FixMe(object):
class test_FixMe:
klass = text.FixMe
def test_init(self):
@@ -148,7 +180,7 @@ class test_FixMe(object):
assert type(unicode(inst)) is unicode
class test_Gettext(object):
class test_Gettext:
klass = text.Gettext
@@ -156,7 +188,7 @@ class test_Gettext(object):
inst = self.klass('what up?', 'foo', 'bar')
assert inst.domain == 'foo'
assert inst.localedir == 'bar'
assert inst.msg is 'what up?'
assert inst.msg == 'what up?'
assert inst.args == ('what up?', 'foo', 'bar')
def test_repr(self):
@@ -169,7 +201,13 @@ class test_Gettext(object):
def test_mod(self):
inst = self.klass('hello %(adj)s nurse', 'foo', 'bar')
assert inst % dict(adj='naughty', stuff='junk') == 'hello naughty nurse'
assert inst % dict(adj='tall', stuff='junk') == 'hello tall nurse'
def test_format(self):
inst = self.klass('{0} {adj} nurse', 'foo', 'bar')
posargs = ('hello', 'bye')
knownargs = {'adj': 'caring', 'stuff': 'junk'}
assert inst.format(*posargs, **knownargs) == 'hello caring nurse'
def test_eq(self):
inst1 = self.klass('what up?', 'foo', 'bar')
@@ -177,6 +215,7 @@ class test_Gettext(object):
inst3 = self.klass('Hello world', 'foo', 'bar')
inst4 = self.klass('what up?', 'foo', 'baz')
# pylint: disable=comparison-with-itself
assert (inst1 == inst1) is True
assert (inst1 == inst2) is True
assert (inst1 == inst3) is False
@@ -204,7 +243,7 @@ class test_Gettext(object):
assert (inst4 != inst1) is True
class test_NGettext(object):
class test_NGettext:
klass = text.NGettext
@@ -234,12 +273,28 @@ class test_NGettext(object):
assert inst % dict(count=1, dish='stew') == '1 goose makes a stew'
assert inst % dict(count=2, dish='pie') == '2 geese make a pie'
def test_format(self):
singular = '{count} goose makes a {0} {dish}'
plural = '{count} geese make a {0} {dish}'
inst = self.klass(singular, plural, 'foo', 'bar')
posargs = ('tasty', 'disgusting')
knownargs0 = {'count': 0, 'dish': 'frown', 'stuff': 'junk'}
knownargs1 = {'count': 1, 'dish': 'stew', 'stuff': 'junk'}
knownargs2 = {'count': 2, 'dish': 'pie', 'stuff': 'junk'}
expected_str0 = '0 geese make a tasty frown'
expected_str1 = '1 goose makes a tasty stew'
expected_str2 = '2 geese make a tasty pie'
assert inst.format(*posargs, **knownargs0) == expected_str0
assert inst.format(*posargs, **knownargs1) == expected_str1
assert inst.format(*posargs, **knownargs2) == expected_str2
def test_eq(self):
inst1 = self.klass(singular, plural, 'foo', 'bar')
inst2 = self.klass(singular, plural, 'foo', 'bar')
inst3 = self.klass(singular, '%(count)d thingies', 'foo', 'bar')
inst4 = self.klass(singular, plural, 'foo', 'baz')
# pylint: disable=comparison-with-itself
assert (inst1 == inst1) is True
assert (inst1 == inst2) is True
assert (inst1 == inst3) is False
@@ -267,7 +322,7 @@ class test_NGettext(object):
assert (inst4 != inst1) is True
class test_GettextFactory(object):
class test_GettextFactory:
klass = text.GettextFactory
@@ -295,12 +350,12 @@ class test_GettextFactory(object):
inst = self.klass('foo', 'bar')
g = inst('what up?')
assert type(g) is text.Gettext
assert g.msg is 'what up?'
assert g.msg == 'what up?'
assert g.domain == 'foo'
assert g.localedir == 'bar'
class test_NGettextFactory(object):
class test_NGettextFactory:
klass = text.NGettextFactory
@@ -334,7 +389,7 @@ class test_NGettextFactory(object):
assert ng.localedir == 'bar'
class test_ConcatenatedText(object):
class test_ConcatenatedText:
klass = text.ConcatenatedLazyText
@@ -357,6 +412,12 @@ class test_ConcatenatedText(object):
inst = self.klass('[', text.Gettext('%(color)s', 'foo', 'bar'), ']')
assert inst % dict(color='red', stuff='junk') == '[red]'
def test_format(self):
inst = self.klass('{0}', text.Gettext('{color}', 'foo', 'bar'), ']')
posargs = ('[', '(')
knownargs = {'color': 'red', 'stuff': 'junk'}
assert inst.format(*posargs, **knownargs) == '[red]'
def test_add(self):
inst = (text.Gettext('pale ', 'foo', 'bar') +
text.Gettext('blue', 'foo', 'bar'))

View File

@@ -1,26 +1,77 @@
# Authors:
# Jason Gerard DeRose <jderose@redhat.com>
#
# Copyright (C) 2008 Red Hat
# see file 'COPYING' for use and warranty information
# Copyright (C) 2018 FreeIPA Contributors see COPYING for license
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Test the `ipalib.util` module.
"""Tests for ipalib.util module
"""
from ipalib import util
import os
import ssl
from unittest import mock
import pytest
from ipalib.util import (
get_pager, create_https_connection, get_proper_tls_version_span
)
from ipaplatform.constants import constants
@pytest.mark.parametrize('pager,expected_result', [
# Valid values
('cat', '/bin/cat'),
('/bin/cat', '/bin/cat'),
# Invalid values (wrong command, package is not installed, etc)
('cat_', None),
('', None)
])
def test_get_pager(pager, expected_result):
with mock.patch.dict(os.environ, {'PAGER': pager}):
pager = get_pager()
assert(pager == expected_result or pager.endswith(expected_result))
BASE_CTX = ssl.SSLContext(ssl.PROTOCOL_TLS)
if constants.TLS_HIGH_CIPHERS is not None:
BASE_CTX.set_ciphers(constants.TLS_HIGH_CIPHERS)
else:
BASE_CTX.set_ciphers("PROFILE=SYSTEM")
# options: IPA still supports Python 3.6 without min/max version setters
BASE_OPT = BASE_CTX.options
BASE_OPT |= (
ssl.OP_ALL | ssl.OP_NO_COMPRESSION | ssl.OP_SINGLE_DH_USE |
ssl.OP_SINGLE_ECDH_USE
)
TLS_OPT = (
ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 | ssl.OP_NO_TLSv1 |
ssl.OP_NO_TLSv1_1
)
OP_NO_TLSv1_3 = getattr(ssl, "OP_NO_TLSv1_3", 0) # make pylint happy
@pytest.mark.skip_if_platform(
"debian", reason="Crypto policy is not supported on Debian"
)
@pytest.mark.parametrize('minver,maxver,opt,expected', [
(None, None, BASE_OPT, None),
(None, "tls1.3", BASE_OPT | TLS_OPT, ["tls1.2", "tls1.3"]),
("tls1.2", "tls1.3", BASE_OPT | TLS_OPT, ["tls1.2", "tls1.3"]),
("tls1.2", None, BASE_OPT | TLS_OPT, ["tls1.2", "tls1.3"]),
("tls1.2", "tls1.2", BASE_OPT | TLS_OPT | OP_NO_TLSv1_3, ["tls1.2"]),
(None, "tls1.2", BASE_OPT | TLS_OPT | OP_NO_TLSv1_3, ["tls1.2"]),
("tls1.3", "tls1.3", BASE_OPT | TLS_OPT | ssl.OP_NO_TLSv1_2, ["tls1.3"]),
("tls1.3", None, BASE_OPT | TLS_OPT | ssl.OP_NO_TLSv1_2, ["tls1.3"]),
])
def test_tls_version_span(minver, maxver, opt, expected):
assert get_proper_tls_version_span(minver, maxver) == expected
# file must exist and contain certs
cafile = ssl.get_default_verify_paths().cafile
conn = create_https_connection(
"invalid.test",
cafile=cafile,
tls_version_min=minver,
tls_version_max=maxver
)
ctx = getattr(conn, "_context")
assert ctx.options == BASE_OPT | opt
assert ctx.get_ciphers() == BASE_CTX.get_ciphers()

View File

@@ -21,28 +21,151 @@
Test the `ipalib.x509` module.
"""
import os
from os import path
import sys
from ipatests.util import raises, setitem, delitem, ClassChecker
from ipatests.util import getitem, setitem, delitem
from ipatests.util import TempDir, TempHome
from ipalib.constants import TYPE_ERROR, OVERRIDE_ERROR, SET_ERROR, DEL_ERROR
from ipalib.constants import NAME_REGEX, NAME_ERROR
import base64
from binascii import hexlify
from configparser import RawConfigParser
import datetime
from io import StringIO
import pickle
import pytest
from cryptography import x509 as crypto_x509
from cryptography.x509.general_name import DNSName
from ipalib import x509
from nss.error import NSPRError
from ipapython.dn import DN
pytestmark = pytest.mark.tier0
# certutil -
# certificate for CN=ipa.example.com,O=IPA
goodcert = 'MIICAjCCAWugAwIBAgICBEUwDQYJKoZIhvcNAQEFBQAwKTEnMCUGA1UEAxMeSVBBIFRlc3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTEwMDYyNTEzMDA0MloXDTE1MDYyNTEzMDA0MlowKDEMMAoGA1UEChMDSVBBMRgwFgYDVQQDEw9pcGEuZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAJcZ+H6+cQaN/BlzR8OYkVeJgaU5tCaV9FF1m7Ws/ftPtTJUaSL1ncp6603rjA4tH1aa/B8i8xdC46+ZbY2au8b9ryGcOsx2uaRpNLEQ2Fy//q1kQC8oM+iD8Nd6osF0a2wnugsgnJHPuJzhViaWxYgzk5DRdP81debokF3f3FX/AgMBAAGjOjA4MBEGCWCGSAGG+EIBAQQEAwIGQDATBgNVHSUEDDAKBggrBgEFBQcDATAOBgNVHQ8BAf8EBAMCBPAwDQYJKoZIhvcNAQEFBQADgYEALD6X9V9w381AzzQPcHsjIjiX3B/AF9RCGocKZUDXkdDhsD9NZ3PLPEf1AMjkraKG963HPB8scyiBbbSuSh6m7TCp0eDgRpo77zNuvd3U4Qpm0Qk+KEjtHQDjNNG6N4ZnCQPmjFPScElvc/GgW7XMbywJy2euF+3/Uip8cnPgSH4='
goodcert = (
b'MIICAjCCAWugAwIBAgICBEUwDQYJKoZIhvcNAQEFBQAwKTEnMCUGA1UEAxMeSVBB'
b'IFRlc3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTEwMDYyNTEzMDA0MloXDTE1'
b'MDYyNTEzMDA0MlowKDEMMAoGA1UEChMDSVBBMRgwFgYDVQQDEw9pcGEuZXhhbXBs'
b'ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAJcZ+H6+cQaN/BlzR8OY'
b'kVeJgaU5tCaV9FF1m7Ws/ftPtTJUaSL1ncp6603rjA4tH1aa/B8i8xdC46+ZbY2a'
b'u8b9ryGcOsx2uaRpNLEQ2Fy//q1kQC8oM+iD8Nd6osF0a2wnugsgnJHPuJzhViaW'
b'xYgzk5DRdP81debokF3f3FX/AgMBAAGjOjA4MBEGCWCGSAGG+EIBAQQEAwIGQDAT'
b'BgNVHSUEDDAKBggrBgEFBQcDATAOBgNVHQ8BAf8EBAMCBPAwDQYJKoZIhvcNAQEF'
b'BQADgYEALD6X9V9w381AzzQPcHsjIjiX3B/AF9RCGocKZUDXkdDhsD9NZ3PLPEf1'
b'AMjkraKG963HPB8scyiBbbSuSh6m7TCp0eDgRpo77zNuvd3U4Qpm0Qk+KEjtHQDj'
b'NNG6N4ZnCQPmjFPScElvc/GgW7XMbywJy2euF+3/Uip8cnPgSH4='
)
goodcert_headers = (
b'-----BEGIN CERTIFICATE-----\n' +
goodcert +
b'\n-----END CERTIFICATE-----'
)
# The base64-encoded string 'bad cert'
badcert = 'YmFkIGNlcnQ='
badcert = (
b'-----BEGIN CERTIFICATE-----\n'
b'YmFkIGNlcnQ=\r\n'
b'-----END CERTIFICATE-----'
)
class test_x509(object):
good_pkcs7 = (
b'-----BEGIN PKCS7-----\n'
b'MIIDvAYJKoZIhvcNAQcCoIIDrTCCA6kCAQExADALBgkqhkiG9w0BBwGgggOPMIID\n'
b'izCCAnOgAwIBAgIBATANBgkqhkiG9w0BAQsFADA2MRQwEgYDVQQKDAtFWEFNUExF\n'
b'LkNPTTEeMBwGA1UEAwwVQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTE3MDkyMDIw\n'
b'NDI1N1oXDTM3MDkyMDIwNDI1N1owNjEUMBIGA1UECgwLRVhBTVBMRS5DT00xHjAc\n'
b'BgNVBAMMFUNlcnRpZmljYXRlIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQAD\n'
b'ggEPADCCAQoCggEBAMNojX57UCCPTtEn9tQJBS4By5NixwodKm1UqOGsiecDrB0i\n'
b'Pw7D6uGP6g4b6srYtbh+YsRJnfekB2L08q1dX3LVEItq2TS0WKqgZuRZkw7DvnGl\n'
b'eANMwjHmE8k6/E0yI3GGxJLAfDZYw6CDspLkyN9anjQwVCz5N5z5bpeqi5BeVwin\n'
b'O8WVF6FNn3iyL66uwOsTGEzCo3Y5HiwqYgaND73TtdsBHcIqOdRql3CC3IdoXXcW\n'
b'044w4Lm2E95MuY729pPBHREtyzVkYtyuoKJ8KApghIY5oCklBkRDjyFK4tE7iF/h\n'
b's+valeT9vcz2bHMIpvbjqAu/kqE8MjcNEFPjLhcCAwEAAaOBozCBoDAfBgNVHSME\n'
b'GDAWgBTUB04/d1eLhbMtBi4AB65tsAt+2TAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud\n'
b'DwEB/wQEAwIBxjAdBgNVHQ4EFgQU1AdOP3dXi4WzLQYuAAeubbALftkwPQYIKwYB\n'
b'BQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwOi8vaXBhLWNhLmdyZXlvYWsuY29t\n'
b'L2NhL29jc3AwDQYJKoZIhvcNAQELBQADggEBADQFwX1uh8tqLq8SqWZWtH95j33o\n'
b'5Ze2dW7sVppb/wVnNauG0wDQW7uIx+Ynr7GgufXLNBMn1aP/mA2CdHk7NZz2IB1s\n'
b'ZvbIfE8dVxzkA+Hh9d6cdgk4eU5rGf6Fw8ScEJ/48Mmncea3uGkHcOmt+BGLA8a1\n'
b'wtruy+iQylOkbv36CbxKV7IsZDP106Zc+cVeOUQZnCLKmvQkotn6UJd8N1X0R2J3\n'
b'4/qv0rUtcCnyEBNSgpTGCRlYM4kd98Dqc5W7wUpMcsQMFxQMSYY7pFQkdLPfJEx2\n'
b'Mg63SPawxfAgUeukrdsF3wTIKkIBu1TVse+kvRvgmRRrfF2a4ZOv5qORe2uhADEA\n'
b'-----END PKCS7-----'
)
long_oid_cert = b'''
-----BEGIN CERTIFICATE-----
MIIFiTCCBHGgAwIBAgITSAAAAAd1bEC5lsOdnQAAAAAABzANBgkqhkiG9w0BAQsF
ADBLMRUwEwYKCZImiZPyLGQBGRYFbG9jYWwxEjAQBgoJkiaJk/IsZAEZFgJhZDEe
MBwGA1UEAxMVYWQtV0lOLVBQSzAxNUY5TURRLUNBMB4XDTE3MDUyNTIzNDg0NVoX
DTE5MDUyNTIzNTg0NVowNDESMBAGA1UEChMJSVBBLkxPQ0FMMR4wHAYDVQQDExVD
ZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQDyyuty6irlL89hdaSW0UyAGLsOOMgAuJwBAeuRUorR159rsSnUXLcTHIsm
EszKhwxp3NkkawRWx/s0UN1m2+RUwMl6gvlw+G80Mz0S77C77M+2lO8HRmZGm+Wu
zBNcc9SANHuDQ1NISfZgLiscMS0+l0T3g6/Iqtg1kPWrq/tMevfh6tJEIedSBGo4
3xKEMSDkrvaeTuSVrgn/QT0m+WNccZa0c7X35L/hgR22/l5sr057Ef8F9vL8zUH5
TttFBIuiWJo8A8XX9I1zYIFhWjW3OVDZPBUnhGHH6yNyXGxXMRfcrrc74eTw8ivC
080AQuRtgwvDErB/JPDJ5w5t/ielAgMBAAGjggJ7MIICdzA9BgkrBgEEAYI3FQcE
MDAuBiYrBgEEAYI3FQiEoqJGhYq1PoGllQqGi+F4nacAgRODs5gfgozzAAIBZAIB
BTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUnSrC
yW3CR0e3ilJdN6kL06P3KHMwHwYDVR0jBBgwFoAUj69xtyUNwp8on+NWO+HlxKyg
X7AwgdgGA1UdHwSB0DCBzTCByqCBx6CBxIaBwWxkYXA6Ly8vQ049YWQtV0lOLVBQ
SzAxNUY5TURRLUNBLENOPVdJTi1QUEswMTVGOU1EUSxDTj1DRFAsQ049UHVibGlj
JTIwS2V5JTIwU2VydmljZXMsQ049U2VydmljZXMsQ049Q29uZmlndXJhdGlvbixE
Qz1hZCxEQz1sb2NhbD9jZXJ0aWZpY2F0ZVJldm9jYXRpb25MaXN0P2Jhc2U/b2Jq
ZWN0Q2xhc3M9Y1JMRGlzdHJpYnV0aW9uUG9pbnQwgcQGCCsGAQUFBwEBBIG3MIG0
MIGxBggrBgEFBQcwAoaBpGxkYXA6Ly8vQ049YWQtV0lOLVBQSzAxNUY5TURRLUNB
LENOPUFJQSxDTj1QdWJsaWMlMjBLZXklMjBTZXJ2aWNlcyxDTj1TZXJ2aWNlcyxD
Tj1Db25maWd1cmF0aW9uLERDPWFkLERDPWxvY2FsP2NBQ2VydGlmaWNhdGU/YmFz
ZT9vYmplY3RDbGFzcz1jZXJ0aWZpY2F0aW9uQXV0aG9yaXR5MDMGA1UdIAQsMCow
KAYmKwYBBAGCNxUIhKKiRoWKtT6BpZUKhovheJ2nAIEThrXzUYabpA4wDQYJKoZI
hvcNAQELBQADggEBAIsFS+Qc/ufTrkuHbMmzksOpxq+OIi9rot8zy9/1Vmj6d+iP
kB+vQ1u4/IhdQArJFNhsBzWSY9Pi8ZclovpepFeEZfXPUenyeRCU43HdMXcHXnlP
YZfyLQWOugdo1WxK6S9qQSOSlC7BSGZWvKkiAPAwr4zNbbS+ROA2w0xaYMv0rr5W
A4UAyzZAdqaGRJBRvCZ/uFHM5wMw0LzNCL4CqKW9jfZX0Fc2tdGx8zbTYxIdgr2D
PL25as32r3S/m4uWqoQaK0lxK5Y97eusK2rrmidy32Jctzwl29UWq8kpjRAuD8iR
CSc7sKqOf+fn3+fKITR2/DcSVvb0SGCr5fVVnjQ=
-----END CERTIFICATE-----
'''
ipa_demo_crt = b'''\
-----BEGIN CERTIFICATE-----
MIIGFTCCBP2gAwIBAgISA61CoqWtpZoTEyfLCXliPLYFMA0GCSqGSIb3DQEBCwUA
MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD
ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xODA3MjUwNTM2NTlaFw0x
ODEwMjMwNTM2NTlaMCAxHjAcBgNVBAMTFWlwYS5kZW1vMS5mcmVlaXBhLm9yZzCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKisvYUdarWE0CS9i+RcNf9Q
41Euw36R4Myf/PUCDVUvGsVXQWSCanbtyxa8Ows4cAHrfqhiKAnSg0IhLqCMJVQ8
8F699FHrP9EfPmZkG3RMLYPxKNrSmOVyNpIEQY9qfkDXZPLung6dk/c225Znoltq
bVWLObXA7eP9C/djupg3gUD7vOAMHFmfZ3OKnx1uktL5p707o2/qlkSiEO4Z5ebD
M8X0dTkN8V3LCCOjzCp88itGUWJM8Tjb86WkmYkJxmeZx6REd37rDXjqgYhwgXOB
bSqDkYKRaihwvd5Up/vE1wApBS1k7b1oEW80teDUbzbaaqp7oBWbZD2Ac1yJF7UC
AwEAAaOCAx0wggMZMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcD
AQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUUmTMI1CB6qFMXc0+
AGmqpfBAwhIwHwYDVR0jBBgwFoAUqEpqYwR93brm0Tm3pkVl7/Oo7KEwbwYIKwYB
BQUHAQEEYzBhMC4GCCsGAQUFBzABhiJodHRwOi8vb2NzcC5pbnQteDMubGV0c2Vu
Y3J5cHQub3JnMC8GCCsGAQUFBzAChiNodHRwOi8vY2VydC5pbnQteDMubGV0c2Vu
Y3J5cHQub3JnLzAgBgNVHREEGTAXghVpcGEuZGVtbzEuZnJlZWlwYS5vcmcwgf4G
A1UdIASB9jCB8zAIBgZngQwBAgEwgeYGCysGAQQBgt8TAQEBMIHWMCYGCCsGAQUF
BwIBFhpodHRwOi8vY3BzLmxldHNlbmNyeXB0Lm9yZzCBqwYIKwYBBQUHAgIwgZ4M
gZtUaGlzIENlcnRpZmljYXRlIG1heSBvbmx5IGJlIHJlbGllZCB1cG9uIGJ5IFJl
bHlpbmcgUGFydGllcyBhbmQgb25seSBpbiBhY2NvcmRhbmNlIHdpdGggdGhlIENl
cnRpZmljYXRlIFBvbGljeSBmb3VuZCBhdCBodHRwczovL2xldHNlbmNyeXB0Lm9y
Zy9yZXBvc2l0b3J5LzCCAQQGCisGAQQB1nkCBAIEgfUEgfIA8AB2AMEWSuCnctLU
OS3ICsEHcNTwxJvemRpIQMH6B1Fk9jNgAAABZNAnsSAAAAQDAEcwRQIgHkd/UkTZ
w8iV1Ox8MPHLrpY33cX6i5FV6w9+7YH3H2kCIQCVcrhsr4fokDyE2ueUqSFxkBVH
WND84/w5rFNAPjyO1QB2ACk8UZZUyDlluqpQ/FgH1Ldvv1h6KXLcpMMM9OVFR/R4
AAABZNAnsyUAAAQDAEcwRQIhALDWY2k55abu7IPwnFvMr4Zqd1DYQXEKWZEQLXUP
s4XGAiAabjpUwrLKVXpbp4WNLkTNlFjrSJafOzLG68H9AnoD4zANBgkqhkiG9w0B
AQsFAAOCAQEAfBNuQn/A2olJHxoBGLfMcQCkkNOfvBpfQeKgni2VVM+r1ZY8YVXx
OtVnV6XQ5M+l+6xlRpP1IwDdmJd/yaQgwbmYf4zl94W/s/qq4nlTd9G4ahmJOhlc
mWeIQMoEtAmQlIOqWto+Knfakz6Xyo+HVCQEyeoBmYFGZcakeAm6tp/6qtpkej+4
wBjShMPAdSYDPRaAqnZ3BAK2UmmlpAA5tkNvqOaHBCi760zYoxT6j1an7FotG0v9
2+W0aL34eMWKz/g4qhwk+Jiz45LLQWhHGIgXIUoNSzHgLIVuVOQI8DPsguvT6GHW
QUs1Hx1wL7mL4U8fKCFDKA+ds2B2xWgoZg==
-----END CERTIFICATE-----
'''
class test_x509:
"""
Test `ipalib.x509`
@@ -58,24 +181,21 @@ class test_x509(object):
"""
# Load a good cert
cert = x509.load_certificate(goodcert)
x509.load_pem_x509_certificate(goodcert_headers)
# Load a good cert with headers
newcert = '-----BEGIN CERTIFICATE-----' + goodcert + '-----END CERTIFICATE-----'
cert = x509.load_certificate(newcert)
# Load a good cert with headers and leading text
newcert = (
b'leading text\n' + goodcert_headers)
x509.load_pem_x509_certificate(newcert)
# Load a good cert with bad headers
newcert = '-----BEGIN CERTIFICATE-----' + goodcert
try:
cert = x509.load_certificate(newcert)
except TypeError:
pass
newcert = b'-----BEGIN CERTIFICATE-----' + goodcert_headers
with pytest.raises((TypeError, ValueError)):
x509.load_pem_x509_certificate(newcert)
# Load a bad cert
try:
cert = x509.load_certificate(badcert)
except NSPRError:
pass
with pytest.raises(ValueError):
x509.load_pem_x509_certificate(badcert)
def test_1_load_der_cert(self):
"""
@@ -85,55 +205,181 @@ class test_x509(object):
der = base64.b64decode(goodcert)
# Load a good cert
cert = x509.load_certificate(der, x509.DER)
def test_2_get_subject(self):
"""
Test retrieving the subject
"""
subject = x509.get_subject(goodcert)
assert DN(str(subject)) == DN(('CN','ipa.example.com'),('O','IPA'))
der = base64.b64decode(goodcert)
subject = x509.get_subject(der, x509.DER)
assert DN(str(subject)) == DN(('CN','ipa.example.com'),('O','IPA'))
# We should be able to pass in a tuple/list of certs too
subject = x509.get_subject((goodcert))
assert DN(str(subject)) == DN(('CN','ipa.example.com'),('O','IPA'))
subject = x509.get_subject([goodcert])
assert DN(str(subject)) == DN(('CN','ipa.example.com'),('O','IPA'))
def test_2_get_serial_number(self):
"""
Test retrieving the serial number
"""
serial = x509.get_serial_number(goodcert)
assert serial == 1093
der = base64.b64decode(goodcert)
serial = x509.get_serial_number(der, x509.DER)
assert serial == 1093
# We should be able to pass in a tuple/list of certs too
serial = x509.get_serial_number((goodcert))
assert serial == 1093
serial = x509.get_serial_number([goodcert])
assert serial == 1093
x509.load_der_x509_certificate(der)
def test_3_cert_contents(self):
"""
Test the contents of a certificate
"""
# Verify certificate contents. This exercises python-nss more than
# anything but confirms our usage of it.
# Verify certificate contents. This exercises python-cryptography
# more than anything but confirms our usage of it.
cert = x509.load_certificate(goodcert)
not_before = datetime.datetime(2010, 6, 25, 13, 0, 42)
not_after = datetime.datetime(2015, 6, 25, 13, 0, 42)
cert = x509.load_pem_x509_certificate(goodcert_headers)
assert DN(str(cert.subject)) == DN(('CN','ipa.example.com'),('O','IPA'))
assert DN(str(cert.issuer)) == DN(('CN','IPA Test Certificate Authority'))
assert DN(cert.subject) == DN(('CN', 'ipa.example.com'), ('O', 'IPA'))
assert DN(cert.issuer) == DN(('CN', 'IPA Test Certificate Authority'))
assert cert.serial_number == 1093
assert cert.valid_not_before_str == 'Fri Jun 25 13:00:42 2010 UTC'
assert cert.valid_not_after_str == 'Thu Jun 25 13:00:42 2015 UTC'
assert cert.not_valid_before == not_before
assert cert.not_valid_after == not_after
assert cert.san_general_names == []
assert cert.san_a_label_dns_names == []
assert cert.extended_key_usage == {'1.3.6.1.5.5.7.3.1'}
assert cert.extended_key_usage_bytes == (
b'0\x16\x06\x03U\x1d%\x01\x01\xff\x04\x0c0\n\x06\x08'
b'+\x06\x01\x05\x05\x07\x03\x01'
)
def test_load_pkcs7_pem(self):
certlist = x509.pkcs7_to_certs(good_pkcs7, datatype=x509.PEM)
assert len(certlist) == 1
cert = certlist[0]
assert DN(cert.subject) == DN('CN=Certificate Authority,O=EXAMPLE.COM')
assert cert.serial_number == 1
def test_long_oid(self):
"""
Test cerificate with very long OID. In this case we are using a
certificate from an opened case where one of X509v3 Certificate`s
Policies OID is longer then 80 chars.
"""
cert = x509.load_pem_x509_certificate(long_oid_cert)
ext = cert.extensions.get_extension_for_class(crypto_x509.
CertificatePolicies)
assert len(ext.value) == 1
assert ext.value[0].policy_identifier.dotted_string == (
u'1.3.6.1.4.1.311.21.8.8950086.10656446.2706058.12775672.480128.'
'147.13466065.13029902')
def test_ipa_demo_letsencrypt(self):
cert = x509.load_pem_x509_certificate(ipa_demo_crt)
assert DN(cert.subject) == DN('CN=ipa.demo1.freeipa.org')
assert DN(cert.issuer) == DN(
"CN=Let's Encrypt Authority X3,O=Let's Encrypt,C=US")
assert cert.serial_number == 0x03ad42a2a5ada59a131327cb0979623cb605
not_before = datetime.datetime(2018, 7, 25, 5, 36, 59)
not_after = datetime.datetime(2018, 10, 23, 5, 36, 59)
assert cert.not_valid_before == not_before
assert cert.not_valid_after == not_after
assert cert.san_general_names == [DNSName('ipa.demo1.freeipa.org')]
assert cert.san_a_label_dns_names == ['ipa.demo1.freeipa.org']
assert cert.extended_key_usage == {
'1.3.6.1.5.5.7.3.1', '1.3.6.1.5.5.7.3.2'
}
assert cert.extended_key_usage_bytes == (
b'0 \x06\x03U\x1d%\x01\x01\xff\x04\x160\x14\x06\x08+\x06\x01'
b'\x05\x05\x07\x03\x01\x06\x08+\x06\x01\x05\x05\x07\x03\x02'
)
class test_ExternalCAProfile:
def test_MSCSTemplateV1_good(self):
o = x509.MSCSTemplateV1("MySubCA")
assert hexlify(o.get_ext_data()) == b'1e0e004d007900530075006200430041'
def test_MSCSTemplateV1_bad(self):
with pytest.raises(ValueError):
x509.MSCSTemplateV1("MySubCA:1")
def test_MSCSTemplateV1_pickle_roundtrip(self):
o = x509.MSCSTemplateV1("MySubCA")
s = pickle.dumps(o)
assert o.get_ext_data() == pickle.loads(s).get_ext_data()
def test_MSCSTemplateV2_too_few_parts(self):
with pytest.raises(ValueError):
x509.MSCSTemplateV2("1.2.3.4")
def test_MSCSTemplateV2_too_many_parts(self):
with pytest.raises(ValueError):
x509.MSCSTemplateV2("1.2.3.4:100:200:300")
def test_MSCSTemplateV2_bad_oid(self):
with pytest.raises(ValueError):
x509.MSCSTemplateV2("not_an_oid:1")
def test_MSCSTemplateV2_non_numeric_major_version(self):
with pytest.raises(ValueError):
x509.MSCSTemplateV2("1.2.3.4:major:200")
def test_MSCSTemplateV2_non_numeric_minor_version(self):
with pytest.raises(ValueError):
x509.MSCSTemplateV2("1.2.3.4:100:minor")
def test_MSCSTemplateV2_major_version_lt_zero(self):
with pytest.raises(ValueError):
x509.MSCSTemplateV2("1.2.3.4:-1:200")
def test_MSCSTemplateV2_minor_version_lt_zero(self):
with pytest.raises(ValueError):
x509.MSCSTemplateV2("1.2.3.4:100:-1")
def test_MSCSTemplateV2_major_version_gt_max(self):
with pytest.raises(ValueError):
x509.MSCSTemplateV2("1.2.3.4:4294967296:200")
def test_MSCSTemplateV2_minor_version_gt_max(self):
with pytest.raises(ValueError):
x509.MSCSTemplateV2("1.2.3.4:100:4294967296")
def test_MSCSTemplateV2_good_major(self):
o = x509.MSCSTemplateV2("1.2.3.4:4294967295")
assert hexlify(o.get_ext_data()) == b'300c06032a0304020500ffffffff'
def test_MSCSTemplateV2_good_major_minor(self):
o = x509.MSCSTemplateV2("1.2.3.4:4294967295:0")
assert hexlify(o.get_ext_data()) \
== b'300f06032a0304020500ffffffff020100'
def test_MSCSTemplateV2_pickle_roundtrip(self):
o = x509.MSCSTemplateV2("1.2.3.4:4294967295:0")
s = pickle.dumps(o)
assert o.get_ext_data() == pickle.loads(s).get_ext_data()
def test_ExternalCAProfile_dispatch(self):
"""
Test that constructing ExternalCAProfile actually returns an
instance of the appropriate subclass.
"""
assert isinstance(
x509.ExternalCAProfile("MySubCA"),
x509.MSCSTemplateV1)
assert isinstance(
x509.ExternalCAProfile("1.2.3.4:100"),
x509.MSCSTemplateV2)
def test_write_pkispawn_config_file_MSCSTemplateV1(self):
template = x509.MSCSTemplateV1(u"SubCA")
expected = (
'[CA]\n'
'pki_req_ext_oid = 1.3.6.1.4.1.311.20.2\n'
'pki_req_ext_data = 1e0a00530075006200430041\n\n'
)
self._test_write_pkispawn_config_file(template, expected)
def test_write_pkispawn_config_file_MSCSTemplateV2(self):
template = x509.MSCSTemplateV2(u"1.2.3.4:4294967295")
expected = (
'[CA]\n'
'pki_req_ext_oid = 1.3.6.1.4.1.311.21.7\n'
'pki_req_ext_data = 300c06032a0304020500ffffffff\n\n'
)
self._test_write_pkispawn_config_file(template, expected)
def _test_write_pkispawn_config_file(self, template, expected):
"""
Test that the values we read from an ExternalCAProfile
object can be used to produce a reasonable-looking pkispawn
configuration.
"""
config = RawConfigParser()
config.optionxform = str
config.add_section("CA")
config.set("CA", "pki_req_ext_oid", template.ext_oid)
config.set("CA", "pki_req_ext_data",
hexlify(template.get_ext_data()).decode('ascii'))
out = StringIO()
config.write(out)
assert out.getvalue() == expected