#!/usr/bin/python
# @package      hubzero-openldap
# @file         hzldap
# @author       David R. Benham <dbenham@purdue.edu>
# @copyright    Copyright (c) 2012-2014 HUBzero Foundation, LLC.
# @license      http://www.gnu.org/licenses/lgpl-3.0.html LGPLv3
#
# Copyright (c) 2012-2014 HUBzero Foundation, LLC.
#
# This file is part of: The HUBzero(R) Platform for Scientific Collaboration
#
# The HUBzero(R) Platform for Scientific Collaboration (HUBzero) is free
# software: you can redistribute it and/or modify it under the terms of
# the GNU Lesser General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any
# later version.
#
# HUBzero 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# HUBzero is a registered trademark of HUBzero Foundation, LLC.
#
import argparse
import base64
import ConfigParser
import hashlib
import hubzero.data.db
import hubzero.utilities.misc
import hubzero.utilities.user
import hubzero.config.passwords
import hubzero.config.webconfig
import ldap
import ldap.modlist
import os
import re
import subprocess
import sys
import traceback

hubname = hubzero.config.webconfig.getDefaultSite()
docroot = hubzero.config.hubzerositeconfig.getHubzeroConfigOption(hubname, "DocumentRoot")


if os.path.exists(docroot + "/libraries/cms/index.html"):
        hubzero.utilities.misc.JOOMLA_25 = True

hubzero.utilities.misc.JOOMLA_25 = False

if os.path.exists("/usr/share/hubzero-cms-1.2/cms/index.php"):
	hubzero.utilities.misc.JOOMLA_25 = True


def _write_sssd_conf(adminDN, adminPW, groupSearchBase, userSearchBase):

	fileText = """
[nss]
filter_groups = root
filter_users = root

[sssd]
config_file_version = 2
domains = default, ldap
services = pam, nss

[domain/LDAP]
enumerate = true
tls_reqcert = never
access_provider = ldap
ldap_user_search_base = %userSearchBase%
ldap_group_search_base = %groupSearchBase%
ldap_default_bind_dn = %adminDN%
ldap_default_authtok_type = password
ldap_default_authtok = %adminPW%
ldap_id_use_start_tls = False
ldap_auth_disable_tls_never_use_in_production = true
id_provider = ldap
ldap_uri = ldap://localhost
ldap_access_filter = host=web
"""
        filename = "/etc/sssd/sssd.conf"

        fileText = fileText.replace("%userSearchBase%", userSearchBase)
        fileText = fileText.replace("%groupSearchBase%", groupSearchBase)
        fileText = fileText.replace("%adminDN%", adminDN)
        fileText = fileText.replace("%adminPW%", adminPW)

        f1 = open(filename, 'w')
        f1.write(fileText)
        f1.close();

	hubzero.utilities.misc.exShellCommand(['chmod', '0600', filename])


def _write_nslcd_conf(suffix, searchAcctDN, searchAcctPW, adminAcctDN):
	
	fileText = """

# /etc/nslcd.conf
# nslcd configuration file. See nslcd.conf(5)
# for details.

# The user and group nslcd should run as.
uid nslcd
gid nslcd

# The location at which the LDAP server(s) should be reachable.
uri ldap://localhost/

# The search base that will be used for all queries.
base %suffix%

# The LDAP protocol version to use.
ldap_version 3

# The DN to bind with for normal lookups.
binddn %searchAcctDN%
bindpw %searchAcctPW%

# The DN used for password modifications by root.
#rootpwmoddn %adminAcctDN% 

# SSL options
#ssl off
#tls_reqcert demand
#tls_cacertfile /etc/ssl/certs/ca-bundle.crt

# The search scope.
#scope sub

# initial server connection time limit (default 30 s is too long)
bind_timelimit 1

# Search timelimit.
timelimit 5

# Reconnect policy.
reconnect_sleeptime 0

# The search scope.
#scope sub

base passwd ou=users,%suffix%
base shadow ou=users,%suffix%
base group ou=groups,%suffix%
scope passwd one
scope group one
scope shadow one
filter shadow (&(objectClass=posixAccount)(host=web))
pam_authz_search (&(objectClass=posixAccount)(uid=$username)(host=web))

"""

	filename = "/etc/nslcd.conf"

	fileText = fileText.replace("%suffix%", suffix)
	fileText = fileText.replace("%searchAcctDN%", searchAcctDN)
	fileText = fileText.replace("%searchAcctPW%", searchAcctPW)
	fileText = fileText.replace("%adminAcctDN%", adminAcctDN)
	
	f1 = open(filename, 'w')
	f1.write(fileText)
	f1.close();
	
	hubzero.utilities.misc.exShellCommand(['chmod', '0640', filename])
	hubzero.utilities.misc.exShellCommand(['chown', 'root.nslcd', filename])



def _write_nsswitch_conf():
	
	fileText = """# /etc/nsswitch.conf
#
# Example configuration of GNU Name Service Switch functionality.
# If you have the `glibc-doc-reference' and `info' packages installed, try:
# `info libc "Name Service Switch"' for information about this file.

passwd:         %r1%
group:          %r1%
shadow:         %r1%

hosts:          files dns
networks:       files

protocols:      db files
services:       db files
ethers:         db files
rpc:            db files

netgroup:       nis	
"""

	if hubzero.utilities.misc.isRHEL():
		fileText = fileText.replace("%r1%", 'files sss')

	if hubzero.utilities.misc.isDebian():
		fileText = fileText.replace("%r1%", 'compat ldap')

	filename = "/etc/nsswitch.conf"

	f1 = open(filename, 'w')
	f1.write(fileText)
	f1.close();
	
	hubzero.utilities.misc.exShellCommand(['chmod', '0644', filename])
	hubzero.utilities.misc.exShellCommand(['chown', 'root.root', filename])



def getAlphaNumericPW():
	validPW = False
	while not validPW:
		print "Enter user password and press return"
		pw = raw_input("Enter an password")
	
		p = re.compile("[a-zA-Z0-9]*")
		m = p.match(pw)
	
		if not m:
			print "Password invalid - must be alphanumeric"
		else:
			validPW = True

	return pw


 
def removeOlcAccessEntry(dbnum, dbtype):
	"""Remove any previous olcAccess entires for specified database"""
	
	ldifData = ("dn: olcDatabase={%s}" + dbtype + ",cn=config\n"
		"changetype: modify\n"
		"delete: olcAccess\n"
		"-\n"
		"delete: olcTLSCertificateFile\n"
		"-\n"
		"delete: olcTLSCertificateKeyFile")

	ldifData = ldifData % dbnum

	rc, procOutput, procError = hubzero.utilities.misc.exShellCommand(['ldapmodify', '-Y', 'EXTERNAL', '-H', 'ldapi:///'], ldifData)

	if rc: 
		print procOutput.rstrip() 
		print procError.rstrip()



def change_olcSuffix(dbnum, dbtype, newSuffix):
	"""Modify the olcSuffix for the specified DB number"""
	
	ldifData = ("dn: olcDatabase={%s}" + dbtype + ",cn=config\n"
	            "changetype: modify\n"
	            "replace: olcSuffix\n"
	            "olcSuffix: %s\n"
	            "-\n"
	            "replace: olcRootDN\n"
	            "olcRootDN: cn=Manager,%s")

	ldifData = ldifData % (dbnum, newSuffix, newSuffix)

	rc, procOutput, procError = hubzero.utilities.misc.exShellCommand(['ldapmodify', '-Y', 'EXTERNAL', '-H', 'ldapi:///'], ldifData)
	if rc: 
		print procOutput 
		print procError
	else: 
		print procOutput



def remove_olcRootPW(dbnum, dbtype):
	"""Remove the olcRootPW entry for the specified DB number"""
	
	ldifData = ("dn: olcDatabase={%s}" + dbtype + ",cn=config\n"
	            "changetype: modify\n"
	            "delete: olcRootPW")

	ldifData = ldifData % (dbnum)
	
	rc, procOutput, procError = hubzero.utilities.misc.exShellCommand(['ldapmodify', '-Y', 'EXTERNAL', '-H', 'ldapi:///'], ldifData)
	if rc: 
		print procOutput 
		print procError
	else: 
		print procOutput



def getDBNumber():
	
	rc, procOutput, procErr = hubzero.utilities.misc.exShellCommand(['slapcat', '-b', 'cn=config', '-a', '(objectClass=olcDatabaseConfig)'])
		
	# find the number of the min olcDatabase entries that has the format bdb 
	minbdbDBNumber = 999
	for m in re.finditer("olcDatabase=\{(\d+)\}bdb", procOutput):
		if int(m.group(1)) < minbdbDBNumber:
			minbdbDBNumber = int(m.group(1))

	# find the number of the min olcDatabase entries that has the format hdb 
	minhdbDBNumber = 999
	for m in re.finditer("olcDatabase=\{(-?\d+)\}hdb", procOutput):
		if int(m.group(1)) < minhdbDBNumber:
			minhdbDBNumber = int(m.group(1))

	# This way, if all databses use hdb or all use bdb, or even if there is a mix
	# we take the lowest (first) one
	if minbdbDBNumber <	minhdbDBNumber:
		return minbdbDBNumber, 'bdb'
	else:
		return minhdbDBNumber, 'hdb'



def addOlcAccessEntries(dbnum, dbtype, suffix):

	# our access rules	
	ldifData = ('dn: olcDatabase={%1}' + dbtype + ',cn=config\n'
		            'changetype: modify\n'
		            'delete: olcAccess\n'
		            '-\n'
		            'add: olcAccess\n'
		            'olcAccess: {0}to dn.one="%2"\n'
		            '  filter=(objectClass=simpleSecurityObject) attrs=userPassword\n'
		            '  by dn="cn=admin,%2" write\n'
		            '  by dn="cn=syncuser,%2" read\n'
		            '  by anonymous auth\n'
		            '  by * none\n'
		            'olcAccess: {1}to dn.one="ou=users,%2"\n'
		            '  filter=(objectClass=posixAccount) attrs=userPassword\n'
		            '  by dn="cn=admin,%2" write\n'
		            '  by dn="cn=syncuser,%2" read\n'
		            '  by anonymous auth\n'
		            '  by * none\n'
		            'olcAccess: {2}to dn.one="ou=users,%2"\n'
		            '  filter=(objectClass=posixAccount)\n'
		            '  attrs=entry,objectClass,cn,gecos,gidNumber,homeDirectory,loginShell,uid,uidNumber,host\n'
		            '  by dn="cn=admin,%2" write\n'
		            '  by dn="cn=syncuser,%2" read\n'
		            '  by dn="cn=search,%2" read\n'
		            '  by self read\n'
		            '  by * none\n'
		            'olcAccess: {3}to dn.one="ou=users,%2"\n'
		            '  filter=(&(objectClass=posixAccount)(objectClass=shadowAccount))\n'
		            '  attrs=shadowExpire,shadowFlag,shadowInactive,shadowLastChange,shadowMax,shadowMin,shadowWarning\n'
		            '  by dn="cn=admin,%2" write\n'
		            '  by dn="cn=syncuser,%2" read\n'
		            '  by dn="cn=search,%2" read\n'
		            '  by * none\n'
		            'olcAccess: {4}to dn.one="ou=users,%2"\n'
		            '  filter=(objectClass=posixAccount)\n'
		            '  by dn="cn=admin,%2" write\n'
		            '  by dn="cn=update,%2" read\n'
		            '  by * none\n'
		            'olcAccess: {5}to dn.one="ou=groups,%2"\n'
		            '  filter=(objectClass=posixGroup)\n'
		            '  attrs=entry,objectClass,cn,gidNumber,memberUid,userPassword\n'
		            '  by dn="cn=admin,%2" write\n'
		            '  by dn="cn=syncuser,%2" read\n'
		            '  by dn="cn=search,%2" read\n'
		            '  by * none\n'
		            'olcAccess: {6}to dn.one="ou=groups,%2"\n'
		            '  filter=(objectClass=posixGroup)\n'
		            '  by dn="cn=admin,%2" write\n'
		            '  by dn="cn=syncuser,%2" read\n'
		            '  by * none\n'
		            'olcAccess: {7}to dn.subtree="%2"\n'
		            '  by dn="cn=admin,%2" write\n'
		            '  by dn="cn=syncuser,%2" read\n'
		            '  by dn="cn=search,%2" search\n'
		            '  by * none')
	
	ldifData =  ldifData.replace('%1', str(dbnum))
	ldifData =  ldifData.replace('%2', suffix)
	
	#print "olcAccess" + ldifData
	
	rc, procOutput, procError = hubzero.utilities.misc.exShellCommand(['ldapmodify', '-Y', 'EXTERNAL', '-H', 'ldapi:///'], ldifData)
	if rc: 
		print procOutput.rstrip()
		print procError.rstrip()




def _init_ldap(args):

	# See if account info is already stored, i.e. ldap has already been setup for us
	adminUserDN = hubzero.config.hubzerositeconfig.getHubzeroConfigOption('ldap','adminuserDN')
	searchUserDN = hubzero.config.hubzerositeconfig.getHubzeroConfigOption('ldap','adminuserDN')
	syncUserDN = hubzero.config.hubzerositeconfig.getHubzeroConfigOption('ldap','adminuserDN')

	adminUserPW = hubzero.config.hubzerositeconfig.getHubzeroConfigOption('ldap','adminuserPW')
	searchUserPW = hubzero.config.hubzerositeconfig.getHubzeroConfigOption('ldap','adminuserPW')
	syncUserPW = hubzero.config.hubzerositeconfig.getHubzeroConfigOption('ldap','adminuserDPW')

	# if ldap has not been previously setup
	if not adminUserDN:	

		# prompt for passwords or create from scratch
		if not args.promptforadminpw:
			adminUserPW = hubzero.config.passwords.generateAlphaNumPassword(10)
		else:
			adminUserPW = getAlphaNumericPW()

		if not args.promptforsearchpw:
			searchUserPW = hubzero.config.passwords.generateAlphaNumPassword(10)
		else:
			searchUserPW = getAlphaNumericPW()
	
		if not args.promptforsyncuserpw:
			syncUserPW = hubzero.config.passwords.generateAlphaNumPassword(10)
		else:
			syncUserPW = getAlphaNumericPW()
	

		print "Getting current info via slapcat"
		rc, procOutput, procErr = hubzero.utilities.misc.exShellCommand(['slapcat', '-b', 'cn=config', '-a', '(objectClass=olcDatabaseConfig)'])
	
		# We'll use this later
		fullSlapcatOutput = procOutput
	
		# find the number of the max olcDatabase entries
		maxDBNumber, dbtype = getDBNumber()
	
		# find the oldDbDirectory for the max db 
		rc, procOutput, procErr = hubzero.utilities.misc.exShellCommand(['slapcat', '-b', 'cn=config', '-a', '(&(objectClass=olcDatabaseConfig)(olcDatabase={' + str(maxDBNumber) + '}' + dbtype + '))'])
	
		matchObj = re.search(r'olcDbDirectory: (.*)$' , procOutput, re.M)
		if matchObj:
			print 'Found olcDbDirectory for for olcDatabase={' + str(maxDBNumber) + '}' + dbtype + ': ' + matchObj.group(1)
			configFileDir = matchObj.group(1)
		else:
			print 'Error, cannot find an entry for olcDbDirectory for (olcDatabase={' + str(maxDBNumber) + '}' + dbtype + ')'
			return(1)
	
		# See if this file exists for this database
		configFileName = configFileDir + '/DB_CONFIG'
		print "Checking for " + configFileName

		if not os.path.exists(configFileName):
			print configFileDir + "/DB_CONFIG file not present"
	
			# add the data to ldap
			ldifData = ("dn: olcDatabase={"+ str(maxDBNumber) +"}" + dbtype + ",cn=config\n"
			"changetype: modify\n"
			"add: olcDbConfig\n"
			"olcDbConfig: set_cachesize 0 268435456 1\n"
			"olcDbConfig: set_lg_regionmax 262144\n"
			"olcDbConfig: set_lg_bsize 2097152")
	
			rc, procOutput, procError = hubzero.utilities.misc.exShellCommand(['ldapmodify', '-Y', 'EXTERNAL', '-H', 'ldapi:///'], ldifData)
			if rc: 
				print procOutput 
				print procError
			else: 
				print procOutput

			# restarting the server should generate the file we were looking for from the attribute mods we did above
			rc, procOutput, procError = hubzero.utilities.misc.exShellCommand(['service', 'slapd', 'restart'])
		else:
			print configFileDir + '/DB_CONFIG' " exists, no need to create"
	
	
		# See if our ldap db is empty
		rc, procOutput, procErr = hubzero.utilities.misc.exShellCommand(['slapcat', '-n', str(maxDBNumber)])
		
		#if procOutput:
		#	print "Current DB does not appear to be empty"
		#	print procOutput
		#	exit(1)

		# get the olcSuffix
		matchObj = re.search(r'olcSuffix: (.*)$' , fullSlapcatOutput, re.M)
		if matchObj:
			print "found olcSuffix: " + matchObj.group(1)
			suffix = matchObj.group(1)
		else:
			print "Error, cannot find olcSuffix"
			exit(1)	

		ldapManagerUserDN = "cn=Manager," + suffix

		# Since the database was empty, we're assuming it's ok to clear out any existing rules
		print "Removing olcAccess entires"
		removeOlcAccessEntry(maxDBNumber, dbtype)
		print "Adding olcAccess entires"
		addOlcAccessEntries(maxDBNumber, dbtype, suffix)

		# if olcRootPW doesn't exist, make it
		if fullSlapcatOutput.find('olcRootPW:') == -1:

			print "olcRootPW already exists, adding it"
			
			# create a root user for the new database to allow remote connections
			ldifData = ("dn: olcDatabase={%s}" + dbtype + ",cn=config\n"
			            "changetype: modify\n"
			            "replace: olcRootDN\n"
			            "olcRootDN: %s\n"
			            "-\n"
			            "add: olcRootPW\n"
			            "olcRootPW: %s")

		else:

			print "olcRootPW already exists, changing it"

			# create a root user for the new database to allow remote connections
			ldifData = ("dn: olcDatabase={%s}" + dbtype + ",cn=config\n"
		                "changetype: modify\n"
		                "replace: olcRootDN\n"
	        	        "olcRootDN: %s\n"
		                "-\n"
		                "replace: olcRootPW\n"
		                "olcRootPW: %s")
		
		ldapPWHash = "{MD5}" + base64.encodestring(hashlib.md5(str(adminUserPW)).digest())
		ldifData = ldifData % (maxDBNumber, ldapManagerUserDN, ldapPWHash)

		rc, procOutput, procError = hubzero.utilities.misc.exShellCommand(['ldapmodify', '-Y', 'EXTERNAL', '-H', 'ldapi:///'], ldifData)
		if rc: 
			print procError
			print procOutput 


		# Switch over to native ldap calls for the remainder of the setup using the above user and pw we just setup
		print "Attempting ldap bind to localhost"

		l = ldap.initialize('ldap://localhost')
		print "ldapManagerUserDN=" + ldapManagerUserDN,
		print "adminUserPW=" + adminUserPW
		l.simple_bind_s(ldapManagerUserDN, adminUserPW)

		# create top level entry if it's not already there. For some reason this fails with an 
		# ldap.NO_SUCH_OBJECT error if it's not there, unlike other ldap searches that return 
		# an empty result set. This is why we have odd exception handling here and noplace else
		dn = suffix
		resultid = l.search(suffix, ldap.SCOPE_SUBTREE, '(&(objectclass=top)(objectclass=dcObject))', None)
		try:
			result_type, result_data = l.result(resultid, 0)
			if not result_data == []:
				print "Top level entry already exists"

		except ldap.NO_SUCH_OBJECT:
			attrs = {}
			attrs['objectclass'] = ['top', 'dcObject', 'organization']
			attrs['o'] = suffix
			ldif = ldap.modlist.addModlist(attrs)
			print "Adding root container"
			l.add_s(dn, ldif)
	

		# create admin
		dn = "cn=admin," + suffix
		resultid = l.search(suffix, ldap.SCOPE_SUBTREE, '(&(&(objectclass=organizationalRole)(objectclass=simpleSecurityObject))(cn=admin))', None)
		result_type, result_data = l.result(resultid, 0)
		if not result_data == []:
			print "admin user already exists, deleting and readding"
			l.delete_s(dn)
		
		attrs = {}
		attrs['objectclass'] = ['organizationalRole', 'simpleSecurityObject']
		attrs['cn'] = 'admin'
		attrs['userPassword'] = ldapPWHash = "{MD5}" + base64.encodestring(hashlib.md5(str(adminUserPW)).digest())
		ldif = ldap.modlist.addModlist(attrs)
		print "Adding admin user"
		l.add_s(dn, ldif)


		# create sync user
		dn = "cn=syncuser," + suffix
		resultid = l.search(suffix, ldap.SCOPE_SUBTREE, '(&(&(objectclass=organizationalRole)(objectclass=simpleSecurityObject))(cn=syncuser))', None)
		result_type, result_data = l.result(resultid, 0)
	
		if not result_data == []:
			print "syncuser user already exists, deleting and readding"
			l.delete_s(dn)
	
		attrs = {}
		attrs['objectclass'] = ['organizationalRole', 'simpleSecurityObject']
		attrs['cn'] = 'syncuser'
		attrs['userPassword'] = ldapPWHash = "{MD5}" + base64.encodestring(hashlib.md5(str(syncUserPW)).digest())
		ldif = ldap.modlist.addModlist(attrs)
		print "Adding sync user"
		l.add_s(dn, ldif)


		# create search user
		dn = "cn=search," + suffix
		resultid = l.search(suffix, ldap.SCOPE_SUBTREE, '(&(&(objectclass=organizationalRole)(objectclass=simpleSecurityObject))(cn=search))', None)
		result_type, result_data = l.result(resultid, 0)
		if not result_data == []:
			print "search user already exists, deleting and readding"
			l.delete_s(dn)
	
		attrs = {}
		attrs['objectclass'] = ['organizationalRole', 'simpleSecurityObject']
		attrs['cn'] = 'search'
		attrs['userPassword'] = ldapPWHash = "{MD5}" + base64.encodestring(hashlib.md5(str(searchUserPW)).digest())
		ldif = ldap.modlist.addModlist(attrs)
		print "Adding search user"
		l.add_s(dn, ldif)


		# add users ou
		dn = "ou=users," + suffix
		resultid = l.search(suffix, ldap.SCOPE_SUBTREE, '(&(objectclass=organizationalUnit)(ou=users))', None)
		result_type, result_data = l.result(resultid, 0)
		if not result_data == []:
			print "ou=users already exists"
		else:
			attrs = {}
			attrs['objectclass'] = ['organizationalUnit']
			attrs['ou'] = 'users'
			attrs['description'] = 'Users container'
			ldif = ldap.modlist.addModlist(attrs)
			print "Adding users root ou container"
			l.add_s(dn, ldif)


		# add groups ou
		dn = "ou=groups," + suffix
		resultid = l.search(suffix, ldap.SCOPE_SUBTREE, '(&(objectclass=organizationalUnit)(ou=groups))', None)
		result_type, result_data = l.result(resultid, 0)
		if not result_data == []:
			print "ou=groups already exists"
		else:
			attrs = {}
			attrs['objectclass'] = ['organizationalUnit']
			attrs['ou'] = 'groups'
			attrs['description'] = 'Groups container'
			ldif = ldap.modlist.addModlist(attrs)
			print "Adding groups root ou container"
			l.add_s(dn, ldif)
	

		print "Unbinding from ldap " + suffix
		l.unbind();


		## Some debain/redhat differences from here on out

		## RHEL
		if hubzero.utilities.misc.isRHEL():

			## authconfig only creates data in the 'default' provider, since there could be multiple
			## providers and default might already be used, we will not utilize authconfig for
			## sssd.conf file mods. authconfig will not overwrite settings to custom providers
			print "Writing out /etc/sssd/sssd.conf"
			_write_sssd_conf("cn=admin," + suffix, adminUserPW, "ou=groups," + suffix, "ou=users," + suffix)

			print "Running authconfig"
			rc, procOutput, procError = hubzero.utilities.misc.exShellCommand(['authconfig', 
				'--enablesssdauth', 
				'--enablesssd', 
				'--enableldap', 
				'--enableldapauth',
				'--update'])

			#print procOutput.strip()
			if rc:
				print procError

			print "Restarting sssd"
			if os.path.exists('/etc/init.d/sssd'):
				rc, procOutput, procError = hubzero.utilities.misc.exShellCommand(['/etc/init.d/sssd', 'restart'])
				#print procOutput.strip()
				if rc: 
					print procError

			print "Restarting slapd"
			if os.path.exists('/etc/init.d/slapd'):
				rc, procOutput, procError = hubzero.utilities.misc.exShellCommand(['/etc/init.d/slapd', 'restart'])
				#print procOutput.strip()
				if rc: 
					print procError

			print "Setting slapd for autostart"
			if os.path.exists('/etc/init.d/slapd'):
				rc, procOutput, procError = hubzero.utilities.misc.exShellCommand(['chkconfig', 'slapd', 'on'])
				#print procOutput.strip()
				if rc: 
					print procError


		## Debian
		if hubzero.utilities.misc.isDebian():

			print "Writing out /etc/nslcd.conf"
			_write_nslcd_conf(suffix, "cn=search," + suffix, searchUserPW, "cn=admin," + suffix)

			print "Restarting nslcd"
			if os.path.exists('/etc/init.d/nslcd'):
				rc, procOutput, procError = hubzero.utilities.misc.exShellCommand(['/etc/init.d/nslcd', 'stop'])
				#print procOutput.strip()
				if rc: 
					print procError

				rc, procOutput, procError = hubzero.utilities.misc.exShellCommand(['/etc/init.d/nslcd', 'start'])
				#print procOutput.strip()
				if rc: 
					print procError

			print "restarting nscd"
			if os.path.exists('/etc/init.d/nscd'):
				rc, procOutput, procError = hubzero.utilities.misc.exShellCommand(['/etc/init.d/nscd', 'stop'])
				#print procOutput.strip()
				if rc: 
					print procError
				rc, procOutput, procError = hubzero.utilities.misc.exShellCommand(['/etc/init.d/nscd', 'start'])
				#print procOutput.strip()
				if rc: 
					print procError

			print "Writing out /etc/nsswitch.conf"
			_write_nsswitch_conf()


		# Write ldap info to the /etc/hubzero.conf file 
		hubzero.config.hubzerositeconfig.setHubzeroConfigOption('ldap', 'adminuserDN', adminUserDN) 
		hubzero.config.hubzerositeconfig.setHubzeroConfigOption('ldap', 'searchuserDN', searchUserDN) 
		hubzero.config.hubzerositeconfig.setHubzeroConfigOption('ldap', 'syncuserDN', syncUserDN) 
		hubzero.config.hubzerositeconfig.setHubzeroConfigOption('ldap', 'adminuserPW', adminUserPW) 
		hubzero.config.hubzerositeconfig.setHubzeroConfigOption('ldap', 'searchuserPW', searchUserPW) 
		hubzero.config.hubzerositeconfig.setHubzeroConfigOption('ldap', 'syncuserPW', syncUserPW) 

	# end if for ldap already being setup for us


	# update the jos_components table record for the com_system component
	hubzero.config.webconfig.addComponentParam("com_system", "ldap_managerdn" , "cn=admin," + suffix)
	hubzero.config.webconfig.addComponentParam("com_system", "ldap_managerpw" , adminUserPW)
	hubzero.config.webconfig.addComponentParam("com_system", "ldap_basedn" , suffix)
	hubzero.config.webconfig.addComponentParam("com_system", "ldap_searchdn" , "cn=search," + suffix)
	hubzero.config.webconfig.addComponentParam("com_system", "ldap_searchpw" , searchUserPW)

	# add hubrepo password to hubzero.secrets file, if file is not there, create it
	hubzeroSecretsFilename = "/etc/hubzero.secrets"
	secretsConfig = ConfigParser.ConfigParser()
	secretsConfig.optionxform = str

	# read in existing data from hubzero.secrets
	if os.path.exists(hubzeroSecretsFilename):
		f1 = open(hubzeroSecretsFilename, "r")
		secretsConfig.readfp(f1)
		f1.close()

	# write our new password data
	secretsConfig.set("DEFAULT", "LDAP-ADMINPW", adminUserPW)
	secretsConfig.set("DEFAULT", "LDAP-SEARCHPW", searchUserPW)
	secretsConfig.set("DEFAULT", "LDAP-SYNCPW", syncUserPW)

	f2 = open(hubzeroSecretsFilename, "w")
	secretsConfig.write(f2)
	f2.close()

	print "==============================================="
	print "LDAP default setup complete"
	print "==============================================="
	print "LDAP admin user pw: " + adminUserPW
	print "LDAP search user pw: " + searchUserPW
	print "LDAP sync user pw: " + syncUserPW
	
	print "LDAP config written to com_system component params field in database: " + hubzero.config.webconfig.getDefaultSite()

	return(0)
	

def _delete_ldap_user(args):
	hubzero.utilities.user._delete_ldap_user(args.username)


def _add_ldap_user(args):
	
	username = args.username
	suffix = getLDAPSiteSuffix()
	
	if not args.promptforpw:
		pw = hubzero.config.passwords.generateAlphaNumPassword(10)
	else:
		pw = getAlphaNumericPW()	

	hubzero.utilities.user._add_ldap_user(username, pw, args.uidnumber, 'none')
	
	print "New user pw: " + pw


def _sync_ldap_users(args):
	hubzero.utilities.user.syncusers("", args.purgeorphans, args.syncattributes)


def _sync_ldap_groups(args):
	hubzero.utilities.group.syncgroups("", args.purgeorphans, args.addgroupmemberships)


def _delete_ldap_groups(args):
	
	# by only deleting groups in db, any ldap only groups will be untouched
	if not args.deleteorphans:
		print "Orphaned groups in ldap without a corresponding group in the database will be preserved"

		dbPW = hubzero.config.webconfig.getWebConfigDBPassword()
		dbUserName = hubzero.config.webconfig.getWebConfigDBUsername()
		dbName = hubzero.config.webconfig.getWebConfigDBName()
		db =  hubzero.data.db.MySQLConnection("localhost", dbName, dbUserName, dbPW)
	
		# grab all users groups db 
		sql = "SELECT `cn`, `gidNumber` from jos_xgroups"

		for group in db.query(sql, None):
			print "Deleting " + group.cn

			try:
				hubzero.utilities.group.del_ldap_group(group.cn)
			except ldap.NO_SUCH_OBJECT:
				print " Group not found"

	else:
		print "Orphaned groups in ldap without a corresponding group in the database will be DELETED"

		# grab all groups from ldap
		hubLDAPAcctMgrDN = hubzero.config.webconfig.getComponentParam("com_system", "ldap_managerdn")
		hubLDAPAcctMgrPW = hubzero.config.webconfig.getComponentParam("com_system", "ldap_managerpw")
		hubLDAPBaseDN = hubzero.config.webconfig.getComponentParam("com_system", "ldap_basedn")
		siteSuffix = getLDAPSiteSuffix()
		
		l = ldap.initialize('ldap://localhost')
		l.simple_bind_s(hubLDAPAcctMgrDN, hubLDAPAcctMgrPW)
		
		try:
			r = l.search_s("ou=groups," + siteSuffix, ldap.SCOPE_SUBTREE, '(objectClass=posixGroup)',   ['cn'])

		except ldap.NO_SUCH_OBJECT:
			print "No groups"
	
		for dn,entry in r:
			cn = str(entry['cn'][0])
			hubzero.utilities.group.delete_ldap_group(cn)


# #######################################################################
# Main
# main parser
parser = argparse.ArgumentParser(prog="hzldap")
subparsers = parser.add_subparsers()

parser_init = subparsers.add_parser('init', help='initialize hub ldap')
parser_init.add_argument("--promptforadminpw", help="Prompt for password", action="store_true", default=False)
parser_init.add_argument("--promptforsearchpw", help="Prompt for password", action="store_true", default=False)
parser_init.add_argument("--promptforsyncuserpw", help="Prompt for password", action="store_true", default=False)
parser_init.set_defaults(func=_init_ldap)

parser_adduser = subparsers.add_parser('adduser', help='add ldap user')
parser_adduser.add_argument("username", help="username")
parser_adduser.add_argument("--promptforpw", help='prompt for a user password, if not provided, one will be created and output and output to screen', action="store_true", default=False)
parser_adduser.add_argument("--uidnumber", help="Hub UserIDNumber", default="99999999")
parser_adduser.set_defaults(func=_add_ldap_user)

parser_deleteuser = subparsers.add_parser('deleteuser', help='delete ldap user')
parser_deleteuser.add_argument("username", help="username")
parser_deleteuser.set_defaults(func=_delete_ldap_user)

parser_syncusers = subparsers.add_parser('syncusers', help='add all users from db into ldap')
parser_syncusers.add_argument("--purgeorphans", help='remove any ldap user not in the jos_users table', action="store_true", default=True)
parser_syncusers.add_argument("--syncattributes", help='sync non required ldap user attributes', action="store_true", default=True)
parser_syncusers.set_defaults(func=_sync_ldap_users)

parser_syncgroups = subparsers.add_parser('syncgroups', help='add all groups from db into ldap')
parser_syncgroups.add_argument("--addgroupmemberships", help='add newly created users group memberships', action="store_true", default=True)
parser_syncgroups.add_argument("--purgeorphans", help='remove any ldap group not in the jos_xgroups table', action="store_true", default=True)
parser_syncgroups.set_defaults(func=_sync_ldap_groups)

args =  parser.parse_args()
rc = args.func(args)

exit(rc)

