#!/usr/bin/env python3
# coding:utf-8

#import re
import time
from datetime import datetime
from cplCommand import LocalClient, SSHClient

# TODO: we might need other cmd/kadmin options, either ssh or kadmin instead of kadmin.local

msg0001 = "no password provided to sync principal '{0}' with kerberos"
msg0002 = "kerberos principal already exists for: '{0}'"
msg0003 = "failed to create principal: '{0}'"
msg0004 = "successfully created kerberos principal: '{0}'"
msg0005 = "no kerberos principal found for: '{0}'"
msg0006 = "failed to set principal expiration date: '{0}'"
msg0007 = "successfully set expiration date '{0}' for kerberos principal '{1}'"
msg0008 = "failed to set principal password: {0}"
msg0009 = "sucessfully changed principal password for: '{0}'"
msg0010 = "failed to lock principal: '{0}'"
msg0011 = "sucessfully locked principal: '{0}'"
msg0012 = "failed to unlock principal: '{0}'"
msg0013 = "sucessfully unlocked principal: '{0}'"
msg0014 = "no kerberos principal found during delete for: '{0}'"
msg0015 = "failed to delete principal: '{0}'"
msg0016 = "sucessfully deleted principal: '{0}'"
msg0017 = "no new name provided for principal '{0}'"
msg0018 = "failed to rename principal: '{0}'"
msg0019 = "sucessfully renamed principal '{0}' to '{1}'"
msg0020 = "fetching configuration for: '{0}'"
msg0021 = "failed to connect to ssh host: '{0}'"


class KAdmin(): 

	def __init__(self, postcmd):

		# get kadmin configuration
		self.kadmin_config = self.__getKadminConfig()

		self.krb = self.kadmin_config['handlekrb5']
		if not self.krb: return
		self.ka_cmd = self.__getKAClient()
		self.krb_policy = self.kadmin_config['userpolicy']

		if self.kadmin_config['krbadminpwd']:
			pwd = self.kadmin_config['krbadminpwd'].replace('"','""',)
			self.kadm = 'kadmin -w "{0}"'.\
				format(pwd)
		else:
			self.kadm = "kadmin.local"


	def __getKadminConfig(self):

		cfg_def = {
			'handlekrb5' : False,
			'userpolicy' : 'users',
			'krbadminpwd' : '',
			'sshhost' : None,
			'sshuser' : None,
			'sshpwd' : None,
			'sshkey' : None,
			'sshport' : None,
			'sshhkeysave' : False,
			'sshhkeypol' : 'reject',
			}
		cfg_ver = {
			'handlekrb5' : ('bool', ),
			'userpolicy' : ('str', ),
			'krbadminpwd' : ('str', ),
			'sshhost' : ('str', ),
			'sshuser' : ('str', ),
			'sshpwd' : ('str', ),
			'sshkey' : ('str', ),
			'sshport' : ('int', ),
			'sshhkeysave' : ('bool', ),
			'sshhkeypol' : ('str', ),
			}

		# get kadmin connection configuration
		self.logger.debug(msg0020.format('KAdmin'))
		self.config.updateDefaults('KAdmin', cfg_def)
		self.config.updateVerify(cfg_ver)

		return self.config.getSection('KAdmin')


	def __getKAClient(self):

		mod_name = self.class_name+'.__getKAClient'

		if self.kadmin_config['sshhost']:

			# using ssh client
			cmd_client = SSHClient(self.logger)
			# connect to ssh host
			value = cmd_client.Connect(
					host = self.kadmin_config['sshhost'],
					username = self.kadmin_config['sshuser'],
					password = self.kadmin_config['sshpwd'],
					keyfile = self.kadmin_config['sshkey'],
					port = self.kadmin_config['sshport'],
					hkeysave = self.kadmin_config['sshhkeysave'],
					hkeypol = self.kadmin_config['sshhkeypol'],
				)

			if value != 0:
				self.exit(1, msg0021.\
					format(self.kadmin_config['sshhost']))

			return cmd_client
		else:
			# using local client
			return LocalClient(self.logger)


	def _array4Log(self, a):
		return ';'.join([x.decode() for x in a])


	def testKrbPrinc(self):

		mod_name = self.class_name+'.testKrbPrinc'
		if not self.krb: return

		kadm_cmd = '{0} getprinc "{1}"'.format(self.kadm, self.cn)

		value, std_out, std_err = self.ka_cmd.runCmd(kadm_cmd)
		if value != 0: return False
		return True


	def setKrbExpire(self):

		mod_name = self.class_name+'.setKrbExpire'
		if not self.krb: return

		kadm_cmd = '{0} modprinc -expire "{1}" "{2}"'

		if not self.testKrbPrinc():

			self.logger.info(msg0005.format(self.cn))
			self.addKrbPrinc()
			return

		# if we can figure out the local timezone name we might use
		#import pytz
		#local_tzname = "Europe/Berlin"
		#datetime.fromtimestamp(sh_exp*60*60*24, pytz.timezone(local_tzname)).\
		#strftime('%Y-%m-%d %H:%M:%S %z')

		try:
			sh_exp = int(self.ldap_data['shadowExpire'][0].decode())
			tz_offset = time.strftime("%z", time.localtime())
			d_str = datetime.fromtimestamp(sh_exp*60*60*24).\
				strftime('%Y-%m-%d %H:%M:%S') + ' ' + tz_offset
		except KeyError:
			d_str = 'never'


		value, std_out, std_err = self.ka_cmd.runCmd(kadm_cmd.\
			format(self.kadm, d_str, self.cn))

		if value != 0:
			self.exit(1, msg0006.format(self._array4Log(std_err)))

		self.logger.info(msg0007.format(d_str, self.cn))


	def addKrbPrinc(self):

		mod_name = self.class_name+'.addKrbPrinc'
		if not self.krb: return

		kadm_cmd = '{0} addprinc -policy {1} -randkey -x "dn={2}" "{3}"'.\
			format(self.kadm, self.krb_policy, self.dn, self.cn)

		if self.testKrbPrinc():

			self.logger.info(msg0002.format(self.cn))
			return

		value, std_out, std_err = self.ka_cmd.runCmd(kadm_cmd)

		if value != 0:
			self.exit(1, msg0003.format(self._array4Log(std_err)))

		self.logger.info(msg0004.format(self.cn))

		self.setKrbExpire()

		

	def delKrbPrinc(self):

		mod_name = self.class_name+'.delKrbPrinc'
		if not self.krb: return

		kadm_cmd = '{0} delprinc -force "{1}"'.\
			format(self.kadm, self.cn)

		if not self.testKrbPrinc():

			self.logger.info(msg0014.format(self.cn))
			return

		value, std_out, std_err = self.ka_cmd.runCmd(kadm_cmd)

		if value != 0:
			self.exit(1, msg0015.format(self._array4Log(std_err)))

		self.logger.info(msg0016.format(self.cn))


	def renKrbPrinc(self):

		mod_name = self.class_name+'.renKrbPrinc'
		if not self.krb: return

		kadm_cmd = '{0} renprinc -force "{1}" "{2}"'

		try:
			new_name = self.opt[0]
		except IndexError:
			self.exit(1, msg0017.format(self.cn))

		if not self.testKrbPrinc():

			self.logger.info(msg0005.format(self.cn))
			self.addKrbPrinc()

		value, std_out, std_err = self.ka_cmd.runCmd(kadm_cmd.\
			format(self.kadm, self.cn, new_name))

		if value != 0:
			self.exit(1, msg0018.format(self._array4Log(std_err)))

		self.logger.info(msg0019.format(self.cn, new_name))


	def lockKrbPrinc(self):

		mod_name = self.class_name+'.lockKrbPrinc'
		if not self.krb: return

		kadm_cmd = '{0} modprinc -allow_tix "{1}"'.\
			format(self.kadm, self.cn)

		if not self.testKrbPrinc():

			self.logger.info(msg0005.format(self.cn))
			self.addKrbPrinc()

		value, std_out, std_err = self.ka_cmd.runCmd(kadm_cmd)

		if value != 0:
			self.exit(1, msg0010.format(self._array4Log(std_err)))

		self.logger.info(msg0011.format(self.cn))


	def unlockKrbPrinc(self):

		mod_name = self.class_name+'.unlockKrbPrinc'
		if not self.krb: return

		kadm_cmd = '{0} modprinc +allow_tix "{1}"'.\
			format(self.kadm, self.cn)

		if not self.testKrbPrinc():

			self.logger.info(msg0005.format(self.cn))
			self.addKrbPrinc()

		value, std_out, std_err = self.ka_cmd.runCmd(kadm_cmd)

		if value != 0:
			self.exit(1, msg0012.format(self._array4Log(std_err)))

		self.logger.info(msg0013.format(self.cn))


	def pwsyncKrbPrinc(self):

		mod_name = self.class_name+'.pwsyncKrbPrinc'
		if not self.krb: return

		kadm_cmd = '{0} change_password -pw "{1}" "{2}"'

		try:
			new_pwd = self.opt[0]
		except IndexError:
			self.exit(253, msg0001.format(self.cn))

		if not self.testKrbPrinc():

			self.logger.info(msg0005.format(self.cn))
			self.addKrbPrinc()

		# double " because of the weired kadmin interpretation of double quotes
		new_pwd = new_pwd.replace('"','""',)
		new_pwd = new_pwd.replace('$','\$',)

		value, std_out, std_err = self.ka_cmd.runCmd(kadm_cmd.\
			format(self.kadm, new_pwd, self.cn))

		if value != 0:
			self.exit(255, msg0008.format(self._array4Log(std_err)))

		self.logger.info(msg0009.format(self.cn))

