# Copyright 2017 VMware, Inc. All rights reserved. -- VMware Confidential
"""
Utilities that are required duing patching and should be only imported on the
destination system (ie. after the dependant components were installed).
This is to ensure that we won't get ImportErrors when loading the patch on the
pre-patched/source system.
"""
import logging
import os
import sys
# Adding cis modules to path.
sys.path.append(os.environ['VMWARE_PYTHON_PATH'])


from cis.cisreglib import (
    _get_syscfg_info, LookupServiceClient, SsoClient, AuthzClient,
    CisregOptionParser)
from cis.defaults import get_component_home_dir
from cis.vecs import SsoGroup, vmafd_machine_id
from cis.utils import get_deployment_nodetype
import vmafd


logger = logging.getLogger(__name__)


def is_embedded():
   try:
      node_type = get_deployment_nodetype()
      logger.info('Node type is %s' % node_type)
      return node_type == 'embedded'
   except Exception as e:
      logger.warning('Failed to get_deployment_nodetype: %s' % str(e))
      return False


class AuthzPatch(object):
    """
    Patches authz by reloading roles in defRoles.xml.
    Roles to groups mapping present in vpxd-service-spec.prop need to be copied
    to GROUP_ROLE_MAP.
    All methods are indempotent.
    """

    ROLES_TO_RELOAD = ['AutoUpdateUser']
    GROUP_ROLE_MAP = {"AutoUpdate": "AutoUpdateUser"}

    def __init__(self):
        """
        Initializes AuthzClient needed for patching roles and permissions
        """
        ls_url, self.domain_name = _get_syscfg_info()
        logger.debug("Connecting to Lookup Service, url=%s", ls_url)
        ls_obj = LookupServiceClient(ls_url, retry_count=1)
        sts_url, sts_cert_data = ls_obj.get_sts_endpoint_data()

        logger.debug("Logging into SSO AdminClient as machine solution user, "
                     "url=%s", sts_url)
        MACHINE_NAME = 'machine'
        self.sso_client = SsoClient.init_with_vecs(sts_url, sts_cert_data,
                                                   MACHINE_NAME, MACHINE_NAME)
        ls_obj.set_sso_client(self.sso_client)
        authz_url, authz_cert_data = ls_obj.get_authz_endpoint_data()
        logger.debug("Connecting to Authz, url=%s", authz_url)
        self.authz_client = AuthzClient(authz_url, self.sso_client)
        logger.debug("Succesfully initialized AuthzClient")

    def reload_roles(self):
        """
        Reloads roles defined in defRoles.xml (new roles will be added,
        existing will not be affected) according to ROLES_TO_RELOAD list.
        This is to be consistent with the logic in upgrade which won't recreate
        deleted roles.
        """
        vpx_dir = get_component_home_dir('vpx')
        cisreg_options = {
            'permission.newRole':
                os.path.join(vpx_dir, 'permission/defRoles.xml')
        }
        cisreg_optparser = CisregOptionParser(cisreg_options)
        # Filter roles from defRoles.xml
        roles = [role for role in cisreg_optparser.get_roles()
                 if role['name'] in AuthzPatch.ROLES_TO_RELOAD]
        added = self.authz_client.load_roles(roles)
        logger.info(
            "Succesfully loaded Authz roles, added %d new roles", added)

    def assign_groups_to_roles(self):
        """
        Assign groups to roles as defined in vpxd-service-spec.prop.
        """
        for group, role in AuthzPatch.GROUP_ROLE_MAP.items():
            self.authz_client.set_permission(
                self.domain_name, role, group, True)


class SSOPatch(object):
    """
    Patches SSO groups and solution users.
    All methods are indempotent so just add more users/groups in the future
    patches.
    """

    # groups to add stored as name -> description
    GROUPS = {
        'AutoUpdate': "Users allowed to perform update related operations"}

    # user-group relations stored as username -> list of groups
    USER_GROUPS = {"machine-{machine_id}": ['AutoUpdate']}

    def __init__(self):
        """
        Initializes vecs.SsoGroup using machine credentials from vmafd.
        """
        vmafdClient = vmafd.client('localhost')
        username = vmafdClient.GetMachineName()
        password = vmafdClient.GetMachinePassword()
        self.sso_group = SsoGroup(username, password)

    def add_groups(self):
        """
        Ensures that given groups are present. Ignores already exsiting groups.
        """
        logger.debug('Adding SSO groups')
        for name, description in SSOPatch.GROUPS.items():
            if self.sso_group.create(name, description):
                logger.info('Added "%s" group', name)
            else:
                # SsoGroup.create returns False when group already exists
                logger.debug('Group "%s" already exists', name)

    def assign_users_to_groups(self):
        """
        Ensures that solutions users are assigned to given groups.
        """
        logger.debug('Assigning solution users to SSO groups')
        machine_id = vmafd_machine_id()
        for username, groups in SSOPatch.USER_GROUPS.items():
            username = username.replace('{machine_id}', machine_id)
            for group in groups:
                self.sso_group.add_user(group, username)
                logger.info('User %s added to %s group', username, group)
