# Copyright (C) 2005-2021 Splunk Inc. All Rights Reserved.
import sys
try:
    from urllib.request import urlopen, Request, build_opener
except ImportError:
    from urllib2 import urlopen, Request, build_opener

from splunk import getDefault
import splunk.admin as admin
from splunk.entity import controlEntity
from splunk.clilib.bundle_paths import make_splunkhome_path

sys.path.append(make_splunkhome_path(['etc', 'apps', 'SA-Hydra-inframon', 'bin']))
import hydra_inframon
from hydra_inframon.models import HydraGatewayStanza

logger = hydra_inframon.setupLogger(log_format='%(asctime)s %(levelname)s [HydraGatekeeper] %(message)s', level="INFO",
                           log_name="hydra_inframon_gatekeeper.log", logger_name="hydra_inframon-gatekeeper")


class HydraGatekeeperHandler(admin.MConfigHandler):
    def setup(self):
        pass

    def handleList(self, confInfo):
        """
        Provide an authentication proxy for the hydra gateway service, as a
        side effect validate the gateway is turned on and running properly.

        If the gateway is either not on or configured properly return an key
        value of DEFER indicating a client should wait before reauthenticating.
        """
        # Get the hydra gateway config
        stanza = HydraGatewayStanza.from_name("gateway", "SA-Hydra-inframon", session_key=self.getSessionKey())
        if stanza is not None and stanza.port is not None:
            port = stanza.port
        else:
            port = 8008
        gateway_uri = "https://" + getDefault("host") + ":" + str(port)

        #Get the Key File
        challenge_key, auth_key = self.get_keys()

        #Validate System
        if not self.validate_challenge_key(gateway_uri, challenge_key):
            #Gateway failed challenge and must be restarted
            logger.warning(
                "gateway failed challenge validation and will be restarted, deferring current authentication request")
            self.restart_gateway()
            #Let the client know they need to re-authenticate
            auth_key = "DEFER"

        #Return the gateway information
        gateway = confInfo["hydra_gateway"]
        gateway['key'] = auth_key

    def get_keys(self):
        """
        Read the challenge and auth keys from the filesystem

        RETURNS tuple of challenge and auth keys
        """
        try:
            with open(make_splunkhome_path(["etc", "apps", "SA-Hydra-inframon", "local", "run", "hydra_gateway.key"]), 'r') as f:
                challenge_key = f.readline().strip("\r\n")
                auth_key = f.readline().strip("\r\n")
                return (challenge_key, auth_key)
        except IOError:
            return ("", "")

    def validate_challenge_key(self, gateway_uri, key):
        """
        Challenge the local gateway on the expected port and ensure that it is
        running under the proper configuration

        RETURNS True if challenge passes, False otherwise.
        """
        try:
            opener = build_opener()
            req = Request(gateway_uri + "/hydra/admin/challenge")
            resp = opener.open(req)
            gateway_challenge_key = resp.read().decode('utf-8').strip("\r\n")
            return gateway_challenge_key == key
        except Exception as e:
            logger.exception("[ValidateChallengeKey] could not validate hydra gateway challenge: %s", str(e))
            return False

    def restart_gateway(self):
        """
        Restart the gateway by disabling and enabling the scripted input process
        """
        input_uri = '/servicesNS/nobody/SA-Hydra-inframon/data/inputs/script/%24SPLUNK_HOME%252Fetc%252Fapps%252FSA-Hydra-inframon%252Fbin%252Fbootstrap_hydra_gateway.py/'
        controlEntity('disable', input_uri + "disable", sessionKey=self.getSessionKey())
        controlEntity('enable', input_uri + "enable", sessionKey=self.getSessionKey())


admin.init(HydraGatekeeperHandler, admin.CONTEXT_NONE)