#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright: (c) 2017, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

from __future__ import (absolute_import, division, print_function)
__metaclass__ = type

DOCUMENTATION = '''
---
module: ovirt_permission
short_description: Module to manage permissions of users/groups in oVirt/RHV
version_added: "1.0.0"
author:
- "Ondra Machacek (@machacekondra)"
- "Martin Necas (@mnecas)"
description:
    - Module to manage permissions of users/groups in oVirt/RHV.
options:
    role:
        description:
            - Name of the role to be assigned to user/group on specific object.
        default: UserRole
        type: str
    state:
        description:
            - Should the permission be present/absent.
        choices: [ absent, present ]
        default: present
        type: str
    object_id:
        description:
            - ID of the object where the permissions should be managed.
        type: str
    object_name:
        description:
            - Name of the object where the permissions should be managed.
        type: str
    object_type:
        description:
            - The object where the permissions should be managed.
        choices:
            - cluster
            - cpu_profile
            - data_center
            - disk
            - disk_profile
            - host
            - network
            - storage_domain
            - system
            - template
            - vm
            - vm_pool
            - vnic_profile
            - mac_pool
        default: vm
        type: str
    user_name:
        description:
            - Username of the user to manage. In most LDAPs it's I(uid) of the user,
              but in Active Directory you must specify I(UPN) of the user.
            - Note that if user does not exist in the system this module will fail,
              you should ensure the user exists by using M(ovirt.ovirt.ovirt_users) module.
        type: str
    group_name:
        description:
            - Name of the group to manage.
            - Note that if group does not exist in the system this module will fail,
               you should ensure the group exists by using M(ovirt.ovirt.ovirt_groups) module.
        type: str
    authz_name:
        description:
            - Authorization provider of the user/group.
        required: true
        aliases: [ domain ]
        type: str
    namespace:
        description:
            - Namespace of the authorization provider, where user/group resides.
        type: str
    quota_name:
        description:
            - Name of the quota to assign permission. Works only with C(object_type) I(data_center).
        type: str
extends_documentation_fragment: ovirt.ovirt.ovirt
'''

EXAMPLES = '''
# Examples don't contain auth parameter for simplicity,
# look at ovirt_auth module to see how to reuse authentication:

- name: Add user user1 from authorization provider example.com-authz
  ovirt.ovirt.ovirt_permission:
    user_name: user1
    authz_name: example.com-authz
    object_type: vm
    object_name: myvm
    role: UserVmManager

- name: Remove permission from user
  ovirt.ovirt.ovirt_permission:
    state: absent
    user_name: user1
    authz_name: example.com-authz
    object_type: cluster
    object_name: mycluster
    role: ClusterAdmin

- name: Assign QuotaConsumer role to user
  ovirt.ovirt.ovirt_permissions:
    state: present
    user_name: user1
    authz_name: example.com-authz
    object_type: data_center
    object_name: mydatacenter
    quota_name: myquota
    role: QuotaConsumer

- name: Assign QuotaConsumer role to group
  ovirt.ovirt.ovirt_permissions:
    state: present
    group_name: group1
    authz_name: example.com-authz
    object_type: data_center
    object_name: mydatacenter
    quota_name: myquota
    role: QuotaConsumer

- ovirt.ovirt.ovirt_permission:
    user_name: user1
    authz_name: example.com-authz
    object_type: mac_pool
    object_name: Default
    role: MacPoolUser
'''

RETURN = '''
id:
    description: ID of the permission which is managed
    returned: On success if permission is found.
    type: str
    sample: 7de90f31-222c-436c-a1ca-7e655bd5b60c
permission:
    description: "Dictionary of all the permission attributes. Permission attributes can be found on your oVirt/RHV instance
                  at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/permission."
    returned: On success if permission is found.
    type: dict
'''

try:
    import ovirtsdk4.types as otypes
except ImportError:
    pass

import traceback

from ansible.module_utils.basic import AnsibleModule
from ansible_collections.ovirt.ovirt.plugins.module_utils.ovirt import (
    BaseModule,
    check_sdk,
    create_connection,
    equal,
    follow_link,
    get_link_name,
    ovirt_full_argument_spec,
    search_by_attributes,
    search_by_name,
    get_id_by_name
)


def _objects_service(connection, object_type):
    if object_type == 'system':
        return connection.system_service()

    return getattr(
        connection.system_service(),
        '%ss_service' % object_type,
        None,
    )()


def _object_service(connection, module):
    object_type = module.params['object_type']
    objects_service = _objects_service(connection, object_type)
    if object_type == 'system':
        return objects_service

    object_id = module.params['object_id']
    if object_id is None:
        sdk_object = search_by_name(objects_service, module.params['object_name'])
        if sdk_object is None:
            raise Exception(
                "'%s' object '%s' was not found." % (
                    module.params['object_type'],
                    module.params['object_name']
                )
            )
        object_id = sdk_object.id

    object_service = objects_service.service(object_id)
    if module.params['quota_name'] and object_type == 'data_center':
        quotas_service = object_service.quotas_service()
        return quotas_service.quota_service(get_id_by_name(quotas_service, module.params['quota_name']))
    return object_service


def _permission(module, permissions_service, connection):
    for permission in permissions_service.list():
        user = follow_link(connection, permission.user)
        if (
            equal(module.params['user_name'], user.principal if user else None) and
            equal(module.params['group_name'], get_link_name(connection, permission.group)) and
            equal(module.params['role'], get_link_name(connection, permission.role))
        ):
            return permission


class PermissionsModule(BaseModule):

    def _user(self):
        user = search_by_attributes(
            self._connection.system_service().users_service(),
            usrname="{name}@{authz_name}".format(
                name=self._module.params['user_name'],
                authz_name=self._module.params['authz_name'],
            ),
        )
        if user is None:
            raise Exception("User '%s' was not found." % self._module.params['user_name'])
        return user

    def _group(self):
        groups = self._connection.system_service().groups_service().list(
            search='name="{name}"'.format(
                name=self._module.params['group_name'],
            )
        )

        # If found more groups, filter them by namespace and authz name:
        # (filtering here, as oVirt/RHV backend doesn't support it)
        if len(groups) > 1:
            groups = [
                g for g in groups if (
                    equal(self._module.params['namespace'], g.namespace) and
                    equal(self._module.params['authz_name'], g.domain.name)
                )
            ]
        if not groups:
            raise Exception("Group '%s' was not found." % self._module.params['group_name'])
        return groups[0]

    def build_entity(self):
        entity = self._group() if self._module.params['group_name'] else self._user()

        return otypes.Permission(
            user=otypes.User(
                id=entity.id
            ) if self._module.params['user_name'] else None,
            group=otypes.Group(
                id=entity.id
            ) if self._module.params['group_name'] else None,
            role=otypes.Role(
                name=self._module.params['role']
            ),
        )


def main():
    argument_spec = ovirt_full_argument_spec(
        state=dict(type='str', default='present', choices=['absent', 'present']),
        role=dict(type='str', default='UserRole'),
        object_type=dict(type='str', default='vm',
                         choices=[
                             'cluster',
                             'cpu_profile',
                             'data_center',
                             'disk',
                             'disk_profile',
                             'host',
                             'network',
                             'storage_domain',
                             'system',
                             'template',
                             'vm',
                             'vm_pool',
                             'vnic_profile',
                             'mac_pool',
                         ]),
        authz_name=dict(type='str', required=True, aliases=['domain']),
        object_id=dict(type='str'),
        object_name=dict(type='str'),
        user_name=dict(type='str'),
        group_name=dict(type='str'),
        namespace=dict(type='str'),
        quota_name=dict(type='str'),
    )
    module = AnsibleModule(
        argument_spec=argument_spec,
        supports_check_mode=True,
    )

    check_sdk(module)

    if (module.params['object_name'] is None and module.params['object_id'] is None) and module.params['object_type'] != 'system':
        module.fail_json(msg='"object_name" or "object_id" is required')

    if module.params['user_name'] is None and module.params['group_name'] is None:
        module.fail_json(msg='"user_name" or "group_name" is required')

    try:
        auth = module.params.pop('auth')
        connection = create_connection(auth)
        permissions_service = _object_service(connection, module).permissions_service()
        permissions_module = PermissionsModule(
            connection=connection,
            module=module,
            service=permissions_service,
        )

        permission = _permission(module, permissions_service, connection)
        state = module.params['state']
        if state == 'present':
            ret = permissions_module.create(entity=permission)
        elif state == 'absent':
            ret = permissions_module.remove(entity=permission)

        module.exit_json(**ret)
    except Exception as e:
        module.fail_json(msg=str(e), exception=traceback.format_exc())
    finally:
        connection.close(logout=auth.get('token') is None)


if __name__ == "__main__":
    main()
