From 99a427833b70164e974c0d17b093dfbce6952813 Mon Sep 17 00:00:00 2001
From: Steve Martinelli <stevemar@ca.ibm.com>
Date: Fri, 11 Dec 2015 02:17:37 -0500
Subject: [PATCH] deprecate write support for identity LDAP

At the previous summit, we decided to deprecate write support for
idenity LPAP. It'll be removed in 2 releases. Several config
options were affected, and those operations should now have
deprecation warnings.

implements bp: deprecated-as-of-mitaka

Change-Id: I1e989d6c5e85ba303609c7bb36116a8bdedce9e4
---
 keystone/common/config.py                     | 30 ++++++++++++++
 keystone/identity/backends/ldap.py            | 21 ++++++++++
 keystone/tests/unit/test_backend_ldap.py      | 41 ++++++++++++++++++-
 ...recated-as-of-mitaka-8534e43fa40c1d09.yaml |  7 ++++
 4 files changed, 97 insertions(+), 2 deletions(-)

diff --git a/keystone/common/config.py b/keystone/common/config.py
index 3663d58bfb..8a6d07a869 100644
--- a/keystone/common/config.py
+++ b/keystone/common/config.py
@@ -626,10 +626,25 @@ FILE_OPTIONS = {
                    help='LDAP attribute mapped to default_project_id for '
                         'users.'),
         cfg.BoolOpt('user_allow_create', default=True,
+                    deprecated_for_removal=True,
+                    deprecated_reason="Write support for Identity LDAP "
+                                      "backends has been deprecated in the M "
+                                      "release and will be removed in the O "
+                                      "release.",
                     help='Allow user creation in LDAP backend.'),
         cfg.BoolOpt('user_allow_update', default=True,
+                    deprecated_for_removal=True,
+                    deprecated_reason="Write support for Identity LDAP "
+                                      "backends has been deprecated in the M "
+                                      "release and will be removed in the O "
+                                      "release.",
                     help='Allow user updates in LDAP backend.'),
         cfg.BoolOpt('user_allow_delete', default=True,
+                    deprecated_for_removal=True,
+                    deprecated_reason="Write support for Identity LDAP "
+                                      "backends has been deprecated in the M "
+                                      "release and will be removed in the O "
+                                      "release.",
                     help='Allow user deletion in LDAP backend.'),
         cfg.BoolOpt('user_enabled_emulation', default=False,
                     help='If true, Keystone uses an alternative method to '
@@ -669,10 +684,25 @@ FILE_OPTIONS = {
                     help='List of attributes stripped off the group on '
                          'update.'),
         cfg.BoolOpt('group_allow_create', default=True,
+                    deprecated_for_removal=True,
+                    deprecated_reason="Write support for Identity LDAP "
+                                      "backends has been deprecated in the M "
+                                      "release and will be removed in the O "
+                                      "release.",
                     help='Allow group creation in LDAP backend.'),
         cfg.BoolOpt('group_allow_update', default=True,
+                    deprecated_for_removal=True,
+                    deprecated_reason="Write support for Identity LDAP "
+                                      "backends has been deprecated in the M "
+                                      "release and will be removed in the O "
+                                      "release.",
                     help='Allow group update in LDAP backend.'),
         cfg.BoolOpt('group_allow_delete', default=True,
+                    deprecated_for_removal=True,
+                    deprecated_reason="Write support for Identity LDAP "
+                                      "backends has been deprecated in the M "
+                                      "release and will be removed in the O "
+                                      "release.",
                     help='Allow group deletion in LDAP backend.'),
         cfg.ListOpt('group_additional_attribute_mapping',
                     default=[],
diff --git a/keystone/identity/backends/ldap.py b/keystone/identity/backends/ldap.py
index ce08b7dfc1..d458b20325 100644
--- a/keystone/identity/backends/ldap.py
+++ b/keystone/identity/backends/ldap.py
@@ -17,6 +17,7 @@ import uuid
 import ldap.filter
 from oslo_config import cfg
 from oslo_log import log
+from oslo_log import versionutils
 import six
 
 from keystone.common import clean
@@ -31,6 +32,10 @@ from keystone import identity
 CONF = cfg.CONF
 LOG = log.getLogger(__name__)
 
+_DEPRECATION_MSG = _('%s for the LDAP identity backend has been deprecated in '
+                     'the Mitaka release in favor of read-only identity LDAP '
+                     'access. It will be removed in the "O" release.')
+
 
 class Identity(identity.IdentityDriverV8):
     def __init__(self, conf=None):
@@ -87,11 +92,15 @@ class Identity(identity.IdentityDriverV8):
 
     # CRUD
     def create_user(self, user_id, user):
+        msg = _DEPRECATION_MSG % "create_user"
+        versionutils.report_deprecated_feature(LOG, msg)
         self.user.check_allow_create()
         user_ref = self.user.create(user)
         return self.user.filter_attributes(user_ref)
 
     def update_user(self, user_id, user):
+        msg = _DEPRECATION_MSG % "update_user"
+        versionutils.report_deprecated_feature(LOG, msg)
         self.user.check_allow_update()
         old_obj = self.user.get(user_id)
         if 'name' in user and old_obj.get('name') != user['name']:
@@ -110,6 +119,8 @@ class Identity(identity.IdentityDriverV8):
         return self.user.get_filtered(user_id)
 
     def delete_user(self, user_id):
+        msg = _DEPRECATION_MSG % "delete_user"
+        versionutils.report_deprecated_feature(LOG, msg)
         self.user.check_allow_delete()
         user = self.user.get(user_id)
         user_dn = user['dn']
@@ -122,6 +133,8 @@ class Identity(identity.IdentityDriverV8):
         self.user.delete(user_id)
 
     def create_group(self, group_id, group):
+        msg = _DEPRECATION_MSG % "create_group"
+        versionutils.report_deprecated_feature(LOG, msg)
         self.group.check_allow_create()
         group['name'] = clean.group_name(group['name'])
         return common_ldap.filter_entity(self.group.create(group))
@@ -135,21 +148,29 @@ class Identity(identity.IdentityDriverV8):
         return self.group.get_filtered_by_name(group_name)
 
     def update_group(self, group_id, group):
+        msg = _DEPRECATION_MSG % "update_group"
+        versionutils.report_deprecated_feature(LOG, msg)
         self.group.check_allow_update()
         if 'name' in group:
             group['name'] = clean.group_name(group['name'])
         return common_ldap.filter_entity(self.group.update(group_id, group))
 
     def delete_group(self, group_id):
+        msg = _DEPRECATION_MSG % "delete_group"
+        versionutils.report_deprecated_feature(LOG, msg)
         self.group.check_allow_delete()
         return self.group.delete(group_id)
 
     def add_user_to_group(self, user_id, group_id):
+        msg = _DEPRECATION_MSG % "add_user_to_group"
+        versionutils.report_deprecated_feature(LOG, msg)
         user_ref = self._get_user(user_id)
         user_dn = user_ref['dn']
         self.group.add_user(user_dn, group_id, user_id)
 
     def remove_user_from_group(self, user_id, group_id):
+        msg = _DEPRECATION_MSG % "remove_user_from_group"
+        versionutils.report_deprecated_feature(LOG, msg)
         user_ref = self._get_user(user_id)
         user_dn = user_ref['dn']
         self.group.remove_user(user_dn, group_id, user_id)
diff --git a/keystone/tests/unit/test_backend_ldap.py b/keystone/tests/unit/test_backend_ldap.py
index 633436616a..b74ca4d439 100644
--- a/keystone/tests/unit/test_backend_ldap.py
+++ b/keystone/tests/unit/test_backend_ldap.py
@@ -20,6 +20,7 @@ import uuid
 import ldap
 import mock
 from oslo_config import cfg
+from oslo_log import versionutils
 from oslotest import mockpatch
 import pkg_resources
 from six.moves import http_client
@@ -669,21 +670,46 @@ class BaseLDAPIdentity(test_backend.IdentityTests):
     # do not yet support the update of either group or domain names with LDAP.
     # In the tests below, the update is demonstrated by updating description.
     # Refer to bug 1136403 for more detail.
-    def test_group_crud(self):
+    @mock.patch.object(versionutils, 'report_deprecated_feature')
+    def test_group_crud(self, mock_deprecator):
+        # NOTE(stevemar): As of the Mitaka release, we now check for calls that
+        # the LDAP write functionality has been deprecated.
         group = unit.new_group_ref(domain_id=CONF.identity.default_domain_id)
         group = self.identity_api.create_group(group)
+        args, _kwargs = mock_deprecator.call_args
+        self.assertIn("create_group for the LDAP identity backend", args[1])
+
         group_ref = self.identity_api.get_group(group['id'])
         self.assertDictEqual(group, group_ref)
         group['description'] = uuid.uuid4().hex
         self.identity_api.update_group(group['id'], group)
+        args, _kwargs = mock_deprecator.call_args
+        self.assertIn("update_group for the LDAP identity backend", args[1])
+
         group_ref = self.identity_api.get_group(group['id'])
         self.assertDictEqual(group, group_ref)
 
         self.identity_api.delete_group(group['id'])
+        args, _kwargs = mock_deprecator.call_args
+        self.assertIn("delete_group for the LDAP identity backend", args[1])
         self.assertRaises(exception.GroupNotFound,
                           self.identity_api.get_group,
                           group['id'])
 
+    @mock.patch.object(versionutils, 'report_deprecated_feature')
+    def test_add_remove_user_group_deprecated(self, mock_deprecator):
+        group = unit.new_group_ref(domain_id=CONF.identity.default_domain_id)
+        group = self.identity_api.create_group(group)
+        user = unit.new_user_ref(domain_id=CONF.identity.default_domain_id)
+        user = self.identity_api.create_user(user)
+        self.identity_api.add_user_to_group(user['id'], group['id'])
+        args, _kwargs = mock_deprecator.call_args
+        self.assertIn("add_user_to_group for the LDAP identity", args[1])
+
+        self.identity_api.remove_user_from_group(user['id'], group['id'])
+        args, _kwargs = mock_deprecator.call_args
+        self.assertIn("remove_user_from_group for the LDAP identity", args[1])
+
     @unit.skip_if_cache_disabled('identity')
     def test_cache_layer_group_crud(self):
         group = unit.new_group_ref(domain_id=CONF.identity.default_domain_id)
@@ -1852,10 +1878,16 @@ class LDAPIdentityEnabledEmulation(LDAPIdentity):
                           self.resource_api.get_project,
                           project['id'])
 
-    def test_user_crud(self):
+    @mock.patch.object(versionutils, 'report_deprecated_feature')
+    def test_user_crud(self, mock_deprecator):
+        # NOTE(stevemar): As of the Mitaka release, we now check for calls that
+        # the LDAP write functionality has been deprecated.
         user_dict = self.new_user_ref(
             domain_id=CONF.identity.default_domain_id)
         user = self.identity_api.create_user(user_dict)
+        args, _kwargs = mock_deprecator.call_args
+        self.assertIn("create_user for the LDAP identity backend", args[1])
+
         del user_dict['password']
         user_ref = self.identity_api.get_user(user['id'])
         user_ref_dict = {x: user_ref[x] for x in user_ref}
@@ -1863,12 +1895,17 @@ class LDAPIdentityEnabledEmulation(LDAPIdentity):
 
         user_dict['password'] = uuid.uuid4().hex
         self.identity_api.update_user(user['id'], user_dict)
+        args, _kwargs = mock_deprecator.call_args
+        self.assertIn("update_user for the LDAP identity backend", args[1])
+
         del user_dict['password']
         user_ref = self.identity_api.get_user(user['id'])
         user_ref_dict = {x: user_ref[x] for x in user_ref}
         self.assertDictContainsSubset(user_dict, user_ref_dict)
 
         self.identity_api.delete_user(user['id'])
+        args, _kwargs = mock_deprecator.call_args
+        self.assertIn("delete_user for the LDAP identity backend", args[1])
         self.assertRaises(exception.UserNotFound,
                           self.identity_api.get_user,
                           user['id'])
diff --git a/releasenotes/notes/deprecated-as-of-mitaka-8534e43fa40c1d09.yaml b/releasenotes/notes/deprecated-as-of-mitaka-8534e43fa40c1d09.yaml
index 9de697a826..11b0e21150 100644
--- a/releasenotes/notes/deprecated-as-of-mitaka-8534e43fa40c1d09.yaml
+++ b/releasenotes/notes/deprecated-as-of-mitaka-8534e43fa40c1d09.yaml
@@ -6,3 +6,10 @@ deprecations:
     deprecated. They will be removed in the 'O' release. Due to this change,
     the ``hash_algorithm`` option in the ``[token]`` section of the
     configuration file has also been deprecated.
+  - >
+    [`blueprint deprecated-as-of-mitaka <https://blueprints.launchpad.net/keystone/+spec/deprecated-as-of-mitaka>`_]
+    As of the Mitaka release, write support for the LDAP driver of the Identity
+    backend has been deprecated. This includes the following operations: create user,
+    create group, delete user, delete group, update user, update group,
+    add user to group, and remove user from group. These operations will be
+    removed in the 'O' release.