# Authors:
#    Alexander Bokovoy <abokovoy@redhat.com>
#
# Copyright (C) 2012  Red Hat
# see file 'COPYING' for use and warranty information
#
# 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/plugins/idrange.py` module, and XML-RPC in general.
"""

from ipalib import api, errors
from xmlrpc_test import Declarative, fuzzy_uuid
from ipatests.test_xmlrpc import objectclasses
from ipatests.util import MockLDAP
from ipapython.dn import DN
from ipatests.test_xmlrpc.test_user_plugin import get_user_result

# Determine the test shift used

id_shift = 0
rid_shift = 0

for idrange in api.Command['idrange_find']()['result']:
    size = int(idrange['ipaidrangesize'][0])
    base_id = int(idrange['ipabaseid'][0])

    id_end = base_id + size
    rid_end = 0

    if 'ipabaserid' in idrange:
        base_rid = int(idrange['ipabaserid'][0])
        rid_end = base_rid + size

    if 'ipasecondarybaserid' in idrange:
        secondary_base_rid = int(idrange['ipasecondarybaserid'][0])
        rid_end = max(base_rid, secondary_base_rid) + size

    if id_shift < id_end:
        id_shift = id_end + 1000000

    if rid_shift < rid_end:
        rid_shift = rid_end + 1000000

# Local ranges definitions

testrange1 = u'testrange1'
testrange1_base_id = id_shift + 900000
testrange1_size = 99999
testrange1_base_rid = rid_shift + 10000
testrange1_secondary_base_rid = rid_shift + 200000

testrange2 = u'testrange2'
testrange2_base_id = id_shift + 100
testrange2_size = 50
testrange2_base_rid = rid_shift + 100
testrange2_secondary_base_rid = rid_shift + 1000

testrange3 = u'testrange3'
testrange3_base_id = id_shift + 200
testrange3_size = 50
testrange3_base_rid = rid_shift + 70
testrange3_secondary_base_rid = rid_shift + 1100

testrange4 = u'testrange4'
testrange4_base_id = id_shift + 300
testrange4_size = 50
testrange4_base_rid = rid_shift + 200
testrange4_secondary_base_rid = rid_shift + 1030

testrange5 = u'testrange5'
testrange5_base_id = id_shift + 400
testrange5_size = 50
testrange5_base_rid = rid_shift + 1020
testrange5_secondary_base_rid = rid_shift + 1200

testrange6 = u'testrange6'
testrange6_base_id = id_shift + 130
testrange6_size = 50
testrange6_base_rid = rid_shift + 500
testrange6_secondary_base_rid = rid_shift + 1300

testrange7 = u'testrange7'
testrange7_base_id = id_shift + 600
testrange7_size = 50
testrange7_base_rid = rid_shift + 600
testrange7_secondary_base_rid = rid_shift + 649

testrange8 = u'testrange8'
testrange8_base_id = id_shift + 700
testrange8_size = 50
testrange8_base_rid = rid_shift + 700
testrange8_secondary_base_rid = rid_shift + 800

# Helper functions definitions


def get_range_dn(name):
    format_str = "cn={name},cn=ranges,cn=etc,{basedn}"
    data = dict(name=name, basedn=api.env.basedn)
    return format_str.format(**data)


def get_trust_dn(name):
    format_str = "cn={name},cn=ad,cn=trusts,{basedn}"
    data = dict(name=name, basedn=api.env.basedn)
    return format_str.format(**data)


def get_trusted_dom_range_dict(name, base_id, size, rangetype, base_rid, sid):
    return dict(
        objectClass=["ipaIDrange", "ipatrustedaddomainrange"],
        ipaBaseID=str("{base_id}".format(base_id=base_id)),
        ipaBaseRID=str("{base_rid}".format(base_rid=base_rid)),
        ipaIDRangeSize=str("{size}".format(size=size)),
        ipaNTTrustedDomainSID=str("{sid}".format(sid=sid)),
        ipaRangeType=str("{rangetype}".format(rangetype=rangetype)),
        )


def get_trusted_dom_dict(name, sid):
    return dict(
        objectClass=["ipaNTTrustedDomain", "ipaIDobject", "top"],
        ipaNTFlatName=str(name.upper()),
        ipaNTTrustedDomainSID=str(sid),
        ipaNTSIDBlacklistIncoming='S-1-0',
        ipaNTTrustPartner=str('{name}.mock'.format(name=name)),
        ipaNTTrustType='2',
        ipaNTTrustDirection='3',
        ipaNTTrustAttributes='8',
        )

# Domain ranges definitions

# Domain1 - AD domain nonactive (not present in LDAP)
domain1_sid = u'S-1-5-21-259319770-2312917334-591429603'

domain1range1 = u'domain1range1'
domain1range1_base_id = id_shift + 10000
domain1range1_size = 50
domain1range1_base_rid = rid_shift + 10000
domain1range1_type = u'ipa-ad-trust'

domain1range1_dn = get_range_dn(name=domain1range1)

domain1range1_add = get_trusted_dom_range_dict(
    name=domain1range1,
    base_id=domain1range1_base_id,
    size=domain1range1_size,
    rangetype=domain1range1_type,
    base_rid=domain1range1_base_rid,
    sid=domain1_sid
)

# Domain2 - AD domain active (present in LDAP)
domain2 = u'domain2'
domain2_dn = get_trust_dn(domain2)
domain2_sid = u'S-1-5-21-2997650941-1802118864-3094776726'

domain2_add = get_trusted_dom_dict(domain2, domain2_sid)

domain2range1 = u'domain2range1'
domain2range1_base_id = id_shift + 10100
domain2range1_size = 50
domain2range1_base_rid = rid_shift + 10100
domain2range1_type = u'ipa-ad-trust'

domain2range1_dn = get_range_dn(name=domain2range1)

domain2range1_add = get_trusted_dom_range_dict(
    name=domain2range1,
    base_id=domain2range1_base_id,
    size=domain2range1_size,
    rangetype=domain2range1_type,
    base_rid=domain2range1_base_rid,
    sid=domain2_sid
)


# Domain3 - Posix active AD domain, two posix ranges
domain3 = u'domain3'
domain3_dn = get_trust_dn(domain3)
domain3_sid = u'S-1-5-21-1980929950-1830687243-1002863068'

domain3_add = get_trusted_dom_dict(domain3, domain3_sid)

domain3range1 = u'domain3range1'
domain3range1_base_id = id_shift + 10200
domain3range1_size = 50
domain3range1_base_rid = rid_shift + 10200
domain3range1_type = u'ipa-ad-trust-posix'

domain3range1_dn = get_range_dn(name=domain3range1)

domain3range1_add = get_trusted_dom_range_dict(
    name=domain3range1,
    base_id=domain3range1_base_id,
    size=domain3range1_size,
    rangetype=domain3range1_type,
    base_rid=domain3range1_base_rid,
    sid=domain3_sid
)

domain3range2 = u'domain3range2'
domain3range2_base_id = id_shift + 10300
domain3range2_size = 50
domain3range2_base_rid = rid_shift + 10300
domain3range2_type = u'ipa-ad-trust-posix'

domain3range2_dn = get_range_dn(name=domain3range2)

domain3range2_add = get_trusted_dom_range_dict(
    name=domain3range2,
    base_id=domain3range2_base_id,
    size=domain3range2_size,
    rangetype=domain3range2_type,
    base_rid=domain3range2_base_rid,
    sid=domain3_sid
)

# Domain4 - Posix active AD domain, one posix range
domain4 = u'domain4'
domain4_dn = get_trust_dn(domain4)
domain4_sid = u'S-1-5-21-2630044516-2228086573-3500008130'

domain4_add = get_trusted_dom_dict(domain4, domain4_sid)

domain4range1 = u'domain4range1'
domain4range1_base_id = id_shift + 10400
domain4range1_size = 50
domain4range1_base_rid = rid_shift + 10400
domain4range1_type = u'ipa-ad-trust-posix'

domain4range1_dn = get_range_dn(name=domain4range1)

domain4range1_add = get_trusted_dom_range_dict(
    name=domain4range1,
    base_id=domain4range1_base_id,
    size=domain4range1_size,
    rangetype=domain4range1_type,
    base_rid=domain4range1_base_rid,
    sid=domain4_sid
)

# Domain5 - NonPosix active AD domain, two nonposix ranges
domain5 = u'domain5'
domain5_dn = get_trust_dn(domain5)
domain5_sid = u'S-1-5-21-2936727573-1940715531-2353349748'

domain5_add = get_trusted_dom_dict(domain5, domain5_sid)

domain5range1 = u'domain5range1'
domain5range1_base_id = id_shift + 10500
domain5range1_size = 50
domain5range1_base_rid = rid_shift + 10500
domain5range1_type = u'ipa-ad-trust'

domain5range1_dn = get_range_dn(name=domain5range1)

domain5range1_add = get_trusted_dom_range_dict(
    name=domain5range1,
    base_id=domain5range1_base_id,
    size=domain5range1_size,
    rangetype=domain5range1_type,
    base_rid=domain5range1_base_rid,
    sid=domain5_sid
)

domain5range2 = u'domain5range2'
domain5range2_base_id = id_shift + 10600
domain5range2_size = 50
domain5range2_base_rid = rid_shift + 10600
domain5range2_type = u'ipa-ad-trust'

domain5range2_dn = get_range_dn(name=domain5range2)

domain5range2_add = get_trusted_dom_range_dict(
    name=domain5range2,
    base_id=domain5range2_base_id,
    size=domain5range2_size,
    rangetype=domain5range2_type,
    base_rid=domain5range2_base_rid,
    sid=domain5_sid
)

# Domain6 - NonPosix active AD domain, one nonposix ranges
domain6 = u'domain6'
domain6_dn = get_trust_dn(domain6)
domain6_sid = u'S-1-5-21-2824814446-180299986-1494994477'

domain6_add = get_trusted_dom_dict(domain6, domain6_sid)

domain6range1 = u'domain6range1'
domain6range1_base_id = id_shift + 10700
domain6range1_size = 50
domain6range1_base_rid = rid_shift + 10700
domain6range1_type = u'ipa-ad-trust'

domain6range1_dn = get_range_dn(name=domain6range1)

domain6range1_add = get_trusted_dom_range_dict(
    name=domain6range1,
    base_id=domain6range1_base_id,
    size=domain6range1_size,
    rangetype=domain6range1_type,
    base_rid=domain6range1_base_rid,
    sid=domain6_sid
)


# Container for all trusted objects

trust_container_dn = "cn=ad,cn=trusts,{basedn}".format(basedn=api.env.basedn)
trust_container_add = dict(
    objectClass=["nsContainer", "top"]
    )

user1 = u'tuser1'
user1_uid = id_shift + 900000
group1 = u'group1'
group1_gid = id_shift + 900100


class test_range(Declarative):
    @classmethod
    def setUpClass(cls):
        super(test_range, cls).setUpClass()
        cls.tearDownClass()
        cls.mockldap = MockLDAP()
        cls.mockldap.add_entry(trust_container_dn, trust_container_add)

        cls.mockldap.add_entry(domain2_dn, domain2_add)
        cls.mockldap.add_entry(domain3_dn, domain3_add)
        cls.mockldap.add_entry(domain4_dn, domain4_add)
        cls.mockldap.add_entry(domain5_dn, domain5_add)
        cls.mockldap.add_entry(domain6_dn, domain6_add)

        cls.mockldap.add_entry(domain1range1_dn, domain1range1_add)
        cls.mockldap.add_entry(domain2range1_dn, domain2range1_add)
        cls.mockldap.add_entry(domain3range1_dn, domain3range1_add)
        cls.mockldap.add_entry(domain3range2_dn, domain3range2_add)
        cls.mockldap.add_entry(domain4range1_dn, domain4range1_add)
        cls.mockldap.add_entry(domain5range1_dn, domain5range1_add)
        cls.mockldap.add_entry(domain5range2_dn, domain5range2_add)
        cls.mockldap.add_entry(domain6range1_dn, domain6range1_add)
        cls.mockldap.unbind()

    @classmethod
    def tearDownClass(cls):
        cls.mockldap = MockLDAP()

        cls.mockldap.del_entry(domain2_dn)
        cls.mockldap.del_entry(domain3_dn)
        cls.mockldap.del_entry(domain4_dn)
        cls.mockldap.del_entry(domain5_dn)
        cls.mockldap.del_entry(domain6_dn)

        cls.mockldap.del_entry(domain1range1_dn)
        cls.mockldap.del_entry(domain2range1_dn)
        cls.mockldap.del_entry(domain3range1_dn)
        cls.mockldap.del_entry(domain3range2_dn)
        cls.mockldap.del_entry(domain4range1_dn)
        cls.mockldap.del_entry(domain5range1_dn)
        cls.mockldap.del_entry(domain5range2_dn)
        cls.mockldap.del_entry(domain6range1_dn)
        cls.mockldap.del_entry(trust_container_dn)
        cls.mockldap.unbind()

    cleanup_commands = [
        ('idrange_del', [testrange1, testrange2, testrange3, testrange4,
                         testrange5, testrange6, testrange7, testrange8],
                        {'continue': True}),
        ('user_del', [user1], {}),
        ('group_del', [group1], {}),
    ]

    # Basic tests.

    tests = [
        dict(
            desc='Create ID range %r' % (testrange1),
            command=('idrange_add', [testrange1],
                      dict(ipabaseid=testrange1_base_id, ipaidrangesize=testrange1_size,
                           ipabaserid=testrange1_base_rid, ipasecondarybaserid=testrange1_secondary_base_rid)),
            expected=dict(
                result=dict(
                    dn=DN(('cn',testrange1),('cn','ranges'),('cn','etc'),
                          api.env.basedn),
                    cn=[testrange1],
                    objectclass=[u'ipaIDrange', u'ipadomainidrange'],
                    ipabaseid=[unicode(testrange1_base_id)],
                    ipabaserid=[unicode(testrange1_base_rid)],
                    ipasecondarybaserid=[unicode(testrange1_secondary_base_rid)],
                    ipaidrangesize=[unicode(testrange1_size)],
                    iparangetype=[u'local domain range'],
                ),
                value=testrange1,
                summary=u'Added ID range "%s"' % (testrange1),
            ),
        ),

        dict(
            desc='Retrieve ID range %r' % (testrange1),
            command=('idrange_show', [testrange1], dict()),
            expected=dict(
                result=dict(
                    dn=DN(('cn',testrange1),('cn','ranges'),('cn','etc'),
                          api.env.basedn),
                    cn=[testrange1],
                    ipabaseid=[unicode(testrange1_base_id)],
                    ipabaserid=[unicode(testrange1_base_rid)],
                    ipasecondarybaserid=[unicode(testrange1_secondary_base_rid)],
                    ipaidrangesize=[unicode(testrange1_size)],
                    iparangetype=[u'local domain range'],
                ),
                value=testrange1,
                summary=None,
            ),
        ),

        # Checks for modifications leaving objects outside of the range.

        dict(
            desc='Create user %r in ID range %r' % (user1, testrange1),
            command=(
                'user_add', [user1], dict(givenname=u'Test', sn=u'User1',
                                          uidnumber=user1_uid)
            ),
            expected=dict(
                value=user1,
                summary=u'Added user "%s"' % user1,
                result=get_user_result(
                    user1, u'Test', u'User1', 'add',
                    uidnumber=[unicode(user1_uid)],
                    gidnumber=[unicode(user1_uid)],
                ),
            ),
        ),


        dict(
            desc='Create group %r in ID range %r' % (group1, testrange1),
            command=(
                'group_add', [group1], dict(description=u'Test desc 1',
                                            gidnumber=group1_gid)
            ),
            expected=dict(
                value=group1,
                summary=u'Added group "%s"' % group1,
                result=dict(
                    cn=[group1],
                    description=[u'Test desc 1'],
                    gidnumber=[unicode(group1_gid)],
                    objectclass=objectclasses.group + [u'posixgroup'],
                    ipauniqueid=[fuzzy_uuid],
                    dn=DN(('cn',group1),('cn','groups'),('cn','accounts'), api.env.basedn),
                ),
            ),
        ),


        dict(
            desc='Try to modify ID range %r to get out bounds object #1' % (testrange1),
            command=(
                'idrange_mod', [testrange1], dict(ipabaseid=user1_uid + 1)
            ),
            expected=errors.ValidationError(name='ipabaseid,ipaidrangesize',
                error=u'range modification leaving objects with ID out of the'
                      u' defined range is not allowed'),
        ),


        dict(
            desc='Try to modify ID range %r to get out bounds object #2' % (testrange1),
            command=('idrange_mod', [testrange1], dict(ipaidrangesize=100)),
            expected=errors.ValidationError(name='ipabaseid,ipaidrangesize',
                error=u'range modification leaving objects with ID out of the'
                      u' defined range is not allowed'),
        ),


        dict(
            desc='Try to modify ID range %r to get out bounds object #3' % (testrange1),
            command=('idrange_mod', [testrange1], dict(ipabaseid=100, ipaidrangesize=100)),
            expected=errors.ValidationError(name='ipabaseid,ipaidrangesize',
                error=u'range modification leaving objects with ID out of the'
                      u' defined range is not allowed'),
        ),


        dict(
            desc='Modify ID range %r' % (testrange1),
            command=('idrange_mod', [testrange1], dict(ipaidrangesize=90000)),
            expected=dict(
                result=dict(
                    cn=[testrange1],
                    ipabaseid=[unicode(testrange1_base_id)],
                    ipabaserid=[unicode(testrange1_base_rid)],
                    ipasecondarybaserid=[unicode(testrange1_secondary_base_rid)],
                    ipaidrangesize=[u'90000'],
                    iparangetype=[u'local domain range'],
                ),
                value=testrange1,
                summary=u'Modified ID range "%s"' % (testrange1),
            ),
        ),


        dict(
            desc='Try to delete ID range %r with active IDs inside it' % testrange1,
            command=('idrange_del', [testrange1], {}),
            expected=errors.ValidationError(name='ipabaseid,ipaidrangesize',
                error=u'range modification leaving objects with ID out of the'
                      u' defined range is not allowed'),
        ),


        dict(
            desc='Delete user %r' % user1,
            command=('user_del', [user1], {}),
            expected=dict(
                result=dict(failed=[]),
                value=[user1],
                summary=u'Deleted user "%s"' % user1,
            ),
        ),


        dict(
            desc='Delete group %r' % group1,
            command=('group_del', [group1], {}),
            expected=dict(
                result=dict(failed=[]),
                value=[group1],
                summary=u'Deleted group "%s"' % group1,
            ),
        ),


        dict(
            desc='Delete ID range %r' % testrange1,
            command=('idrange_del', [testrange1], {}),
            expected=dict(
                result=dict(failed=[]),
                value=[testrange1],
                summary=u'Deleted ID range "%s"' % testrange1,
            ),
        ),

        # Tests for overlapping local ranges.

        dict(
            desc='Create ID range %r' % (testrange2),
            command=('idrange_add', [testrange2],
                      dict(ipabaseid=testrange2_base_id,
                          ipaidrangesize=testrange2_size,
                          ipabaserid=testrange2_base_rid,
                          ipasecondarybaserid=testrange2_secondary_base_rid)),
            expected=dict(
                result=dict(
                    dn=DN(('cn',testrange2),('cn','ranges'),('cn','etc'),
                          api.env.basedn),
                    cn=[testrange2],
                    objectclass=[u'ipaIDrange', u'ipadomainidrange'],
                    ipabaseid=[unicode(testrange2_base_id)],
                    ipabaserid=[unicode(testrange2_base_rid)],
                    ipasecondarybaserid=[unicode(testrange2_secondary_base_rid)],
                    ipaidrangesize=[unicode(testrange2_size)],
                    iparangetype=[u'local domain range'],
                ),
                value=testrange2,
                summary=u'Added ID range "%s"' % (testrange2),
            ),
        ),

        dict(
            desc='Try to modify ID range %r so that its rid ranges are overlapping themselves' % (testrange2),
            command=('idrange_mod', [testrange2],
                      dict(ipabaserid=(testrange2_secondary_base_rid))),
            expected=errors.ValidationError(
                name='ID Range setup', error='Primary RID range and secondary RID range cannot overlap'),
        ),

        dict(
            desc='Try to create ID range %r with overlapping rid range' % (testrange3),
            command=('idrange_add', [testrange3],
                      dict(ipabaseid=testrange3_base_id,
                          ipaidrangesize=testrange3_size,
                          ipabaserid=testrange3_base_rid,
                          ipasecondarybaserid=testrange3_secondary_base_rid)),
            expected=errors.DatabaseError(
                desc='Constraint violation', info='New primary rid range overlaps with existing primary rid range.'),
        ),

       dict(
            desc='Try to create ID range %r with overlapping secondary rid range' % (testrange4),
            command=('idrange_add', [testrange4],
                      dict(ipabaseid=testrange4_base_id,
                          ipaidrangesize=testrange4_size,
                          ipabaserid=testrange4_base_rid,
                          ipasecondarybaserid=testrange4_secondary_base_rid)),
            expected=errors.DatabaseError(
                desc='Constraint violation', info='New secondary rid range overlaps with existing secondary rid range.'),
        ),

        dict(
            desc='Try to create ID range %r with primary range overlapping secondary rid range' % (testrange5),
            command=('idrange_add', [testrange5],
                      dict(ipabaseid=testrange5_base_id,
                          ipaidrangesize=testrange5_size,
                          ipabaserid=testrange5_base_rid,
                          ipasecondarybaserid=testrange5_secondary_base_rid)),
            expected=errors.DatabaseError(
                desc='Constraint violation', info='New primary rid range overlaps with existing secondary rid range.'),
        ),

        dict(
            desc='Try to create ID range %r with overlapping id range' % (testrange6),
            command=('idrange_add', [testrange6],
                      dict(ipabaseid=testrange6_base_id,
                          ipaidrangesize=testrange6_size,
                          ipabaserid=testrange6_base_rid,
                          ipasecondarybaserid=testrange6_secondary_base_rid)),
            expected=errors.DatabaseError(
                desc='Constraint violation', info='New base range overlaps with existing base range.'),
        ),

        dict(
            desc='Try to create ID range %r with rid ranges overlapping themselves' % (testrange7),
            command=('idrange_add', [testrange7],
                      dict(ipabaseid=testrange7_base_id,
                          ipaidrangesize=testrange7_size,
                          ipabaserid=testrange7_base_rid,
                          ipasecondarybaserid=testrange7_secondary_base_rid)),
            expected=errors.ValidationError(
                name='ID Range setup', error='Primary RID range and secondary RID range cannot overlap'),
        ),

        dict(
            desc='Delete ID range %r' % testrange2,
            command=('idrange_del', [testrange2], {}),
            expected=dict(
                result=dict(failed=[]),
                value=[testrange2],
                summary=u'Deleted ID range "%s"' % testrange2,
            ),
        ),

        # Testing framework validation: --dom-sid/--dom-name and secondary RID
        #                               base cannot be used together

        dict(
            desc='Create ID range %r' % (testrange8),
            command=('idrange_add', [testrange8],
                      dict(ipabaseid=testrange8_base_id,
                          ipaidrangesize=testrange8_size,
                          ipabaserid=testrange8_base_rid,
                          ipasecondarybaserid=testrange8_secondary_base_rid,
                          ipanttrusteddomainsid=domain1_sid)),
            expected=errors.ValidationError(
                name='ID Range setup', error='Options dom-sid/dom-name and '
                     'secondary-rid-base cannot be used together'),
        ),

        # Testing prohibition of deletion of ranges belonging to active
        # trusted domains.

        dict(
            desc='Delete non-active AD trusted range %r' % domain1range1,
            command=('idrange_del', [domain1range1], {}),
            expected=dict(
                result=dict(failed=[]),
                value=[domain1range1],
                summary=u'Deleted ID range "%s"' % domain1range1,
            ),
        ),

        dict(
            desc='Try to delete active AD trusted range %r' % domain2range1,
            command=('idrange_del', [domain2range1], {}),
            expected=errors.DependentEntry(
                    label='Active Trust domain',
                    key=domain2range1,
                    dependent=domain2),
        ),

        # Testing base range overlaps for ranges of different types and
        # different domains

        # - Base range overlaps

        # 1. ipa-ad-trust-posix type ranges from the same forest can overlap
        # on base ranges, use domain3range1 and domain3range2

        dict(
            desc=('Modify ipa-ad-trust-posix range %r to overlap on base range'
                  ' with posix range from the same domain' % (domain3range2)),
            command=('idrange_mod', [domain3range2],
                     dict(ipabaseid=domain3range1_base_id)),
            expected=dict(
                result=dict(
                    cn=[domain3range2],
                    ipabaseid=[unicode(domain3range1_base_id)],
                    ipabaserid=[unicode(domain3range2_base_rid)],
                    ipaidrangesize=[unicode(domain3range2_size)],
                    ipanttrusteddomainsid=[unicode(domain3_sid)],
                    iparangetype=[u'Active Directory trust range with POSIX '
                                   'attributes'],
                ),
                value=domain3range2,
                summary=u'Modified ID range "%s"' % (domain3range2),
            ),
        ),

        # 2. ipa-ad-trust-posix type ranges from different forests cannot
        # overlap on base ranges, use domain3range1 and domain4range1

        dict(
            desc=('Modify ipa-ad-trust-posix range %r to overlap on base range'
                  ' with posix range from different domain' % (domain3range1)),
            command=('idrange_mod', [domain3range1],
                     dict(ipabaseid=domain4range1_base_id)),
            expected=errors.DatabaseError(
                desc='Constraint violation',
                info='New base range overlaps with existing base range.'),
        ),

        # 3. ipa-ad-trust ranges from same forest cannot overlap on base ranges,
        # use domain5range1 and domain5range2

        dict(
            desc=('Modify ipa-ad-trust range %r to overlap on base range'
                  ' with posix range from the same domain' % (domain5range1)),
            command=('idrange_mod', [domain5range1],
                     dict(ipabaseid=domain5range2_base_id)),
            expected=errors.DatabaseError(
                desc='Constraint violation',
                info='New base range overlaps with existing base range.'),
        ),

        # 4. ipa-ad-trust ranges from different forests cannot overlap on base
        # ranges, use domain5range1 and domain6range1

        dict(
            desc=('Modify ipa-ad-trust range %r to overlap on base range'
                  ' with posix range from different domain' % (domain5range1)),
            command=('idrange_mod', [domain5range1],
                     dict(ipabaseid=domain6range1_base_id)),
            expected=errors.DatabaseError(
                desc='Constraint violation',
                info='New base range overlaps with existing base range.'),
        ),

        # - RID range overlaps

        # 1. Overlaps on base RID ranges are allowed for ranges from different
        # domains, use domain4range1 and domain5range1

        dict(
            desc=('Modify ipa-ad-trust-posix range %r to overlap on base RID'
                  ' range with nonposix range from different domain'
                  % (domain4range1)),
            command=('idrange_mod', [domain4range1],
                     dict(ipabaserid=domain5range1_base_rid)),
            expected=dict(
                result=dict(
                    cn=[domain4range1],
                    ipabaseid=[unicode(domain4range1_base_id)],
                    ipabaserid=[unicode(domain5range1_base_rid)],
                    ipaidrangesize=[unicode(domain4range1_size)],
                    ipanttrusteddomainsid=[unicode(domain4_sid)],
                    iparangetype=[u'Active Directory trust range with POSIX '
                                   'attributes'],
                ),
                value=domain4range1,
                summary=u'Modified ID range "%s"' % (domain4range1),
            ),
        ),

        # 2. ipa-ad-trust ranges from the same forest cannot overlap on base
        # RID ranges, use domain5range1 and domain5range2

        dict(
            desc=('Modify ipa-ad-trust range %r to overlap on base RID range'
                  ' with range from the same domain' % (domain5range1)),
            command=('idrange_mod', [domain5range1],
                     dict(ipabaserid=domain5range2_base_rid)),
            expected=errors.DatabaseError(
                desc='Constraint violation',
                info='New primary rid range overlaps with existing primary rid '
                     'range.'),
        ),

    ]
