#!/usr/bin/python3.11
# ------------------------------------------------------------------------------
# Copyright [2023-2024] Open Text.
#
# The only warranties for products and services of Open Text and its
# affiliates and licensors (“Open Text”) are as may be set forth in the
# express warranty statements accompanying such products and services.
# Nothing herein should be construed as constituting an additional
# warranty. Open Text shall not be liable for technical or editorial
# errors or omissions contained herein. The information contained herein
# is subject to change without notice.
#
# Except as specifically indicated otherwise, this document contains
# confidential information and a valid license is required for possession,
# use or copying. If this work is provided to the U.S. Government,
# consistent with FAR 12.211 and 12.212, Commercial Computer Software,
# Computer Software Documentation, and Technical Data for Commercial Items
# are licensed to the U.S. Government under vendor's standard commercial
# license.
# -------------------------------------------------------------------------

import os
import sys
import argparse
import logging
from pathlib import Path
import shutil
import configparser
import oes_cert_mgmt_utils
import json


#Arguments
#1 - operation with below valid values
#CERTCHANGE = 1
#RECONFIG = 2
#MOVETOEDIR = 4
#EDIRCERTCHANGE = 3
#2 - certificate
#3 - cacertificate
#4 - privatekey
#5 - restart
# For operations 3 and 4, no arguments required
# operation 3 - Service should reconfigure to use eDirectory Server certificate either servercert.pem Or serverECCert.pem
# operation 4 - Indication that eDirectory server certificate has changed. Service should take required measures
# operation 1 and 2 - Reconfigure with new certificate, privatekey and cacertificate

#Sample usage
#1. oes-cert-mgmt-cis-core-reconfig --operation 3 (Change in eDirectory server certificate)
#3. oes-cert-mgmt-cis-core-reconfig --operation 1 --certificate /tmp/newcert.pem --cacertificate /tmp/newncacert.pem --privatekey /tmp/newcertprivatekey.pem


#def setupcliargs():
args = []
EDIRECCERT = "/etc/ssl/servercerts/servercert.pem"
CERTCHANGE = 1
RECONFIG = 2
MOVETOEDIR = 4
EDIRCERTCHANGE = 3
certificate = ""
cacertificate = ""
privatekey = ""
CONF_FILE_PATH = "/etc/opt/novell/cis/config"
CONF_FILE_PATH_FOR_SCALE = "/etc/opt/novell/cis-scale/config"
CRT_SEARCH_STRING = "CERTS_PATH"
SSLKEY_FILE_PATH= "/etc/ssl/servercerts/serverkey.pem"
CA_CERT_PATH = "/etc/opt/novell/certs/SSCert.pem"

LOG_PATH = "/var/opt/novell/log/oes-cert-mgmt/oes-cert-mgmt.log"

logger = logging.getLogger('Logger')
logLevel = oes_cert_mgmt_utils.getloglevel()
logger.setLevel(logLevel)

def initialize_logger():
    """
    Initialize logger.
    """
    rfh = logging.handlers.RotatingFileHandler(LOG_PATH, maxBytes=1024*1024*10, backupCount=2)
    formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s', '%m/%d/%Y %I:%M:%S')
    rfh.setFormatter(formatter)
    logger.addHandler(rfh)

def parsecli():
    global args
    parser = argparse.ArgumentParser()
    parser.add_argument('--operation', type=int)
    parser.add_argument('--certificate', type=str)
    parser.add_argument('--cacertificate', type=str)
    parser.add_argument('--privatekey', type=str)
    parser.add_argument('--restart', type=str)
    args = parser.parse_args()


def validatecliargs():
    #Operation is mandatory
    if args.operation:
        operation = args.operation
        if (operation != CERTCHANGE  and operation != RECONFIG and operation != EDIRCERTCHANGE and operation != MOVETOEDIR):
            logger.info("ciscore: Invalid Operation passed")
            exit(200)
    else:
        logger.info("ciscore: Operation argument is missing")
        exit(200)

    #For reconfiguration, new certificate should be passed
    if args.certificate:
        certificate = args.certificate
    if args.cacertificate:
        cacertificate = args.cacertificate
    if args.privatekey:
        privatekey = args.privatekey

    if (args.operation == CERTCHANGE or args.operation == RECONFIG):
        if len(sys.argv) < 4:
            exit(200)

        if (len(args.certificate) == 0  or len(cacertificate) == 0 or len(privatekey) == 0):
            exit(200)
def get_cert_path(filePath, searchString):
    """
    Get the certificate path from the conf file
    """
    found = False
    certificatePath = ""
    try:
        with open(filePath,'r') as file:
            for line in file.readlines():
                if not line.strip().startswith("#"):
                    if searchString in line:
                        elements = line.strip().split("=")
                        if len(elements) >= 2:
                            certificatePath = elements[1].split('\t')[-1].split(' ')[-1].strip("\"")
                            found = True
                            break

            if found == False:
                logger.warn( "ciscore: "+ str(searchString)+ " is not found in conf file")
                exit(200)

    except FileNotFoundError as e:
        logger.warn("cis - " + "File " + filePath + " not present")

    finally:
        if(file != None):
            file.close()

    return certificatePath



def configwithnewcert():
    logger.info("ciscore: Reconfiguring cis core services with new certificate")

    try:
            if (os.path.exists(args.certificate)) and (os.path.exists(args.cacertificate)) and (os.path.exists(args.privatekey)):

                cert_dir = get_cert_path(config_file_path, CRT_SEARCH_STRING)
                privatekeyPath = cert_dir + "/serverkey.pem"
                cacertificatePath = cert_dir + "/rootCAs" + "/SSCert.pem"
                logger.info("ciscore: private key path " + privatekeyPath)

                if((args.certificate)==certificatePath  and args.privatekey==privatekeyPath):
                    logger.info("ciscore: the certificate already exists")
                    exit(200)
                elif((args.certificate)!=certificatePath  and args.privatekey!=privatekeyPath):
                    shutil.copy(args.certificate, certificatePath)
                    shutil.copy(args.privatekey, privatekeyPath)
                    shutil.copy(args.cacertificate, cacertificatePath)
                    logger.info("ciscore: Reconfiguring cis core services with new certificate")
                else:
                    logger.error("ciscore: invalid paths are given")
                    logger.info("ciscore: certificate value is "+str(args.certificate)+"privatekey value is"+str(args.privatekey))
                    exit(200)
            else:
                logger.error("ciscore: certificate path not found")

    except FileNotFoundError as e:
        logger.error("ciscore: File not present \n" f"{e}")

def handleedircertchange():
    logger.info("ciscore: Handling change in eDirectory server certificate")

    if (certificatePath == EDIRECCERT):
        logger.info("ciscore: the service is using eDirectory certificate")
    else:
        if(os.path.exists(EDIRECCERT)) and (os.path.exists(SSLKEY_FILE_PATH)) :
            cert_dir = get_cert_path(config_file_path, CRT_SEARCH_STRING)

            privatekeyPath = cert_dir + "/serverkey.pem"
            cacertificatePath = cert_dir + "/rootCAs" + "/SSCert.pem"

            logger.info("ciscore: private key path " + privatekeyPath)
            shutil.copy(EDIRECCERT, certificatePath)
            shutil.copy(SSLKEY_FILE_PATH, privatekeyPath)
            shutil.copy(CA_CERT_PATH, cacertificatePath)

            logger.info("ciscore: the path of certificate is "+ str(privatekeyPath) +". Handling change in eDirectory server certificate")
        else:
            logger.error("ciscore: eDirectory server certificate not found")

def getdeploymenttype():
    if os.path.exists('/etc/opt/novell/cis/configurationStatus.json'):
        configStatusjson = open('/etc/opt/novell/cis/configurationStatus.json')
        data = json.load(configStatusjson)
        deploymenttype = data['configType']
        logger.info("ciscore: deployment type %s", deploymenttype)
        configStatusjson.close()
        return deploymenttype
    else:
        return ""

def handlerestartciscoreservice():
    deploymenttype = getdeploymenttype()
    try:
        logger.info("ciscore: Restarting cis core services")
        if (deploymenttype == "standalone" or deploymenttype == "core") :
            os.popen("systemctl restart oes-cis-server.target")
        elif (deploymenttype == "scale"):
            cmd = "systemctl status oes-cis-gatewayforscale.service > /dev/null 2>&1"
            gatewayforscale = os.system(cmd)
            if gatewayforscale == 0:
                os.popen("systemctl restart oes-cis-gatewayforscale.service")
                logger.debug("ciscore: cis-gatewayforscale service restarted successfully...")
            cmd = "systemctl status oes-cis-dataatscale.service > /dev/null 2>&1"
            dataatscale = os.system(cmd)
            if dataatscale == 0:
                os.popen("systemctl restart oes-cis-dataatscale.service")
                logger.debug("ciscore: cis-dataatscale service restarted successfully...")
        else:
            logger.debug("ciscore: deployment type not matching to restart core services")

    except :
        logger.error("ciscore: failed to start cis core service")

def main():
    initialize_logger()
    logger.info("ciscore: Reconfiguration of cis core services")

    global config_file_path
    config_file_path = CONF_FILE_PATH

    #Only root can execute
    if(os.geteuid() != 0):
        logger.info("ciscore: Only root can reconfigure the services")
        exit(200)

    #Parse and validate CLI args
    parsecli()
    validatecliargs()

    global certificatePath
    if (getdeploymenttype() == "scale"):
        config_file_path = CONF_FILE_PATH_FOR_SCALE
    certificatePath = get_cert_path(config_file_path , CRT_SEARCH_STRING)
    certificatePath = certificatePath + "/servercert.pem"

    if (args.operation == RECONFIG or args.operation == MOVETOEDIR):
        logger.error("ciscore: cert RECONFIG and MOVETOEDIR is not supported by cis core services")
        exit(205)
    elif (args.operation == CERTCHANGE):
        configwithnewcert()
    elif(args.operation == EDIRCERTCHANGE):
        handleedircertchange()
    else:
        logger.error("ciscore: user entered invalid operation")
        exit(200)
    if(args.restart):
        if (args.restart).lower():
            if(args.restart == "yes"):
               handlerestartciscoreservice()
            else:
              exit(0)

    exit(0)


#Main code starts here
if __name__ == '__main__':
    main()

