Эх сурвалжийг харах

[HUE-6233] Implement ldaptest Hue LDAP test management command.

When Hue is integrated with LDAP, users can use their existing credentials
to authenticate and inherit their existing groups transparently in Hue.
Currently Hue has more that 20 different LDAP configuration variables.
Often times it creates difficulty for Hue LDAP integration. We have
many field requests for adding a tool in CM to validate Hue's
Ldap configuration.

Hue django management command "ldaptest" uses "hue.ini". This command is
available through CM UI.

Tested on:
- Tested on CDEP cluster.

Test Cases:
- Generate hints when
  - ldap_url is not defined
  - bind_user is not defined
  - bind_password is not defined
  - error in setting ldap connection
  - test_ldap_user is not defined
  - test_ldap_group is not defined
  - when search_bind_authentication is false
    - check nt_domain and ldap_username_pattern
  - when search_bind_authentication is true
    - check user_filter, user_name_attr attribute
- Added unit test:
  - hue test specific desktop.ldaptestcmd_tests:CmdTests.checkcmd
  - hue test specific desktop.ldaptestcmd_tests:CmdTests.runcommand
  - hue test specific desktop.ldaptestcmd_tests:CmdTests.handlenoargs
Prakash Ranade 8 жил өмнө
parent
commit
9a681d1b96

+ 54 - 0
apps/useradmin/src/useradmin/ldap_access.py

@@ -72,6 +72,36 @@ def get_connection(ldap_config):
   else:
     return LdapConnection(ldap_config, ldap_url, get_ldap_username(username, ldap_config.NT_DOMAIN.get()), password, ldap_cert)
 
+def get_auth(ldap_config):
+  ldap_url = ldap_config.LDAP_URL.get()
+  if ldap_url is None:
+    raise Exception('No LDAP URL was specified')
+  username = ldap_config.BIND_DN.get()
+  password = ldap_config.BIND_PASSWORD.get()
+  if not password:
+    password = ldap_config.BIND_PASSWORD_SCRIPT.get()
+  ldap_cert = ldap_config.LDAP_CERT.get()
+  search_bind_authentication = ldap_config.SEARCH_BIND_AUTHENTICATION.get()
+
+  if search_bind_authentication:
+    ldap_conf = (ldap_url, username, password, ldap_cert)
+  else:
+    ldap_conf = (ldap_url, get_ldap_username(username, ldap_config.NT_DOMAIN.get()), password, ldap_cert)
+
+  return ldap_conf
+
+def get_connection(ldap_config):
+  global CACHED_LDAP_CONN
+  if CACHED_LDAP_CONN is not None:
+    return CACHED_LDAP_CONN
+
+  search_bind_authentication = ldap_config.SEARCH_BIND_AUTHENTICATION.get()
+  if search_bind_authentication:
+    ldap_obj = LdapConnection(ldap_config, *get_auth(ldap_config))
+  else:
+    ldap_obj = LdapConnection(ldap_config, *get_auth(ldap_config))
+  return ldap_obj
+
 def get_ldap_username(username, nt_domain):
   if nt_domain:
     return '%s@%s' % (username, nt_domain)
@@ -119,6 +149,9 @@ class LdapConnection(object):
     Constructor initializes the LDAP connection
     """
     self.ldap_config = ldap_config
+    self._ldap_url = ldap_url
+    self._username = bind_user
+    self._ldap_cert = cert_file
 
     if cert_file is not None:
       ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_ALLOW)
@@ -308,6 +341,10 @@ class LdapConnection(object):
       ldap_filter = '(&' + ldap_filter + user_name_filter + ')'
     attrlist = ['objectClass', 'isMemberOf', 'memberOf', 'givenName', 'sn', 'mail', 'dn', user_name_attr]
 
+    self._search_dn = search_dn
+    self._ldap_filter = ldap_filter
+    self._attrlist = attrlist
+
     ldap_result_id = self.ldap_handle.search(search_dn, scope, ldap_filter, attrlist)
     result_type, result_data = self.ldap_handle.result(ldap_result_id)
 
@@ -358,6 +395,10 @@ class LdapConnection(object):
     ldap_filter = '(&' + group_filter + group_name_filter + ')'
     attrlist = ['objectClass', 'dn', 'memberUid', group_member_attr, group_name_attr]
 
+    self._search_dn = search_dn
+    self._ldap_filter = ldap_filter
+    self._attrlist = attrlist
+
     ldap_result_id = self.ldap_handle.search(search_dn, scope, ldap_filter, attrlist)
     result_type, result_data = self.ldap_handle.result(ldap_result_id)
 
@@ -379,6 +420,10 @@ class LdapConnection(object):
     ldap_filter = '(&%(ldap_filter)s(|(isMemberOf=%(group_dn)s)(memberOf=%(group_dn)s)))' % {'group_dn': dn, 'ldap_filter': ldap_filter}
     attrlist = ['objectClass', 'isMemberOf', 'memberOf', 'givenName', 'sn', 'mail', 'dn', search_attr]
 
+    self._search_dn = search_dn
+    self._ldap_filter = ldap_filter
+    self._attrlist = attrlist
+
     ldap_result_id = self.ldap_handle.search(search_dn, scope, ldap_filter, attrlist)
     result_type, result_data = self.ldap_handle.result(ldap_result_id)
 
@@ -402,3 +447,12 @@ class LdapConnection(object):
 
   def _get_root_dn(self):
     return self.ldap_config.BASE_DN.get()
+
+  def ldapsearch_cmd(self):
+    ldapsearch = 'ldapsearch -x -LLL -H {ldap_url} -D "{binddn}" -w "********" -b "{base}" ' \
+                 '"{filterstring}" {attr}'.format(ldap_url=self._ldap_url,
+                                                  binddn=self._username,
+                                                  base=self._search_dn,
+                                                  filterstring=self._ldap_filter,
+                                                  attr=" ".join(self._attrlist))
+    return ldapsearch

+ 12 - 0
desktop/core/src/desktop/conf.py

@@ -1017,6 +1017,12 @@ LDAP = ConfigSection(
                                     help=_("Whether or not to follow referrals."),
                                     type=coerce_bool,
                                     default=False),
+          TEST_LDAP_USER=Config("test_ldap_user",
+                           default=None,
+                           help=_("The test user name to use for LDAP search.")),
+          TEST_LDAP_GROUP=Config("test_ldap_group",
+                            default=None,
+                            help=_("The test group name to use for LDAP search.")),
 
           DEBUG = Config("debug",
             type=coerce_bool,
@@ -1100,6 +1106,12 @@ LDAP = ConfigSection(
                    default=True,
                    type=coerce_bool,
                    help=_("Use search bind authentication.")),
+    TEST_LDAP_USER=Config("test_ldap_user",
+                   default=None,
+                   help=_("The test user name to use for LDAP search.")),
+    TEST_LDAP_GROUP=Config("test_ldap_group",
+                   default=None,
+                   help=_("The test group name to use for LDAP search.")),
 
     USERS = ConfigSection(
       key="users",

+ 53 - 0
desktop/core/src/desktop/ldaptestcmd_tests.py

@@ -0,0 +1,53 @@
+#!/usr/bin/env python
+# Licensed to Cloudera, Inc. under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  Cloudera, Inc. licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from __future__ import unicode_literals
+
+import sys
+
+from django.core import management
+from django.core.management import get_commands
+from django.test import SimpleTestCase
+from django.utils.six import StringIO
+
+class CmdTests(SimpleTestCase):
+  def checkcmd(self):
+    ldapcmd = "ldaptest"
+    try:
+      app_name = get_commands()[ldapcmd]
+    except:
+      app_name = None
+    self.assertIsNotNone(app_name)
+
+  def runcommand(self):
+    old_stdout = sys.stdout
+    sys.stdout = out = StringIO()
+    try:
+      with self.assertRaises(SystemExit):
+        management.ManagementUtility(['hue', 'ldaptest']).execute()
+    finally:
+      sys.stdout = old_stdout
+    self.assertIn("Could not find LDAP_URL server in hue.ini required for authentication", out.getvalue())
+
+  def handlenoargs(self):
+    old_stderr = sys.stderr
+    sys.stderr = err = StringIO()
+    try:
+      with self.assertRaises(SystemExit):
+        management.ManagementUtility(['hue', 'ldaptest', '-i']).execute()
+    finally:
+      sys.stderr = old_stderr
+    self.assertIn("no such option", err.getvalue())

+ 512 - 0
desktop/core/src/desktop/management/commands/ldaptest.py

@@ -0,0 +1,512 @@
+#!/usr/bin/env python
+# Licensed to Cloudera, Inc. under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  Cloudera, Inc. licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+When Hue is integrated with LDAP users can use their existing credentials
+to authenticate and inherit their existing groups transparently. This script
+is for testing LDAP setting in hue.ini. It uses [desktop] > [[ldap]] in hue.ini
+You can run this script through Hue Service in CM(Cloudera Manager) which has
+LDAP Test Command option for the test.
+
+There are two ways to authenticate in Hue:
+Search Bind: requires user_filter and user_name_attr properties.
+Direct Bind: requires nt_domain and ldap_username_pattern
+
+This script uses HUE libraries and works in HUE setup only.
+"""
+import ldap
+import ldap.filter
+import logging
+import os
+import socket
+import sys
+
+from desktop.conf import LDAP
+from django.core.management.base import NoArgsCommand
+from django.utils.translation import ugettext as _
+from useradmin import ldap_access
+
+LOG = logging.getLogger(__name__)
+LOG.setLevel(logging.DEBUG)
+ch = logging.StreamHandler(sys.stdout)
+ch.setLevel(logging.DEBUG)
+formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
+ch.setFormatter(formatter)
+LOG.addHandler(ch)
+
+ldap_url_msg = """
+This is the URL to contact LDAP or AD.
+Syntax: ldap://<server>:<port> or ldaps://<server>:<port>.
+If port is not specified, 389 will be used for LDAP and 636 for LDAPS.
+"""
+
+nt_domain_msg = """
+This is only necessary for connecting to AD without search bind authentication.
+Enter the FQDN of the domain, for example qa.test.com.
+Important:  Only one of Use search bind, NT Domain and LDAP Username Pattern
+should ever be used at one time. They are incompatible with each other.
+"""
+ldap_username_pattern_msg = """
+This is only necessary for connecting to LDAP without Search Bind Authentication.
+It is used to find the user attempting to login in LDAP based on adding
+the username to a predefined DN string. Use <username> to reference the user
+that is logging in.  An example is uid=<username>,ou=people,dc=test,dc=com.
+Important:  Only one of Use Search Bind, NT Domain and LDAP Username Pattern
+should ever be used at one time.  They are incompatible with each other.
+"""
+
+ldap_cert_msg = """
+This is the path to the certificate to use TLS with LDAP or for LDAPS.
+TLS is still over port 389 whereas LDAPS is over port 636.
+"""
+
+base_dn_msg = """
+This is the base DN to search for users.
+Cloudera recommends that you use cn=Users,dc=test,dc=com instead of dc=test,dc=com.
+"""
+
+bind_dn_msg = """
+This is only necessary if LDAP/AD does not support anonymous binds.
+Typically LDAP supports anonymous binds by default and AD does not.
+For AD, this should be a DN, cn=Administrator,cn=Users,dc=test,dc=com,
+when using Search Bind Authentication and just the username, Administrator, when using NT Domain.
+For LDAP, it needs to be a DN, cn=manager,dc=test,dc=com.
+"""
+
+bind_password_msg = """
+This is the password for the bind user.
+"""
+
+user_filter_msg = """
+This is the base filter for searching for users.
+(Optional) - Leaving this blank will use the default (objectclass=*) that will
+allow all LDAP users to log in to Hue. LDAP Sync and Search Bind Authentication
+are the only situations where this is used. NT Domain config and LDAP
+Username Pattern do not use this. Search Bind Authentication can use
+the LDAP Filter to narrow which users are permitted to login based on the filter.
+Typically this is set to objectclass=*, however, this may change based on
+the environment. For example, some LDAP environments are configured to support
+Posix objects for *nix authentication. So the user filter might need to be
+objectclass=posixAccount.
+"""
+
+user_name_attr_msg = """
+This is the attribute in LDAP that contains the username.
+(Optional) - Leaving this blank will use the default (sAMAccountName/uid) that
+will typically work with Active Directory/LDAP. Typically this is uid for
+LDAP and sAMAccountName for Active Directory. When putting attributes in
+this value for Active Directory, Cloudera recommends you maintain case sensitivity.
+"""
+
+group_filter_msg = """
+This is the base filter for searching for groups.
+(Optional) - Leaving this blank will use the default (objectclass=*) that
+will allow for syncing all groups in LDAP. This is only necessary for LDAP Sync,
+it is not used for authentication. Typically this is set to objectclass=*,
+however, this may change based on the environment.  For example, some
+LDAP environments are configured to support Posix objects
+for *nix authentication.  So the group filter might need to be
+objectclass=posixGroup.
+"""
+
+group_name_attr_msg = """
+This is the attribute in ldap that contains the groupname.
+(Optional) - Leaving this blank will use the default (Common Name) that will
+typically work with Active Directory/LDAP. Typically this is CN for LDAP and AD.
+When putting attributes in this value for AD, Cloudera recommends you
+maintain case sensitivity.
+"""
+
+group_member_attr_msg = """
+This is the attribute in the group that contains DNs of all the members.
+(Optional) - Leaving this blank will use the default (memberOf/member) that
+will typically work with Active Directory/LDAP. Typically this is member
+for Active Directory and LDAP.
+"""
+
+class Command(NoArgsCommand):
+  def print_ldap_setting(self, cfg):
+    LOG.info('[desktop]')
+    LOG.info('[[ldap]]')
+    LOG.info('create_users_on_login=%s' % cfg.CREATE_USERS_ON_LOGIN.get())
+    LOG.info('sync_groups_on_login=%s' % cfg.SYNC_GROUPS_ON_LOGIN.get())
+    LOG.info('ignore_username_case=%s' % cfg.IGNORE_USERNAME_CASE.get())
+    LOG.info('force_username_lowercase=%s' % cfg.FORCE_USERNAME_LOWERCASE.get())
+    LOG.info('force_username_uppercase=%s' % cfg.FORCE_USERNAME_UPPERCASE.get())
+    LOG.info('subgroups=%s' % cfg.SUBGROUPS.get())
+    LOG.info('nested_members_search_depth=%s' % cfg.NESTED_MEMBERS_SEARCH_DEPTH.get())
+    LOG.info('follow_referrals=%s' % cfg.FOLLOW_REFERRALS.get())
+    LOG.info('debug=%s' % cfg.DEBUG.get())
+    LOG.info('debug_level=%s' % cfg.DEBUG_LEVEL.get())
+    LOG.info('trace_level=%s' % cfg.TRACE_LEVEL.get())
+    LOG.info('base_dn="%s"' % cfg.BASE_DN.get())
+    LOG.info('nt_domain="%s"' % cfg.NT_DOMAIN.get())
+    LOG.info('ldap_url="%s"' % cfg.LDAP_URL.get())
+    LOG.info('use_start_tls=%s' % cfg.USE_START_TLS.get())
+    LOG.info('ldap_cert="%s"' % cfg.LDAP_CERT.get())
+    LOG.info('ldap_username_pattern="%s"' % cfg.LDAP_USERNAME_PATTERN.get())
+    LOG.info('bind_dn="%s"' % cfg.BIND_DN.get())
+    LOG.info('bind_password=*******')
+    LOG.info('search_bind_authentication=%s' % cfg.SEARCH_BIND_AUTHENTICATION.get())
+    LOG.info('test_ldap_user="%s"' % cfg.TEST_LDAP_USER.get())
+    LOG.info('test_ldap_group="%s"' % cfg.TEST_LDAP_GROUP.get())
+    LOG.info('[[[users]]]')
+    LOG.info('user_filter="%s"' % cfg.USERS.USER_FILTER.get())
+    LOG.info('user_name_attr="%s"' % cfg.USERS.USER_NAME_ATTR.get())
+    LOG.info('[[[groups]]]')
+    LOG.info('group_filter="%s"' % cfg.GROUPS.GROUP_FILTER.get())
+    LOG.info('group_name_attr="%s"' % cfg.GROUPS.GROUP_NAME_ATTR.get())
+    LOG.info('group_member_attr="%s"' % cfg.GROUPS.GROUP_MEMBER_ATTR.get())
+    LOG.info('-----------------------')
+
+  def check_ldap_params(self, ldap_config):
+    err_code = 1
+    ldap_url = ldap_config.LDAP_URL.get()
+    if ldap_url is None:
+      LOG.info(_(ldap_url_msg))
+      LOG.warn('Could not find LDAP_URL server in hue.ini required for authentication')
+      return err_code
+
+    if not ((ldap_url.startswith("ldap") and
+          "://" in ldap_url)):
+      LOG.info(_(ldap_url_msg))
+      LOG.warn("Check your ldap_url=%s" % ldap_url)
+      return err_code
+
+    ldap_cert = ldap_config.LDAP_CERT.get()
+    if ldap_cert is not None and (not os.path.isfile(ldap_cert)):
+      LOG.info(_(ldap_cert_msg))
+      LOG.warn("Could not find certificate %s on %s" % (ldap_cert, socket.gethostname()))
+      return err_code
+
+    if ldap_cert is not None:
+      LOG.info("Setting LDAP TLS option ldap.OPT_X_TLS_CACERTFILE=%s" % ldap_cert)
+      LOG.info("Setting LDAP TLS option ldap.OPT_X_TLS_REQUIRE_CERT=ldap.OPT_X_TLS_ALLOW")
+
+    bind_dn = ldap_config.BIND_DN.get()
+    if bind_dn is None:
+      LOG.info(_(bind_dn_msg))
+      LOG.warn("Could not find bind_dn in hue.ini required for authentication")
+      return err_code
+
+    if ldap_config.SEARCH_BIND_AUTHENTICATION.get():
+      # Search Bind Auth
+      user_name_attr = ldap_config.USERS.USER_NAME_ATTR.get()
+      user_filter = ldap_config.USERS.USER_FILTER.get()
+      bind_password = ldap_config.BIND_PASSWORD.get()
+      if user_name_attr=='' or ' ' in user_name_attr:
+        LOG.info(_(user_name_attr_msg))
+        LOG.warn("Could not find user_name_attr in hue.ini")
+        return err_code
+
+      if user_filter=='' or ' ' in user_filter:
+        LOG.info(_(user_filter_msg))
+        LOG.warn("Could not find user_filter in hue.ini required for authentication")
+        return err_code
+
+      if (not bind_password and not ldap_config.BIND_PASSWORD_SCRIPT.get()):
+        LOG.info(_(bind_password_msg))
+        LOG.warn("Could not find bind_password in hue.ini, required for authentication")
+        return err_code
+    else:
+      # Direct Bind Auth
+      nt_domain = ldap_config.NT_DOMAIN.get()
+      if nt_domain is None:
+        pattern = ldap_config.LDAP_USERNAME_PATTERN.get()
+        if pattern is None:
+          LOG.info(_(nt_domain_msg))
+          LOG.info(_(ldap_username_pattern_msg))
+          LOG.warn('Could not find nt_domain in hue.ini')
+          LOG.warn('Could not find ldap_username_pattern in hue.ini, required for authentication')
+          return err_code
+        else:
+          pattern = pattern.replace('<username>', bind_dn)
+          LOG.info('nt_domain is none, setting USER_DN_TEMPLATE with %s' % pattern)
+      else:
+        if ((',' in bind_dn) or ('@' in bind_dn) or ('=' in bind_dn) or (' ' in bind_dn)):
+          LOG.info(_(nt_domain_msg))
+          LOG.info(_(ldap_username_pattern_msg))
+          LOG.warn('bind_dn value contains , or @ or = or " " character which is not allowed')
+          return err_code
+        # %(user)s is a special string that will get replaced during the authentication process
+        LOG.info('Setting USER_DN_TEMPLATE as %s@%s' % (bind_dn, nt_domain))
+
+    return 0
+
+  def find_ldapusers(self, ldap_config, ldap_obj):
+    err_code = 0
+    test_ldap_user = ldap_config.TEST_LDAP_USER.get()
+    if '*' in test_ldap_user:
+      LOG.warn('Setting test_ldap_user as %s' % test_ldap_user)
+      LOG.warn('This operation can overwhelm the server')
+      LOG.warn('Chances are server may or may not respond')
+      LOG.warn('If you want to test your LDAP Settings please use specific username')
+
+    try:
+      users = ldap_obj.find_users(test_ldap_user)
+    except ldap.NO_SUCH_OBJECT as err:
+      LOG.warn(str(err))
+      LOG.info(_(base_dn_msg))
+      LOG.warn('hints: check base_dn')
+      err_code = 1
+    except:
+      typ, value, traceback = sys.exc_info()
+      LOG.warn("%s %s" % (typ, value))
+      LOG.info(_(base_dn_msg))
+      LOG.warn('hints: check base_dn')
+      err_code = 1
+
+    # print ldapsearch command for debugging purpose
+    if err_code:
+      LOG.warn(ldap_obj.ldapsearch_cmd())
+      return err_code
+    else:
+      LOG.info(ldap_obj.ldapsearch_cmd())
+
+    if users:
+      for user in users:
+        LOG.info('%s' % user)
+        if user.get('username', '')=='':
+          LOG.info(_(user_name_attr_msg))
+          LOG.warn('hints: check user_name_attr="%s"' % ldap_config.USERS.USER_NAME_ATTR.get())
+          err_code = 1
+    else:
+      LOG.warn('test_ldap_user %s may not exist' % test_ldap_user)
+      LOG.info(_(user_filter_msg))
+      LOG.info(_(user_name_attr_msg))
+      LOG.warn('hints: check user_filter="%s"' % ldap_config.USERS.USER_FILTER.get())
+      LOG.warn('hints: check user_name_attr="%s"' % ldap_config.USERS.USER_NAME_ATTR.get())
+      err_code = 1
+
+    return err_code
+
+  def find_ldapgroups(self, ldap_config, ldap_obj):
+    err_code = 0
+    test_ldap_group = ldap_config.TEST_LDAP_GROUP.get()
+    if '*' in test_ldap_group:
+      LOG.warn("Setting test_ldap_group as %s" % test_ldap_group)
+      LOG.warn("This operation can overwhelm the server")
+      LOG.warn("Chances are server may or may not respond")
+      LOG.warn("If you want to test your LDAP Settings please use specific groupname")
+
+    try:
+      groups = ldap_obj.find_groups(test_ldap_group)
+    except ldap.NO_SUCH_OBJECT as err:
+      LOG.warn(str(err))
+      LOG.info(_(base_dn_msg))
+      LOG.warn("hints: check base_dn")
+      err_code = 1
+    except:
+      typ, value, traceback = sys.exc_info()
+      LOG.warn("%s %s" % (typ, value))
+      LOG.info(_(base_dn_msg))
+      LOG.warn("hints: check base_dn")
+      err_code = 1
+
+    if err_code:
+      LOG.warn(ldap_obj.ldapsearch_cmd())
+      return err_code
+    else:
+     LOG.info(ldap_obj.ldapsearch_cmd())
+
+    if groups:
+      for grp in groups:
+        LOG.info("%s" % grp)
+    else:
+      LOG.warn("test_ldap_group %s may not exist" % test_ldap_group)
+      LOG.info(_(group_filter_msg))
+      LOG.info(_(group_name_attr_msg))
+      LOG.warn("hints: check group_filter=\"%s\"" % ldap_config.GROUPS.GROUP_FILTER.get())
+      LOG.warn("hints: check group_name_attr=\"%s\"" % ldap_config.GROUPS.GROUP_NAME_ATTR.get())
+      err_code = 1
+
+    return err_code
+
+  def find_users_of_group(self, ldap_config, ldap_obj):
+    err_code = 0
+    test_ldap_group = ldap_config.TEST_LDAP_GROUP.get()
+
+    try:
+      groups = ldap_obj.find_users_of_group(test_ldap_group)
+    except ldap.NO_SUCH_OBJECT as err:
+      LOG.warn(str(err))
+      LOG.info(_(base_dn_msg))
+      LOG.warn('hints: check base_dn')
+      err_code = 1
+    except:
+      typ, value, traceback = sys.exc_info()
+      LOG.warn("%s %s" % (typ, value))
+      LOG.info(_(base_dn_msg))
+      LOG.warn('hints: check base_dn')
+      err_code = 1
+
+    # print ldapsearch command for debugging purpose
+    if err_code:
+      LOG.warn(ldap_obj.ldapsearch_cmd())
+      return err_code
+    else:
+      LOG.info(ldap_obj.ldapsearch_cmd())
+
+    if groups:
+      for grp in groups:
+        LOG.info('%s' % grp)
+        if grp.get('members', [])==[]:
+          LOG.info(_(group_member_attr_msg))
+          LOG.warn('hints: check group_member_attr="%s"' % ldap_config.GROUPS.GROUP_MEMBER_ATTR.get())
+          err_code = 1
+    else:
+      LOG.warn('find_users_of_group %s may not exist' % test_ldap_group)
+      LOG.info(_(group_filter_msg))
+      LOG.info(_(group_name_attr_msg))
+      LOG.warn('hints: check group_filter="%s"' % ldap_config.GROUPS.GROUP_FILTER.get())
+      LOG.warn('hints: check group_name_attr="%s"' % ldap_config.GROUPS.GROUP_NAME_ATTR.get())
+      err_code = 1
+
+    return err_code
+
+  def find_groups_of_group(self, ldap_config, ldap_obj):
+    err_code = 0
+    test_ldap_group = ldap_config.TEST_LDAP_GROUP.get()
+
+    try:
+      groups = ldap_obj.find_groups_of_group(test_ldap_group)
+    except ldap.NO_SUCH_OBJECT as err:
+      LOG.warn(err.args)
+      LOG.info(_(base_dn_msg))
+      LOG.warn('hints: check base_dn')
+      err_code = 1
+    except:
+      typ, value, traceback = sys.exc_info()
+      LOG.warn("%s %s" % (typ, value))
+      LOG.info(_(base_dn_msg))
+      LOG.warn('hints: check base_dn')
+      err_code = 1
+
+    # print ldapsearch command for debugging purpose
+    if err_code:
+      LOG.warn(ldap_obj.ldapsearch_cmd())
+      return err_code
+    else:
+      LOG.info(ldap_obj.ldapsearch_cmd())
+
+    if groups:
+      for grp in groups:
+        LOG.info('%s' % grp)
+        if grp.get('members',[])==[]:
+          LOG.info(_(group_member_attr_msg))
+          LOG.warn('hints: check group_member_attr="%s"' % ldap_config.GROUPS.GROUP_MEMBER_ATTR.get())
+          err_code = 1
+    else:
+      LOG.info('find_groups_of_group %s may not exist' % test_ldap_group)
+
+    return err_code
+
+  def sys_exit(self, exit_code):
+    if exit_code!=0:
+      LOG.warn('LDAP Test Command failed')
+    sys.exit(exit_code)
+
+  def handle_noargs(self, **options):
+    """
+      ldap_test management command enters here. Main logic as follows:
+      * check ldap parameters from hue.ini file
+      * check ldap connection if connection is not successful then provide hints and equivalent
+      ldapsearch command for more hints.
+      * using successful ldap connection check for the test_ldap_user. If test_ldap_user is not
+      specified then assume filter string for all users
+      * if test_ldap_group is presented in DN(distinguished name format) then execute
+      find users of test_ldap_group and find groups of test_ldap_group
+      * if test_ldap_group is not presented in DN format then execute ldap search function
+      based on group_filter from hue.ini
+    """
+    err_code = 0
+    connection = None
+    ldap_config = None
+
+    if LDAP.LDAP_SERVERS.get():
+      ldap_config = next(LDAP.LDAP_SERVERS.__iter__())
+    else:
+      ldap_config = LDAP
+
+    self.print_ldap_setting(ldap_config)
+    # Basic validation check for hue.ini's ldap parameters [desktop] > [[ldap]]
+    err_code = self.check_ldap_params(ldap_config)
+
+    if not err_code:
+      # Connect to only one LDAP server given in the hue.ini config
+      # @TODO@ support for multiple LDAP servers
+      try:
+        connection = ldap_access.get_connection(ldap_config)
+      except ldap_access.LdapBindException as err:
+        LOG.warn(str(err))
+        LOG.info(_(ldap_url_msg))
+        LOG.info(_(bind_dn_msg))
+        LOG.warn('hints: check bind_dn, bind_password and ldap_url')
+        LOG.warn('ldap_url="%s"' % ldap_config.LDAP_URL.get())
+        LOG.warn('bind_dn="%s"' % ldap_config.BIND_DN.get())
+        err_code = 1
+      except:
+        typ, value, traceback = sys.exc_info()
+        LOG.warn("%s %s" % (typ, value))
+        LOG.info(_(ldap_url_msg))
+        LOG.info(_(bind_dn_msg))
+        LOG.warn('hints: check bind_dn, bind_password and ldap_url')
+        LOG.warn('ldap_url="%s"' % ldap_config.LDAP_URL.get())
+        LOG.warn('bind_dn="%s"' % ldap_config.BIND_DN.get())
+        err_code = 1
+
+      if err_code:
+        cfg = ldap_access.get_auth(ldap_config)
+        ldapsearch = 'ldapsearch -x -LLL -H {ldap_url} -D "{binddn}" -w "********" -b "" ' \
+                     ' -s base'.format(ldap_url=cfg[0], binddn=cfg[1])
+        LOG.warn(ldapsearch)
+        self.sys_exit(err_code)
+
+      LOG.info('LDAP whoami_s() %s' % (connection.ldap_handle.whoami_s()))
+      if ldap_config.TEST_LDAP_USER.get() is not None:
+        err_code = self.find_ldapusers(ldap_config, connection)
+        if err_code:
+          self.sys_exit(err_code)
+
+        if ldap_config.TEST_LDAP_GROUP.get() is not None:
+          group_dn = None
+          try:
+            group_dn = ldap.explode_dn(ldap_config.TEST_LDAP_GROUP.get())
+          except:
+            group_dn = None
+
+          if group_dn is not None:
+            # group DN
+            err_code = self.find_users_of_group(ldap_config, connection)
+            if err_code:
+              self.sys_exit(err_code)
+            err_code = self.find_groups_of_group(ldap_config, connection)
+            if err_code:
+              self.sys_exit(err_code)
+          else:
+            # group name pattern goes as search attribute
+            err_code = self.find_ldapgroups(ldap_config, connection)
+            if err_code:
+              self.sys_exit(err_code)
+        else:
+          LOG.info('Now test further by providing test ldap group in CM')
+          LOG.info('test_ldap_group=somegroupname')
+          LOG.info('test_ldap_group=cn=Administrators,dc=test,dc=com')
+      else:
+        LOG.info('Now test further by providing test ldap user in CM')
+        LOG.info('test_ldap_user=someusername')
+
+    self.sys_exit(0)