On 06/16/2015 01:01 PM, Jan Cholasta wrote:
> Dne 16.6.2015 v 10:14 Martin Babinsky napsal(a):
>> On 05/06/2015 10:12 AM, Tomas Babej wrote:
>>>
>>>
>>> On 05/05/2015 02:02 PM, Tomas Babej wrote:
>>>>
>>>>
>>>> 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
>>>
>>> I did some self-review, updated patches attached.
>>>
>>>
>> Hi Tomas,
>>
>> patches look good and seem to work as expected. I have some comments:
>>
>> 1.) When running the tool I get a number of warnings about users not
>> found (https://paste.fedoraproject.org/232251/43884831/), but in the end
>> everything seems to be fine and users are migrated in the external
>> groups just fine. Is this behavior normal?
>>

In that case, yes. What happened here is that SSSD in POSIX trust will
not resolve users that do not have POSIX attributes set. Winsync
synchornizes all the users, hence the discrepancy.


>> 2.) Since both "--realm" and "--server" options are mandatory, I was
>> thinking if it would be better to use positional arguments, since you
>> always have to specify them. What are your thought on this?
> 
> I would rather stay consistent with ipa-server-install and friends and
> keep them as options.
> 
>>
>> 3.) Patches 317-318 seem to just just rename/move things and could be
>> squashed in the previous ones. But that is just a minor thing and I
>> leave that to your discretion.
>>
>> 4.) After all the renaming and moving around the WinsyncMigrate class
>> (see previous point) there is an unused file
>> "ipaserver/winsync_migrate/__init__.py" left. You should remove it in
>> some patch (e.g. in patch 318 if you decide to keep it).

I removed the file and squashed the change into 318.

> 
> Also please rename the class to "MigrateWinsync", for consistency.
> 

Naming is consistent, the tool is called ipa-winsync-migrate, class is
called WinsyncMigrate. This is consistent with other IPA tools.


>>
>> 5.) Option "--log-file" seems to be broken. When specified on CLI the
>> log is created but empty, the program prints out nothing and then exits
>> without doing anything. However, I suspect that this is AdminTool's
>> problem, not yours.
>>

Yep. Please, file a ticket for this more generic issue.

From 3be5216cb2c58808a56755e03463c2d370921ec4 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 6f7397622e28f81e9a0ec94974004bca86924503 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 d29030ecfe910071b0903cf0fba345b0f2d8388f 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 da2c9417d60c63556094338ebda591ebb34c5539 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 8a6e37a16b7679184012f03a66ed3e4c60c09e09 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 015cabb3aa04ba552643eb91c3210213439ae7a4 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 a1c57d22ca1711661f9b921baf2eb4c3c0657fc3..b6eaa4e2a7afae3e7ac1d149a467cb036f67012b 100644
--- a/ipaserver/dcerpc.py
+++ b/ipaserver/dcerpc.py
@@ -360,7 +360,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):
@@ -378,7 +378,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 d137a976d6396d51ac8d3ff1283067dff71e399d 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 b6eaa4e2a7afae3e7ac1d149a467cb036f67012b..cce9f6f7787190da17b6719f70a317a15a92e489 100644
--- a/ipaserver/dcerpc.py
+++ b/ipaserver/dcerpc.py
@@ -553,6 +553,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 e1d453cdeb75227bd26607894b21868289c3b74f 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 5aa8fee6ed4dc25d87197695b6b61cce1c0d1187 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 db5ce9b38cc82f82fce8d71e9e67a94fb3a28c4d 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 7d2bebc39d5d119294ae23c215c6fe7ed2c4b6e1 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 2f3376f1fbe29268cca35b259572a37622910037 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/__init__.py | 22 ----------------------
 ipaserver/winsync_migrate/base.py     | 11 +++++------
 3 files changed, 7 insertions(+), 30 deletions(-)
 delete mode 100644 ipaserver/winsync_migrate/__init__.py

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/__init__.py b/ipaserver/winsync_migrate/__init__.py
deleted file mode 100644
index e0da63db3a369adc4a34e8675471929b5839a812..0000000000000000000000000000000000000000
--- a/ipaserver/winsync_migrate/__init__.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# 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
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 a89c6739ee2f6fb1bebd14e63ad6e67223ec43c2 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 659dfe9149d6df155dedaac1cdf5d00d30437d5c 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 4f08db9f693318c6f4bfaf5e634ccffa78a4a28c..91d66ca1b4576d55519d36dc57b175b8e7515d94 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -696,6 +696,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 147ce33a9d8a6471b46a3b6f36f6ce3e8237700b..7212dabdbf968ee76cb2830e5054fe1a27712225 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 78fc315ae14b3ba9a45b9279c6a0c07bcbff7f63 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 bccebe55da8a785cbb6ca782904d7523c4a9322f..17a1afe01a1b32c9009f8c4362050260e993ebff 100644
--- a/API.txt
+++ b/API.txt
@@ -2347,13 +2347,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')
@@ -2362,21 +2363,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)
@@ -2389,7 +2392,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')
@@ -2397,6 +2400,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)
@@ -2407,10 +2411,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')
@@ -2418,12 +2423,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)
@@ -2439,20 +2445,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)
@@ -2471,13 +2479,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)
@@ -2495,10 +2504,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 cce9f6f7787190da17b6719f70a317a15a92e489..402f8769de204dc2cf5d024ba66b3030064bcb4b 100644
--- a/ipaserver/dcerpc.py
+++ b/ipaserver/dcerpc.py
@@ -327,12 +327,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 c84d53b330ae2a2b484c49aaf828704f3c7d964e 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 91d66ca1b4576d55519d36dc57b175b8e7515d94..b7b8e2e42124bef10d2abde8b7980e6fcb75f836 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -860,6 +860,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 52fdfe1d47dfe8bf353291f7dcdb2d49a56d3dc0 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 | 51 ++++++++++++++++++++++++++++++++
 1 file changed, 51 insertions(+)

diff --git a/ipaserver/install/ipa_winsync_migrate.py b/ipaserver/install/ipa_winsync_migrate.py
index cb62c7e1122585ae7a73d91219086de8767aa722..bf03dce6681dd614b91ba19d44f32eb23a8d33ba 100644
--- a/ipaserver/install/ipa_winsync_migrate.py
+++ b/ipaserver/install/ipa_winsync_migrate.py
@@ -198,6 +198,56 @@ 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)
+
+        try:
+            groups, _ = self.ldap.find_entries(member_filter,
+                                               base_dn=api.env.basedn)
+        except errors.EmptyResult:
+            # If there's nothing to migrate, then let's get out of here
+            return
+
+        # 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_group', []):
+                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, ipaexternalmember=[user_identifier])
+
     @classmethod
     def main(cls, argv):
         """
@@ -234,4 +284,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

From fdb3c38b25d93dc89f81664e68567addbe02585f Mon Sep 17 00:00:00 2001
From: Tomas Babej <tba...@redhat.com>
Date: Tue, 5 May 2015 22:15:11 +0200
Subject: [PATCH] winsync_migrate: Generalize membership migration

https://fedorahosted.org/freeipa/ticket/4943
---
 ipaserver/install/ipa_winsync_migrate.py | 99 +++++++++++++++++++++++++-------
 1 file changed, 78 insertions(+), 21 deletions(-)

diff --git a/ipaserver/install/ipa_winsync_migrate.py b/ipaserver/install/ipa_winsync_migrate.py
index bf03dce6681dd614b91ba19d44f32eb23a8d33ba..cbe068458cc0b6853573c9d23d4cb6386a816bb3 100644
--- a/ipaserver/install/ipa_winsync_migrate.py
+++ b/ipaserver/install/ipa_winsync_migrate.py
@@ -198,56 +198,110 @@ class WinsyncMigrate(admintool.AdminTool):
 
         return entries
 
-    def migrate_memberships(self, entry):
+    def migrate_memberships(self, user_entry, winsync_group_prefix,
+                            object_membership_command,
+                            object_info_command,
+                            user_dn_attribute,
+                            object_group_membership_key,
+                            object_container_dn):
         """
-        Migrates user memberships to the external identity.
+        Migrates user memberships to theier external identities.
+
+        All migrated users for the given object are migrated to a common
+        external group which is then assigned to the given object as a
+        (user) member group.
         """
 
-        def winsync_group_name(group_entry):
+        def winsync_group_name(object_entry):
             """
             Returns the generated name of group containing migrated external users
             """
 
-            return u"%s_winsync_external" % group_entry['cn'][0]
+            return u"{0}_{1}_winsync_external".format(
+                winsync_group_prefix,
+                object_entry['cn'][0]
+            )
 
-        def create_winsync_group(group_entry):
+        def create_winsync_group(object_entry):
             """
             Creates the group containing migrated external users that were
             previously available via winsync.
             """
 
-            name = winsync_group_name(group_entry)
+            name = winsync_group_name(object_entry)
             api.Command['group_add'](name, external=True)
-            api.Command['group_add_member'](group_entry['cn'][0], group=[name])
+            api.Command[object_membership_command](object_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)
+        # Search for all objects containing the given user as a direct member
+        member_filter = self.ldap.make_filter_from_attr(user_dn_attribute,
+                                                        user_entry.dn)
 
         try:
-            groups, _ = self.ldap.find_entries(member_filter,
-                                               base_dn=api.env.basedn)
+            objects, _ = self.ldap.find_entries(member_filter,
+                                                base_dn=object_container_dn)
         except errors.EmptyResult:
             # If there's nothing to migrate, then let's get out of here
             return
 
-        # 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.
+        # The external user cannot be added directly as member of the IPA
+        # objects, hence we need to wrap all the external users into one
+        # new external group, which will be then added to the original IPA
+        # object as a member.
 
-        for group in groups:
+        for obj in objects:
             # Check for existence of winsync external group
-            name = winsync_group_name(group)
-            info = api.Command['group_show'](group['cn'][0])['result']
+            name = winsync_group_name(obj)
+            info = api.Command[object_info_command](obj['cn'][0])['result']
 
             # If it was not created yet, do it now
-            if name not in info.get('member_group', []):
-                create_winsync_group(group)
+            if name not in info.get(object_group_membership_key, []):
+                create_winsync_group(obj)
 
             # Add the user to the external group. Membership is migrated
             # at this point.
-            user_identifier = u"%s@%s" % (entry['uid'][0], self.options.realm)
+            user_identifier = u"%s@%s" % (user_entry['uid'][0], self.options.realm)
             api.Command['group_add_member'](name, ipaexternalmember=[user_identifier])
 
+    def migrate_group_memberships(self, user_entry):
+        return self.migrate_memberships(user_entry,
+            winsync_group_prefix="group",
+            user_dn_attribute="member",
+            object_membership_command="group_add_member",
+            object_info_command="group_show",
+            object_group_membership_key="member_group",
+            object_container_dn=DN(api.env.container_group, api.env.basedn),
+        )
+
+    def migrate_role_memberships(self, user_entry):
+        return self.migrate_memberships(user_entry,
+            winsync_group_prefix="role",
+            user_dn_attribute="member",
+            object_membership_command="role_add_member",
+            object_info_command="role_show",
+            object_group_membership_key="member_group",
+            object_container_dn=DN(api.env.container_rolegroup, api.env.basedn),
+        )
+
+    def migrate_hbac_memberships(self, user_entry):
+        return self.migrate_memberships(user_entry,
+            winsync_group_prefix="hbacrule",
+            user_dn_attribute="memberuser",
+            object_membership_command="hbacrule_add_user",
+            object_info_command="hbacrule_show",
+            object_group_membership_key="memberuser_group",
+            object_container_dn=DN(api.env.container_hbac, api.env.basedn),
+        )
+
+    def migrate_selinux_memberships(self, user_entry):
+        return self.migrate_memberships(user_entry,
+            winsync_group_prefix="selinux",
+            user_dn_attribute="memberuser",
+            object_membership_command="selinuxusermap_add_user",
+            object_info_command="selinuxusermap_show",
+            object_group_membership_key="memberuser_group",
+            object_container_dn=DN(api.env.container_selinux, api.env.basedn),
+        )
+
     @classmethod
     def main(cls, argv):
         """
@@ -284,5 +338,8 @@ class WinsyncMigrate(admintool.AdminTool):
         entries = self.find_winsync_users()
         for entry in entries:
             self.create_id_user_override(entry)
-            self.migrate_memberships(entry)
+            self.migrate_group_memberships(entry)
+            self.migrate_role_memberships(entry)
+            self.migrate_hbac_memberships(entry)
+            self.migrate_selinux_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