On 04/29/2015 12:28 PM, Tomas Babej wrote:


On 03/11/2015 04:20 PM, Jan Cholasta wrote:
Hi,

Dne 10.3.2015 v 16:35 Tomas Babej napsal(a):

On 03/09/2015 12:26 PM, Tomas Babej wrote:
Hi,

this couple of patches provides a initial implementation of the
winsync migration tool:

https://fedorahosted.org/freeipa/ticket/4524

Some parts could use some polishing, but this is a sound foundation.

Tomas




Attaching one more patch to the bundle. This one should make the winsync
tool readily available after install.

Tomas



Nitpicks:

The winsync_migrate module should be in ipaserver.install. Also I don't see why it has to be a package when there is just one short file in it.

By convention, the AdminTool subclass should be named WinsyncMigrate, or the tool should be named ipa-migrate-winsync.

Honza


Updated patches attached.

Tomas

Rebased patches with cleaned membership bits.

Tomas
>From d98b18d7e1fe98eb81c9030b566ee28860a0d43e Mon Sep 17 00:00:00 2001
From: Tomas Babej <tba...@redhat.com>
Date: Wed, 29 Apr 2015 08:15:50 +0200
Subject: [PATCH] winsync-migrate: Add initial plumbing

https://fedorahosted.org/freeipa/ticket/4524
---
 install/tools/ipa-winsync-migrate     | 23 ++++++++++++
 ipaserver/winsync_migrate/__init__.py | 22 ++++++++++++
 ipaserver/winsync_migrate/base.py     | 67 +++++++++++++++++++++++++++++++++++
 3 files changed, 112 insertions(+)
 create mode 100755 install/tools/ipa-winsync-migrate
 create mode 100644 ipaserver/winsync_migrate/__init__.py
 create mode 100644 ipaserver/winsync_migrate/base.py

diff --git a/install/tools/ipa-winsync-migrate b/install/tools/ipa-winsync-migrate
new file mode 100755
index 0000000000000000000000000000000000000000..9eb9a03eb92396c855706e0f85850baece45b27e
--- /dev/null
+++ b/install/tools/ipa-winsync-migrate
@@ -0,0 +1,23 @@
+#! /usr/bin/python2 -E
+# Authors: Tomas Babej <tba...@redhat.com>
+#
+# Copyright (C) 2015  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/>.
+#
+
+from ipaserver.winsync_migrate.base import MigrateWinsync
+
+MigrateWinsync.run_cli()
diff --git a/ipaserver/winsync_migrate/__init__.py b/ipaserver/winsync_migrate/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e0da63db3a369adc4a34e8675471929b5839a812
--- /dev/null
+++ b/ipaserver/winsync_migrate/__init__.py
@@ -0,0 +1,22 @@
+# Authors: Tomas Babej <tba...@redhat.com>
+#
+# Copyright (C) 2015  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/>.
+#
+
+"""
+Base subpackage for winsync-migrate related code.
+"""
diff --git a/ipaserver/winsync_migrate/base.py b/ipaserver/winsync_migrate/base.py
new file mode 100644
index 0000000000000000000000000000000000000000..c21a861c2e49b4ab6d9783c117d1e4de126cb0b6
--- /dev/null
+++ b/ipaserver/winsync_migrate/base.py
@@ -0,0 +1,67 @@
+# Authors: Tomas Babej <tba...@redhat.com>
+#
+# Copyright (C) 2015  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/>.
+#
+
+import krbV
+import sys
+
+from ipalib import api
+from ipalib import errors
+from ipapython import admintool
+from ipapython.dn import DN
+from ipapython.ipa_log_manager import log_mgr
+from ipaserver.plugins.ldap2 import ldap2
+
+
+class MigrateWinsync(admintool.AdminTool):
+    """
+    Tool to migrate winsync users.
+    """
+
+    command_name = 'ipa-migrate-winsync'
+    usage = "ipa-migrate-winsync"
+    description = (
+        "This tool creates user ID overrides for all the users "
+        "that were previously synced from AD domain using the "
+        "winsync replication agreement. It requires that trust "
+        "with the AD forest has already been established and "
+        "the users in question are resolvable using SSSD. "
+        "For more information, see `man ipa-migrate-winsync`."
+        )
+
+    def run(self):
+        super(MigrateWinsync, self).run()
+
+        # Finalize API
+        api.bootstrap(in_server=True, context='server')
+        api.finalize()
+
+        # Setup LDAP connection
+        try:
+            ctx = krbV.default_context()
+            ccache = ctx.default_ccache()
+        except krbV.Krb5Error, e:
+            sys.exit("Must have Kerberos credentials to migrate Winsync users.")
+
+        try:
+            api.Backend.ldap2.connect(ccache)
+            self.ldap = api.Backend.ldap2
+        except errors.ACIError, e:
+            sys.exit("Outdated Kerberos credentials. Use kdestroy and kinit to update your ticket.")
+        except errors.DatabaseError, e:
+            sys.exit("Cannot connect to the LDAP database. Please check if IPA is running.")
-- 
2.1.0

>From 8521eb5fde599434a2584fe7103b7e8caf48a765 Mon Sep 17 00:00:00 2001
From: Tomas Babej <tba...@redhat.com>
Date: Wed, 29 Apr 2015 08:15:54 +0200
Subject: [PATCH] winsync-migrate: Add a way to find all winsync users

https://fedorahosted.org/freeipa/ticket/4524
---
 ipaserver/winsync_migrate/base.py | 25 +++++++++++++++++++++----
 1 file changed, 21 insertions(+), 4 deletions(-)

diff --git a/ipaserver/winsync_migrate/base.py b/ipaserver/winsync_migrate/base.py
index c21a861c2e49b4ab6d9783c117d1e4de126cb0b6..8403889049081704de9745578f8f37a46de54e3f 100644
--- a/ipaserver/winsync_migrate/base.py
+++ b/ipaserver/winsync_migrate/base.py
@@ -44,6 +44,23 @@ class MigrateWinsync(admintool.AdminTool):
         "For more information, see `man ipa-migrate-winsync`."
         )
 
+    def find_winsync_users(self):
+        """
+        Finds all users that were mirrored from AD using winsync.
+        """
+
+        user_filter = "(&(objectclass=ntuser)(ntUserDomainId=*))"
+        user_base = DN(api.env.container_user, api.env.basedn)
+        entries, _ = self.ldap.find_entries(
+            filter=user_filter,
+            base_dn=user_base,
+            paged_search=True)
+
+        for entry in entries:
+            self.log.debug("Discovered entry: %s" % entry)
+
+        return entries
+
     def run(self):
         super(MigrateWinsync, self).run()
 
@@ -55,13 +72,13 @@ class MigrateWinsync(admintool.AdminTool):
         try:
             ctx = krbV.default_context()
             ccache = ctx.default_ccache()
-        except krbV.Krb5Error, e:
-            sys.exit("Must have Kerberos credentials to migrate Winsync users.")
-
-        try:
             api.Backend.ldap2.connect(ccache)
             self.ldap = api.Backend.ldap2
+        except krbV.Krb5Error, e:
+            sys.exit("Must have Kerberos credentials to migrate Winsync users.")
         except errors.ACIError, e:
             sys.exit("Outdated Kerberos credentials. Use kdestroy and kinit to update your ticket.")
         except errors.DatabaseError, e:
             sys.exit("Cannot connect to the LDAP database. Please check if IPA is running.")
+
+        entries = self.find_winsync_users()
-- 
2.1.0

>From b8bd4fb5d1355eba8af6b056c9a70469223d42bc Mon Sep 17 00:00:00 2001
From: Tomas Babej <tba...@redhat.com>
Date: Wed, 29 Apr 2015 08:15:55 +0200
Subject: [PATCH] migrate-winsync: Create user ID overrides in place of
 winsynced user entries

https://fedorahosted.org/freeipa/ticket/4524
---
 ipaserver/winsync_migrate/base.py | 32 ++++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/ipaserver/winsync_migrate/base.py b/ipaserver/winsync_migrate/base.py
index 8403889049081704de9745578f8f37a46de54e3f..4d2ef4d3c2f91526fa7e0555ed5ddf2e69f7e202 100644
--- a/ipaserver/winsync_migrate/base.py
+++ b/ipaserver/winsync_migrate/base.py
@@ -27,6 +27,8 @@ from ipapython.dn import DN
 from ipapython.ipa_log_manager import log_mgr
 from ipaserver.plugins.ldap2 import ldap2
 
+DEFAULT_TRUST_VIEW_NAME = u'Default Trust View'
+
 
 class MigrateWinsync(admintool.AdminTool):
     """
@@ -44,6 +46,33 @@ class MigrateWinsync(admintool.AdminTool):
         "For more information, see `man ipa-migrate-winsync`."
         )
 
+    def create_id_user_override(self, entry):
+        """
+        Creates ID override corresponding to this user entry.
+        """
+
+        user_identifier = u"%s@%s" % (entry['uid'][0], self.options.realm)
+
+        kwargs = {
+            'uid': entry['uid'][0],
+            'uidnumber': entry['uidnumber'][0],
+            'gidnumber': entry['gidnumber'][0],
+            'gecos': entry['gecos'][0],
+            'loginshell': entry['loginshell'][0]
+        }
+
+        try:
+            result = api.Command['idoverrideuser_add'](
+                DEFAULT_TRUST_VIEW_NAME,
+                user_identifier,
+                **kwargs
+            )
+        except Exception as e:
+            self.log.warning("Migration failed: %s (%s)"
+                             % (user_identifier, str(e)))
+        else:
+            self.log.debug("Migrated: %s" % user_identifier)
+
     def find_winsync_users(self):
         """
         Finds all users that were mirrored from AD using winsync.
@@ -81,4 +110,7 @@ class MigrateWinsync(admintool.AdminTool):
         except errors.DatabaseError, e:
             sys.exit("Cannot connect to the LDAP database. Please check if IPA is running.")
 
+        # Create ID overrides replacing the user winsync entries
         entries = self.find_winsync_users()
+        for entry in entries:
+            self.create_id_user_override(entry)
-- 
2.1.0

>From 315564845a3c39f215ccf94d4a3679d863d9131f Mon Sep 17 00:00:00 2001
From: Tomas Babej <tba...@redhat.com>
Date: Wed, 29 Apr 2015 08:15:57 +0200
Subject: [PATCH] migrate-winsync: Add option validation and handling

https://fedorahosted.org/freeipa/ticket/4524
---
 ipaserver/winsync_migrate/base.py | 44 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 44 insertions(+)

diff --git a/ipaserver/winsync_migrate/base.py b/ipaserver/winsync_migrate/base.py
index 4d2ef4d3c2f91526fa7e0555ed5ddf2e69f7e202..60b75b5eb74b8aa975a4afd1c7d2a8a63674b1fb 100644
--- a/ipaserver/winsync_migrate/base.py
+++ b/ipaserver/winsync_migrate/base.py
@@ -46,6 +46,50 @@ class MigrateWinsync(admintool.AdminTool):
         "For more information, see `man ipa-migrate-winsync`."
         )
 
+    @classmethod
+    def add_options(cls, parser):
+        """
+        Adds command line options to the tool.
+        """
+        super(MigrateWinsync, cls).add_options(parser)
+
+        parser.add_option(
+            "--realm",
+            dest="realm",
+            help="The AD realm the winsynced users belong to")
+        parser.add_option(
+            "-U", "--unattended",
+            dest="interactive",
+            action="store_false",
+            default=True,
+            help="Never prompt for user input")
+
+    def validate_options(self):
+        """
+        Validates the options passed by the user:
+            - Checks that trust has been established with
+              the realm passed via --realm option
+        """
+
+        super(MigrateWinsync, self).validate_options()
+
+        if self.options.realm is None:
+            raise admintool.ScriptError(
+                "AD realm the winsynced users belong to needs to be "
+                "specified.")
+        else:
+            try:
+                api.Command['trust_show'](unicode(self.options.realm))
+            except errors.NotFound:
+                raise admintool.ScriptError(
+                    "Trust with the given realm %s could not be found. "
+                    "Please establish the trust prior to migration."
+                    % self.options.realm)
+            except Exception as e:
+                raise admintool.ScriptError(
+                    "An error occured during detection of the established "
+                    "trust with %s: %s" % (self.options.realm, str(e)))
+
     def create_id_user_override(self, entry):
         """
         Creates ID override corresponding to this user entry.
-- 
2.1.0

>From 7740d768e23b9f28e361f9e2c13e525fc5a42538 Mon Sep 17 00:00:00 2001
From: Tomas Babej <tba...@redhat.com>
Date: Wed, 29 Apr 2015 08:15:58 +0200
Subject: [PATCH] winsync-migrate: Move the api initalization and LDAP
 connection to the main method

https://fedorahosted.org/freeipa/ticket/4524
---
 ipaserver/winsync_migrate/base.py | 15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/ipaserver/winsync_migrate/base.py b/ipaserver/winsync_migrate/base.py
index 60b75b5eb74b8aa975a4afd1c7d2a8a63674b1fb..60c2cad669f8817890d5947671df7555ce55df9e 100644
--- a/ipaserver/winsync_migrate/base.py
+++ b/ipaserver/winsync_migrate/base.py
@@ -134,8 +134,12 @@ class MigrateWinsync(admintool.AdminTool):
 
         return entries
 
-    def run(self):
-        super(MigrateWinsync, self).run()
+    @classmethod
+    def main(cls, argv):
+        """
+        Sets up API and LDAP connection for the tool, then runs the rest of
+        the plumbing.
+        """
 
         # Finalize API
         api.bootstrap(in_server=True, context='server')
@@ -146,7 +150,7 @@ class MigrateWinsync(admintool.AdminTool):
             ctx = krbV.default_context()
             ccache = ctx.default_ccache()
             api.Backend.ldap2.connect(ccache)
-            self.ldap = api.Backend.ldap2
+            cls.ldap = api.Backend.ldap2
         except krbV.Krb5Error, e:
             sys.exit("Must have Kerberos credentials to migrate Winsync users.")
         except errors.ACIError, e:
@@ -154,6 +158,11 @@ class MigrateWinsync(admintool.AdminTool):
         except errors.DatabaseError, e:
             sys.exit("Cannot connect to the LDAP database. Please check if IPA is running.")
 
+        super(MigrateWinsync, cls).main(argv)
+
+    def run(self):
+        super(MigrateWinsync, self).run()
+
         # Create ID overrides replacing the user winsync entries
         entries = self.find_winsync_users()
         for entry in entries:
-- 
2.1.0

>From 46e1f4cc6027ff211e5fa67570df2d0081118138 Mon Sep 17 00:00:00 2001
From: Tomas Babej <tba...@redhat.com>
Date: Wed, 29 Apr 2015 08:16:00 +0200
Subject: [PATCH] dcerpc: Change logging level for debug information

https://fedorahosted.org/freeipa/ticket/4524
---
 ipaserver/dcerpc.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/ipaserver/dcerpc.py b/ipaserver/dcerpc.py
index e342c4973746a113c0ad4f15a1e6050583461ccf..72e7371219311471ed8cbb948da3b39839dc4126 100644
--- a/ipaserver/dcerpc.py
+++ b/ipaserver/dcerpc.py
@@ -356,7 +356,7 @@ class DomainValidator(object):
                error= _('Trusted domain did not return a valid SID for the object'))
 
     def get_trusted_domain_object_from_sid(self, sid):
-        root_logger.info("Converting SID to object name: %s" % sid)
+        root_logger.debug("Converting SID to object name: %s" % sid)
 
         # Check if the given SID is valid
         if not self.is_trusted_sid_valid(sid):
@@ -374,7 +374,7 @@ class DomainValidator(object):
                 return result.get(pysss_nss_idmap.NAME_KEY)
 
         # If unsuccessful, search AD DC LDAP
-        root_logger.info("Searching AD DC LDAP")
+        root_logger.debug("Searching AD DC LDAP")
 
         escaped_sid = escape_filter_chars(
             security.dom_sid(sid).__ndr_pack__(),
-- 
2.1.0

>From 00b9f2436e15dc2cfab6d12236ac3d5f19e1c066 Mon Sep 17 00:00:00 2001
From: Tomas Babej <tba...@redhat.com>
Date: Wed, 29 Apr 2015 08:16:01 +0200
Subject: [PATCH] dcerpc: Add debugging message to failing kinit as http

https://fedorahosted.org/freeipa/ticket/4524
---
 ipaserver/dcerpc.py | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/ipaserver/dcerpc.py b/ipaserver/dcerpc.py
index 72e7371219311471ed8cbb948da3b39839dc4126..48103e00420dce49c511febc3b8630515dfff75b 100644
--- a/ipaserver/dcerpc.py
+++ b/ipaserver/dcerpc.py
@@ -549,6 +549,8 @@ class DomainValidator(object):
         if returncode == 0:
             return (ccache_path, principal)
         else:
+            root_logger.debug('Kinit failed, stout: %s, stderr: %s'
+                              % (stdout, stderr))
             return (None, None)
 
     def search_in_dc(self, domain, filter, attrs, scope, basedn=None,
-- 
2.1.0

>From 7253811edf6230c4de0605c1cae3b6e6d2e0a6d4 Mon Sep 17 00:00:00 2001
From: Tomas Babej <tba...@redhat.com>
Date: Wed, 29 Apr 2015 08:16:03 +0200
Subject: [PATCH] winsync-migrate: Require root privileges

https://fedorahosted.org/freeipa/ticket/4524
---
 ipaserver/winsync_migrate/base.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/ipaserver/winsync_migrate/base.py b/ipaserver/winsync_migrate/base.py
index 60c2cad669f8817890d5947671df7555ce55df9e..936a7eee35352ab2c7a163b75ac9149d0702e505 100644
--- a/ipaserver/winsync_migrate/base.py
+++ b/ipaserver/winsync_migrate/base.py
@@ -71,7 +71,8 @@ class MigrateWinsync(admintool.AdminTool):
               the realm passed via --realm option
         """
 
-        super(MigrateWinsync, self).validate_options()
+        # Require root to have access to HTTP keytab
+        super(MigrateWinsync, self).validate_options(needs_root=True)
 
         if self.options.realm is None:
             raise admintool.ScriptError(
-- 
2.1.0

>From 9712c6c5ec36d886211b92fdb130bdb86aa3ddcc Mon Sep 17 00:00:00 2001
From: Tomas Babej <tba...@redhat.com>
Date: Wed, 29 Apr 2015 08:16:04 +0200
Subject: [PATCH] idviews: Do not abort the find & show commands on conversion
 errors

https://fedorahosted.org/freeipa/ticket/4524
---
 ipalib/plugins/idviews.py | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/ipalib/plugins/idviews.py b/ipalib/plugins/idviews.py
index 57f0cce1549edb4e582df225f7831916d96c216b..fcde2d3e6a18055401740530122266d50c2ec7d5 100644
--- a/ipalib/plugins/idviews.py
+++ b/ipalib/plugins/idviews.py
@@ -639,7 +639,12 @@ class baseidoverride_find(LDAPSearch):
 
     def post_callback(self, ldap, entries, truncated, *args, **options):
         for entry in entries:
-            self.obj.convert_anchor_to_human_readable_form(entry, **options)
+            try:
+                self.obj.convert_anchor_to_human_readable_form(entry, **options)
+            except errors.NotFound:
+                # If the conversion to readle form went wrong, do not
+                # abort the whole find command. Use non-converted entry.
+                pass
         return truncated
 
 
@@ -647,7 +652,12 @@ class baseidoverride_show(LDAPRetrieve):
     __doc__ = _('Display information about an ID override.')
 
     def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
-        self.obj.convert_anchor_to_human_readable_form(entry_attrs, **options)
+        try:
+            self.obj.convert_anchor_to_human_readable_form(entry_attrs, **options)
+        except errors.NotFound:
+            # If the conversion to readle form went wrong, do not
+            # abort the whole show command. Use non-converted entry.
+            pass
         return dn
 
 
-- 
2.1.0

>From ac4815cc3e2a092624d2d2910a4568fd6459d672 Mon Sep 17 00:00:00 2001
From: Tomas Babej <tba...@redhat.com>
Date: Wed, 29 Apr 2015 08:16:06 +0200
Subject: [PATCH] winsync-migrate: Require explicit specification of the target
 server and validate existing agreement

https://fedorahosted.org/freeipa/ticket/4524
---
 ipaserver/winsync_migrate/base.py | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/ipaserver/winsync_migrate/base.py b/ipaserver/winsync_migrate/base.py
index 936a7eee35352ab2c7a163b75ac9149d0702e505..afdbda2de0f531da18628b29f6d4430341e8f466 100644
--- a/ipaserver/winsync_migrate/base.py
+++ b/ipaserver/winsync_migrate/base.py
@@ -26,6 +26,7 @@ from ipapython import admintool
 from ipapython.dn import DN
 from ipapython.ipa_log_manager import log_mgr
 from ipaserver.plugins.ldap2 import ldap2
+from ipaserver.install import replication
 
 DEFAULT_TRUST_VIEW_NAME = u'Default Trust View'
 
@@ -58,6 +59,10 @@ class MigrateWinsync(admintool.AdminTool):
             dest="realm",
             help="The AD realm the winsynced users belong to")
         parser.add_option(
+            "--server",
+            dest="server",
+            help="The AD DC the winsync agreement is established with")
+        parser.add_option(
             "-U", "--unattended",
             dest="interactive",
             action="store_false",
@@ -91,6 +96,34 @@ class MigrateWinsync(admintool.AdminTool):
                     "An error occured during detection of the established "
                     "trust with %s: %s" % (self.options.realm, str(e)))
 
+        if self.options.server is None:
+            raise admintool.ScriptError(
+                "The AD DC the winsync agreement is established with "
+                "needs to be specified.")
+        else:
+            # Validate the replication agreement between given host and localhost
+            try:
+                manager = replication.ReplicationManager(
+                    api.env.realm,
+                    api.env.host,
+                    None)  # Use GSSAPI instead of raw directory manager access
+
+                replica_type = manager.get_agreement_type(self.options.server)
+            except errors.ACIError as e:
+                raise admintool.ScriptError(
+                    "Used Kerberos account does not have privileges to access "
+                    "the replication agreement info: %s" % str(e))
+            except errors.NotFound as e:
+                raise admintool.ScriptError(
+                    "The replication agreement between %s and %s could not "
+                    "be detected" % (api.env.host, self.options.server))
+
+            # Check that the replication agreement is indeed WINSYNC
+            if replica_type != replication.WINSYNC:
+                raise admintool.ScriptError(
+                    "Replication agreement between %s and %s is not winsync."
+                    % (api.env.host, self.options.server))
+
     def create_id_user_override(self, entry):
         """
         Creates ID override corresponding to this user entry.
-- 
2.1.0

>From 3c5e91c9ea4684e5a7e35fc9e38b117184372303 Mon Sep 17 00:00:00 2001
From: Tomas Babej <tba...@redhat.com>
Date: Wed, 29 Apr 2015 08:16:07 +0200
Subject: [PATCH] winsync-migrate: Delete winsync agreement prior to migration

https://fedorahosted.org/freeipa/ticket/4524
---
 ipaserver/winsync_migrate/base.py | 35 +++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/ipaserver/winsync_migrate/base.py b/ipaserver/winsync_migrate/base.py
index afdbda2de0f531da18628b29f6d4430341e8f466..21056a07040bb46f1418746a5b50506e5c4c85ff 100644
--- a/ipaserver/winsync_migrate/base.py
+++ b/ipaserver/winsync_migrate/base.py
@@ -24,6 +24,7 @@ from ipalib import api
 from ipalib import errors
 from ipapython import admintool
 from ipapython.dn import DN
+from ipapython.ipautil import realm_to_suffix
 from ipapython.ipa_log_manager import log_mgr
 from ipaserver.plugins.ldap2 import ldap2
 from ipaserver.install import replication
@@ -124,6 +125,36 @@ class MigrateWinsync(admintool.AdminTool):
                     "Replication agreement between %s and %s is not winsync."
                     % (api.env.host, self.options.server))
 
+            # Save the reference to the replication manager in the object
+            self.manager = manager
+
+    def delete_winsync_agreement(self):
+        """
+        Deletes the winsync agreement between the current master and the
+        given AD server.
+        """
+
+        try:
+            self.manager.delete_agreement(self.options.server)
+            self.manager.delete_referral(self.options.server)
+
+            dn = DN(('cn', self.options.server),
+                    ('cn', 'replicas'),
+                    ('cn', 'ipa'),
+                    ('cn', 'etc'),
+                    realm_to_suffix(api.env.realm))
+            entries = self.manager.conn.get_entries(dn,
+                                                    self.ldap.SCOPE_SUBTREE)
+            if entries:
+                entries.sort(key=len, reverse=True)
+                for entry in entries:
+                    self.ldap.delete_entry(entry)
+
+        except Exception as e:
+            raise admintool.ScriptError(
+                "Deletion of the winsync agreement failed: %s" % str(e))
+
+
     def create_id_user_override(self, entry):
         """
         Creates ID override corresponding to this user entry.
@@ -197,7 +228,11 @@ class MigrateWinsync(admintool.AdminTool):
     def run(self):
         super(MigrateWinsync, self).run()
 
+        # Stop winsync agreement with the given host
+        self.delete_winsync_agreement()
+
         # Create ID overrides replacing the user winsync entries
         entries = self.find_winsync_users()
         for entry in entries:
             self.create_id_user_override(entry)
+            self.ldap.delete_entry(entry)
-- 
2.1.0

>From ba735706875a99542603edaf76aa51aa3ba05821 Mon Sep 17 00:00:00 2001
From: Tomas Babej <tba...@redhat.com>
Date: Wed, 29 Apr 2015 08:16:09 +0200
Subject: [PATCH] winsync-migrate: Rename to tool to achive consistency with
 other tools

https://fedorahosted.org/freeipa/ticket/4524
---
 install/tools/ipa-winsync-migrate |  4 ++--
 ipaserver/winsync_migrate/base.py | 11 +++++------
 2 files changed, 7 insertions(+), 8 deletions(-)

diff --git a/install/tools/ipa-winsync-migrate b/install/tools/ipa-winsync-migrate
index 9eb9a03eb92396c855706e0f85850baece45b27e..9f24663124aa665bc8ccce4baf2911e5bb1cb632 100755
--- a/install/tools/ipa-winsync-migrate
+++ b/install/tools/ipa-winsync-migrate
@@ -18,6 +18,6 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
-from ipaserver.winsync_migrate.base import MigrateWinsync
+from ipaserver.winsync_migrate.base import WinsyncMigrate
 
-MigrateWinsync.run_cli()
+WinsyncMigrate.run_cli()
diff --git a/ipaserver/winsync_migrate/base.py b/ipaserver/winsync_migrate/base.py
index 21056a07040bb46f1418746a5b50506e5c4c85ff..cb62c7e1122585ae7a73d91219086de8767aa722 100644
--- a/ipaserver/winsync_migrate/base.py
+++ b/ipaserver/winsync_migrate/base.py
@@ -32,7 +32,7 @@ from ipaserver.install import replication
 DEFAULT_TRUST_VIEW_NAME = u'Default Trust View'
 
 
-class MigrateWinsync(admintool.AdminTool):
+class WinsyncMigrate(admintool.AdminTool):
     """
     Tool to migrate winsync users.
     """
@@ -53,7 +53,7 @@ class MigrateWinsync(admintool.AdminTool):
         """
         Adds command line options to the tool.
         """
-        super(MigrateWinsync, cls).add_options(parser)
+        super(WinsyncMigrate, cls).add_options(parser)
 
         parser.add_option(
             "--realm",
@@ -77,8 +77,7 @@ class MigrateWinsync(admintool.AdminTool):
               the realm passed via --realm option
         """
 
-        # Require root to have access to HTTP keytab
-        super(MigrateWinsync, self).validate_options(needs_root=True)
+        super(WinsyncMigrate, self).validate_options(needs_root=True)
 
         if self.options.realm is None:
             raise admintool.ScriptError(
@@ -223,10 +222,10 @@ class MigrateWinsync(admintool.AdminTool):
         except errors.DatabaseError, e:
             sys.exit("Cannot connect to the LDAP database. Please check if IPA is running.")
 
-        super(MigrateWinsync, cls).main(argv)
+        super(WinsyncMigrate, cls).main(argv)
 
     def run(self):
-        super(MigrateWinsync, self).run()
+        super(WinsyncMigrate, self).run()
 
         # Stop winsync agreement with the given host
         self.delete_winsync_agreement()
-- 
2.1.0

>From be235af551834c66acd7c2e67aa6aee4bd6784cc Mon Sep 17 00:00:00 2001
From: Tomas Babej <tba...@redhat.com>
Date: Wed, 29 Apr 2015 08:16:10 +0200
Subject: [PATCH] winsync-migrate: Move the tool under ipaserver.install
 package

https://fedorahosted.org/freeipa/ticket/4524
---
 install/tools/ipa-winsync-migrate                                     | 2 +-
 ipaserver/{winsync_migrate/base.py => install/ipa_winsync_migrate.py} | 0
 2 files changed, 1 insertion(+), 1 deletion(-)
 rename ipaserver/{winsync_migrate/base.py => install/ipa_winsync_migrate.py} (100%)

diff --git a/install/tools/ipa-winsync-migrate b/install/tools/ipa-winsync-migrate
index 9f24663124aa665bc8ccce4baf2911e5bb1cb632..7ecee8c39a10b437cc567ba4ad209351265fb580 100755
--- a/install/tools/ipa-winsync-migrate
+++ b/install/tools/ipa-winsync-migrate
@@ -18,6 +18,6 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
-from ipaserver.winsync_migrate.base import WinsyncMigrate
+from ipaserver.install.ipa_winsync_migrate import WinsyncMigrate
 
 WinsyncMigrate.run_cli()
diff --git a/ipaserver/winsync_migrate/base.py b/ipaserver/install/ipa_winsync_migrate.py
similarity index 100%
rename from ipaserver/winsync_migrate/base.py
rename to ipaserver/install/ipa_winsync_migrate.py
-- 
2.1.0

>From bc231c67a6a3efb7350a9dcc8925a7b8de99c079 Mon Sep 17 00:00:00 2001
From: Tomas Babej <tba...@redhat.com>
Date: Wed, 29 Apr 2015 08:16:12 +0200
Subject: [PATCH] winsync-migrate: Include the tool parts in Makefile and
 friends

https://fedorahosted.org/freeipa/ticket/4524
---
 freeipa.spec.in           | 1 +
 install/tools/Makefile.am | 1 +
 2 files changed, 2 insertions(+)

diff --git a/freeipa.spec.in b/freeipa.spec.in
index 725a6cfa55a40e688ca7f4ef18268c22fe46c06e..82f5ed25b66e5d0a285939c97ceba3e12e0751c5 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -669,6 +669,7 @@ fi
 %{_sbindir}/ipa-upgradeconfig
 %{_sbindir}/ipa-advise
 %{_sbindir}/ipa-cacert-manage
+%{_sbindir}/ipa-winsync-migrate
 %{_libexecdir}/certmonger/dogtag-ipa-ca-renew-agent-submit
 %{_libexecdir}/certmonger/ipa-server-guard
 %{_libexecdir}/ipa-otpd
diff --git a/install/tools/Makefile.am b/install/tools/Makefile.am
index e5d45c47966a503da9f25aec13175793a36962e4..99f81699ca32cce4703f313291ddb0793dceb063 100644
--- a/install/tools/Makefile.am
+++ b/install/tools/Makefile.am
@@ -28,6 +28,7 @@ sbin_SCRIPTS =			\
 	ipa-restore		\
 	ipa-advise		\
 	ipa-cacert-manage	\
+	ipa-winsync-migrate	\
 	$(NULL)
 
 EXTRA_DIST =			\
-- 
2.1.0

>From 2182e74b2ab2c7e89c36b887908c93817903eb86 Mon Sep 17 00:00:00 2001
From: Tomas Babej <tba...@redhat.com>
Date: Wed, 29 Apr 2015 08:16:13 +0200
Subject: [PATCH] idviews: Fallback to AD DC LDAP only if specifically allowed

https://fedorahosted.org/freeipa/ticket/4524
---
 API.txt                   | 30 ++++++++++++++++++++----------
 ipalib/plugins/idviews.py | 27 ++++++++++++++++++++++++---
 ipaserver/dcerpc.py       |  7 ++++++-
 3 files changed, 50 insertions(+), 14 deletions(-)

diff --git a/API.txt b/API.txt
index f747765d7f9c87761fed0277cd59d1bc3fbd57e9..36c297d06d2b6d4f25b236a411ecae810a9ce6b4 100644
--- a/API.txt
+++ b/API.txt
@@ -2091,13 +2091,14 @@ args: 0,1,1
 option: Str('version?', exclude='webui')
 output: Output('texts', <type 'dict'>, None)
 command: idoverridegroup_add
-args: 2,8,3
+args: 2,9,3
 arg: Str('idviewcn', cli_name='idview', multivalue=False, primary_key=True, query=True, required=True)
 arg: Str('ipaanchoruuid', attribute=True, cli_name='anchor', multivalue=False, primary_key=True, required=True)
 option: Str('addattr*', cli_name='addattr', exclude='webui')
 option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
 option: Str('cn', attribute=True, cli_name='group_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.][a-zA-Z0-9_.-]{0,252}[a-zA-Z0-9_.$-]?$', required=False)
 option: Str('description', attribute=True, cli_name='desc', multivalue=False, required=False)
+option: Flag('fallback_to_ldap?', autofill=True, default=False)
 option: Int('gidnumber', attribute=True, cli_name='gid', minvalue=1, multivalue=False, required=False)
 option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
 option: Str('setattr*', cli_name='setattr', exclude='webui')
@@ -2106,21 +2107,23 @@ output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDA
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: PrimaryKey('value', None, None)
 command: idoverridegroup_del
-args: 2,2,3
+args: 2,3,3
 arg: Str('idviewcn', cli_name='idview', multivalue=False, primary_key=True, query=True, required=True)
 arg: Str('ipaanchoruuid', attribute=True, cli_name='anchor', multivalue=True, primary_key=True, query=True, required=True)
 option: Flag('continue', autofill=True, cli_name='continue', default=False)
+option: Flag('fallback_to_ldap?', autofill=True, default=False)
 option: Str('version?', exclude='webui')
 output: Output('result', <type 'dict'>, None)
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: ListOfPrimaryKeys('value', None, None)
 command: idoverridegroup_find
-args: 2,10,4
+args: 2,11,4
 arg: Str('idviewcn', cli_name='idview', multivalue=False, primary_key=True, query=True, required=True)
 arg: Str('criteria?', noextrawhitespace=False)
 option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
 option: Str('cn', attribute=True, autofill=False, cli_name='group_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.][a-zA-Z0-9_.-]{0,252}[a-zA-Z0-9_.$-]?$', query=True, required=False)
 option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, query=True, required=False)
+option: Flag('fallback_to_ldap?', autofill=True, default=False)
 option: Int('gidnumber', attribute=True, autofill=False, cli_name='gid', minvalue=1, multivalue=False, query=True, required=False)
 option: Str('ipaanchoruuid', attribute=True, autofill=False, cli_name='anchor', multivalue=False, primary_key=True, query=True, required=False)
 option: Flag('pkey_only?', autofill=True, default=False)
@@ -2133,7 +2136,7 @@ output: ListOfEntries('result', (<type 'list'>, <type 'tuple'>), Gettext('A list
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: Output('truncated', <type 'bool'>, None)
 command: idoverridegroup_mod
-args: 2,11,3
+args: 2,12,3
 arg: Str('idviewcn', cli_name='idview', multivalue=False, primary_key=True, query=True, required=True)
 arg: Str('ipaanchoruuid', attribute=True, cli_name='anchor', multivalue=False, primary_key=True, query=True, required=True)
 option: Str('addattr*', cli_name='addattr', exclude='webui')
@@ -2141,6 +2144,7 @@ option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui
 option: Str('cn', attribute=True, autofill=False, cli_name='group_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.][a-zA-Z0-9_.-]{0,252}[a-zA-Z0-9_.$-]?$', required=False)
 option: Str('delattr*', cli_name='delattr', exclude='webui')
 option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, required=False)
+option: Flag('fallback_to_ldap?', autofill=True, default=False)
 option: Int('gidnumber', attribute=True, autofill=False, cli_name='gid', minvalue=1, multivalue=False, required=False)
 option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
 option: Str('rename', cli_name='rename', multivalue=False, primary_key=True, required=False)
@@ -2151,10 +2155,11 @@ output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDA
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: PrimaryKey('value', None, None)
 command: idoverridegroup_show
-args: 2,4,3
+args: 2,5,3
 arg: Str('idviewcn', cli_name='idview', multivalue=False, primary_key=True, query=True, required=True)
 arg: Str('ipaanchoruuid', attribute=True, cli_name='anchor', multivalue=False, primary_key=True, query=True, required=True)
 option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: Flag('fallback_to_ldap?', autofill=True, default=False)
 option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
 option: Flag('rights', autofill=True, default=False)
 option: Str('version?', exclude='webui')
@@ -2162,12 +2167,13 @@ output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDA
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: PrimaryKey('value', None, None)
 command: idoverrideuser_add
-args: 2,14,3
+args: 2,15,3
 arg: Str('idviewcn', cli_name='idview', multivalue=False, primary_key=True, query=True, required=True)
 arg: Str('ipaanchoruuid', attribute=True, cli_name='anchor', multivalue=False, primary_key=True, required=True)
 option: Str('addattr*', cli_name='addattr', exclude='webui')
 option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
 option: Str('description', attribute=True, cli_name='desc', multivalue=False, required=False)
+option: Flag('fallback_to_ldap?', autofill=True, default=False)
 option: Str('gecos', attribute=True, cli_name='gecos', multivalue=False, required=False)
 option: Int('gidnumber', attribute=True, cli_name='gidnumber', minvalue=1, multivalue=False, required=False)
 option: Str('homedirectory', attribute=True, cli_name='homedir', multivalue=False, required=False)
@@ -2183,20 +2189,22 @@ output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDA
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: PrimaryKey('value', None, None)
 command: idoverrideuser_del
-args: 2,2,3
+args: 2,3,3
 arg: Str('idviewcn', cli_name='idview', multivalue=False, primary_key=True, query=True, required=True)
 arg: Str('ipaanchoruuid', attribute=True, cli_name='anchor', multivalue=True, primary_key=True, query=True, required=True)
 option: Flag('continue', autofill=True, cli_name='continue', default=False)
+option: Flag('fallback_to_ldap?', autofill=True, default=False)
 option: Str('version?', exclude='webui')
 output: Output('result', <type 'dict'>, None)
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: ListOfPrimaryKeys('value', None, None)
 command: idoverrideuser_find
-args: 2,15,4
+args: 2,16,4
 arg: Str('idviewcn', cli_name='idview', multivalue=False, primary_key=True, query=True, required=True)
 arg: Str('criteria?', noextrawhitespace=False)
 option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
 option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, query=True, required=False)
+option: Flag('fallback_to_ldap?', autofill=True, default=False)
 option: Str('gecos', attribute=True, autofill=False, cli_name='gecos', multivalue=False, query=True, required=False)
 option: Int('gidnumber', attribute=True, autofill=False, cli_name='gidnumber', minvalue=1, multivalue=False, query=True, required=False)
 option: Str('homedirectory', attribute=True, autofill=False, cli_name='homedir', multivalue=False, query=True, required=False)
@@ -2215,13 +2223,14 @@ output: ListOfEntries('result', (<type 'list'>, <type 'tuple'>), Gettext('A list
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: Output('truncated', <type 'bool'>, None)
 command: idoverrideuser_mod
-args: 2,17,3
+args: 2,18,3
 arg: Str('idviewcn', cli_name='idview', multivalue=False, primary_key=True, query=True, required=True)
 arg: Str('ipaanchoruuid', attribute=True, cli_name='anchor', multivalue=False, primary_key=True, query=True, required=True)
 option: Str('addattr*', cli_name='addattr', exclude='webui')
 option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
 option: Str('delattr*', cli_name='delattr', exclude='webui')
 option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, required=False)
+option: Flag('fallback_to_ldap?', autofill=True, default=False)
 option: Str('gecos', attribute=True, autofill=False, cli_name='gecos', multivalue=False, required=False)
 option: Int('gidnumber', attribute=True, autofill=False, cli_name='gidnumber', minvalue=1, multivalue=False, required=False)
 option: Str('homedirectory', attribute=True, autofill=False, cli_name='homedir', multivalue=False, required=False)
@@ -2239,10 +2248,11 @@ output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDA
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: PrimaryKey('value', None, None)
 command: idoverrideuser_show
-args: 2,4,3
+args: 2,5,3
 arg: Str('idviewcn', cli_name='idview', multivalue=False, primary_key=True, query=True, required=True)
 arg: Str('ipaanchoruuid', attribute=True, cli_name='anchor', multivalue=False, primary_key=True, query=True, required=True)
 option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: Flag('fallback_to_ldap?', autofill=True, default=False)
 option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
 option: Flag('rights', autofill=True, default=False)
 option: Str('version?', exclude='webui')
diff --git a/ipalib/plugins/idviews.py b/ipalib/plugins/idviews.py
index fcde2d3e6a18055401740530122266d50c2ec7d5..e369a6707b68a632657671c9f29c45ad3f828416 100644
--- a/ipalib/plugins/idviews.py
+++ b/ipalib/plugins/idviews.py
@@ -53,8 +53,17 @@ protected_default_trust_view_error = errors.ProtectedEntryError(
     reason=_('system ID View')
 )
 
+fallback_to_ldap_option = Flag(
+    'fallback_to_ldap?',
+    default=False,
+    label=_('Fallback to AD DC LDAP'),
+    doc=_("Allow falling back to AD DC LDAP when resolving AD "
+          "trusted objects. For two-way trusts only."),
+)
+
 DEFAULT_TRUST_VIEW_NAME = "default trust view"
 
+
 @register()
 class idview(LDAPObject):
     """
@@ -414,7 +423,7 @@ class idview_unapply(baseidview_apply):
 
 
 # ID overrides helper methods
-def resolve_object_to_anchor(ldap, obj_type, obj):
+def resolve_object_to_anchor(ldap, obj_type, obj, fallback_to_ldap):
     """
     Resolves the user/group name to the anchor uuid:
         - first it tries to find the object as user or group in IPA (depending
@@ -461,7 +470,8 @@ def resolve_object_to_anchor(ldap, obj_type, obj):
         if _dcerpc_bindings_installed:
             domain_validator = ipaserver.dcerpc.DomainValidator(api)
             if domain_validator.is_configured():
-                sid = domain_validator.get_trusted_domain_object_sid(obj)
+                sid = domain_validator.get_trusted_domain_object_sid(obj,
+                        fallback_to_ldap=fallback_to_ldap)
 
                 # There is no domain prefix since SID contains information
                 # about the domain
@@ -560,7 +570,8 @@ class baseidoverride(LDAPObject):
         anchor = resolve_object_to_anchor(
             self.backend,
             self.override_object,
-            keys[-1]
+            keys[-1],
+            fallback_to_ldap=options['fallback_to_ldap']
         )
 
         keys = keys[:-1] + (anchor, )
@@ -598,6 +609,8 @@ class baseidoverride_add(LDAPCreate):
     __doc__ = _('Add a new ID override.')
     msg_summary = _('Added ID override "%(value)s"')
 
+    takes_options = LDAPCreate.takes_options + (fallback_to_ldap_option,)
+
     def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
         self.obj.set_anchoruuid_from_dn(dn, entry_attrs)
         self.obj.prohibit_ipa_users_in_default_view(dn, entry_attrs)
@@ -612,11 +625,15 @@ class baseidoverride_del(LDAPDelete):
     __doc__ = _('Delete an ID override.')
     msg_summary = _('Deleted ID override "%(value)s"')
 
+    takes_options = LDAPDelete.takes_options + (fallback_to_ldap_option,)
+
 
 class baseidoverride_mod(LDAPUpdate):
     __doc__ = _('Modify an ID override.')
     msg_summary = _('Modified an ID override "%(value)s"')
 
+    takes_options = LDAPUpdate.takes_options + (fallback_to_ldap_option,)
+
     def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
         if 'rename' in options:
             raise errors.ValidationError(
@@ -637,6 +654,8 @@ class baseidoverride_find(LDAPSearch):
     msg_summary = ngettext('%(count)d ID override matched',
                            '%(count)d ID overrides matched', 0)
 
+    takes_options = LDAPSearch.takes_options + (fallback_to_ldap_option,)
+
     def post_callback(self, ldap, entries, truncated, *args, **options):
         for entry in entries:
             try:
@@ -651,6 +670,8 @@ class baseidoverride_find(LDAPSearch):
 class baseidoverride_show(LDAPRetrieve):
     __doc__ = _('Display information about an ID override.')
 
+    takes_options = LDAPRetrieve.takes_options + (fallback_to_ldap_option,)
+
     def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
         try:
             self.obj.convert_anchor_to_human_readable_form(entry_attrs, **options)
diff --git a/ipaserver/dcerpc.py b/ipaserver/dcerpc.py
index 48103e00420dce49c511febc3b8630515dfff75b..7f127ec85cf9207c332b75dbefe78e6a9b697b0b 100644
--- a/ipaserver/dcerpc.py
+++ b/ipaserver/dcerpc.py
@@ -323,12 +323,17 @@ class DomainValidator(object):
 
         return entries
 
-    def get_trusted_domain_object_sid(self, object_name):
+    def get_trusted_domain_object_sid(self, object_name, fallback_to_ldap=True):
         result = pysss_nss_idmap.getsidbyname(object_name)
         if object_name in result and (pysss_nss_idmap.SID_KEY in result[object_name]):
             object_sid = result[object_name][pysss_nss_idmap.SID_KEY]
             return object_sid
 
+        # If fallback to AD DC LDAP is not allowed, bail out
+        if not fallback_to_ldap:
+            raise errors.ValidationError(name=_('trusted domain object'),
+               error= _('SSSD was unable to resolve the object to a valid SID'))
+
         # Else, we are going to contact AD DC LDAP
         components = normalize_name(object_name)
         if not ('domain' in components or 'flatname' in components):
-- 
2.1.0

>From 81836e9392d8f17b1167554f77eb49fc49deb0f8 Mon Sep 17 00:00:00 2001
From: Tomas Babej <tba...@redhat.com>
Date: Wed, 29 Apr 2015 08:16:17 +0200
Subject: [PATCH] man: Add manpage for ipa-winsync-migrate

https://fedorahosted.org/freeipa/ticket/4524
---
 freeipa.spec.in                         |  1 +
 install/tools/man/Makefile.am           |  1 +
 install/tools/man/ipa-winsync-migrate.1 | 27 +++++++++++++++++++++++++++
 3 files changed, 29 insertions(+)
 create mode 100644 install/tools/man/ipa-winsync-migrate.1

diff --git a/freeipa.spec.in b/freeipa.spec.in
index 82f5ed25b66e5d0a285939c97ceba3e12e0751c5..2a03cc2102186e593496a7176a4721831d517285 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -820,6 +820,7 @@ fi
 %{_mandir}/man1/ipa-advise.1.gz
 %{_mandir}/man1/ipa-otptoken-import.1.gz
 %{_mandir}/man1/ipa-cacert-manage.1.gz
+%{_mandir}/man1/ipa-winsync-migrate.1.gz
 
 %files server-trust-ad
 %{_sbindir}/ipa-adtrust-install
diff --git a/install/tools/man/Makefile.am b/install/tools/man/Makefile.am
index 6db1776191ca855986a152dbd4854a0dc1b744d7..cb5bb152e1a1936131b8d19353e6e2544eb49863 100644
--- a/install/tools/man/Makefile.am
+++ b/install/tools/man/Makefile.am
@@ -26,6 +26,7 @@ man1_MANS = 				\
 	ipa-advise.1			\
 	ipa-otptoken-import.1		\
 	ipa-cacert-manage.1		\
+	ipa-winsync-migrate.1		\
         $(NULL)
 
 man8_MANS =				\
diff --git a/install/tools/man/ipa-winsync-migrate.1 b/install/tools/man/ipa-winsync-migrate.1
new file mode 100644
index 0000000000000000000000000000000000000000..a1e01c83da6017d5cbe10297dbe84a4dd1741ec7
--- /dev/null
+++ b/install/tools/man/ipa-winsync-migrate.1
@@ -0,0 +1,27 @@
+.\" A man page for ipa-advise
+.\" Copyright (C) 2013 Red Hat, Inc.
+.\"
+.\" 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/>.
+.\"
+.\" Author: Tomas Babej <tba...@redhat.com>
+.\"
+.TH "ipa-advise" "1" "Mar 10 2015" "FreeIPA" "FreeIPA Manual Pages"
+.SH "NAME"
+ipa\-winsync\-migrate \- Seamless migration of AD users created by winsync to native AD users.
+.SH "SYNOPSIS"
+ipa\-winsync\-migrate
+.SH "DESCRIPTION"
+Migrates AD users created by winsync agreement to ID overrides in
+the Default Trust View, thus preserving the actual POSIX attributes
+already established.
-- 
2.1.0

>From fbe911fe7c96257d8969400d18847274adbe2e1b Mon Sep 17 00:00:00 2001
From: Tomas Babej <tba...@redhat.com>
Date: Tue, 5 May 2015 12:41:12 +0200
Subject: [PATCH] winsync_migrate: Migrate memberships of the winsynced users

https://fedorahosted.org/freeipa/ticket/4524
---
 ipaserver/install/ipa_winsync_migrate.py | 45 ++++++++++++++++++++++++++++++++
 1 file changed, 45 insertions(+)

diff --git a/ipaserver/install/ipa_winsync_migrate.py b/ipaserver/install/ipa_winsync_migrate.py
index cb62c7e1122585ae7a73d91219086de8767aa722..b7d65666a3eb5e93c15a09607ef412b679f69b59 100644
--- a/ipaserver/install/ipa_winsync_migrate.py
+++ b/ipaserver/install/ipa_winsync_migrate.py
@@ -198,6 +198,50 @@ class WinsyncMigrate(admintool.AdminTool):
 
         return entries
 
+    def migrate_memberships(self, entry):
+        """
+        Migrates user memberships to the external identity.
+        """
+
+        def winsync_group_name(group_entry):
+            """
+            Returns the generated name of group containing migrated external users
+            """
+
+            return u"%s_winsync_external" % group_entry['cn'][0]
+
+        def create_winsync_group(group_entry):
+            """
+            Creates the group containing migrated external users that were
+            previously available via winsync.
+            """
+
+            name = winsync_group_name(group_entry)
+            api.Command['group_add'](name, external=True)
+            api.Command['group_add_member'](group_entry['cn'][0], group=[name])
+
+        # Search for all groups containing the given user as a direct member
+        member_filter = self.ldap.make_filter_from_attr('member', entry.dn)
+        groups, _ = self.ldap.find_entries(member_filter, base_dn=api.env.basedn)
+
+        # The external user cannot be added directly to the IPA groups, hence
+        # we need to wrap all the external users into one new external group,
+        # which will be then added to the original IPA group as a member.
+
+        for group in groups:
+            # Check for existence of winsync external group
+            name = winsync_group_name(group)
+            info = api.Command['group_show'](group['cn'][0])['result']
+
+            # If it was not created yet, do it now
+            if name not in info.get('member_groups', []):
+                create_winsync_group(group)
+
+            # Add the user to the external group. Membership is migrated
+            # at this point.
+            user_identifier = u"%s@%s" % (entry['uid'][0], self.options.realm)
+            api.Command['group_add_member'](name, user=[user_identifier])
+
     @classmethod
     def main(cls, argv):
         """
@@ -234,4 +278,5 @@ class WinsyncMigrate(admintool.AdminTool):
         entries = self.find_winsync_users()
         for entry in entries:
             self.create_id_user_override(entry)
+            self.migrate_memberships(entry)
             self.ldap.delete_entry(entry)
-- 
2.1.0

-- 
Manage your subscription for the Freeipa-devel mailing list:
https://www.redhat.com/mailman/listinfo/freeipa-devel
Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code

Reply via email to