Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
Size: Mime:
# vim: tabstop=4 shiftwidth=4 softtabstop=4

# Copyright (c) 2013 TrilioData, Inc.
# All Rights Reserved.

"""
Handles all requests relating to keymanager + barbican.
"""

import copy
import sys
import json
import requests
from functools import wraps
import binascii

from oslo_config import cfg

from barbicanclient import client as barbican_client
from barbicanclient import exceptions as barbican_exception
from keystoneclient import exceptions as ks_exceptions

from workloadmgr.db import base
from workloadmgr import workloads as workloadAPI
from workloadmgr.vault import vault
from workloadmgr.db.workloadmgrdb import WorkloadMgrDB
from workloadmgr import exception
from workloadmgr.common import context as wlm_context
from workloadmgr.common.clients import client_plugin
from workloadmgr.openstack.common import log as logging

from workloadmgr.common import clients

# TODO: Try using common params from config file.

barbican_opts = [
    cfg.StrOpt('barbican_catalog_info',
               default='key-manager:barbican:publicURL',
               help='Info to match when looking for cinder in the service '
                    'catalog. Format is : separated values of the form: '
                    '<service_type>:<service_name>:<endpoint_type>'),
    cfg.StrOpt('barbican_production_endpoint_template',
               default='http://localhost:9311/v1/%(project_id)s',  # None,
               help='Override service catalog lookup with template for barbican '
                    'endpoint e.g. http://localhost:9311/v1/%(project_id)s'),
    cfg.StrOpt('os_region_name',
               default=None,
               help='region name of this node'),
    cfg.IntOpt('barbican_http_retries',
               default=3,
               help='Number of barbicanclient retries on failed http calls'),
    cfg.BoolOpt('barbican_api_insecure',
                default=False,
                help='Allow to perform insecure SSL requests to barbican'),
    cfg.BoolOpt('barbican_cross_az_attach',
                default=True,
                help='Allow attach between instance and volume in different '
                     'availability zones.'),
]

CONF = cfg.CONF
CONF.register_opts(barbican_opts)

LOG = logging.getLogger(__name__)


def barbicanclient(context, refresh_token=False):
    if hasattr(context, 'user_domain_id'):
        if context.user_domain_id is None:
            user_domain_id = 'default'
        else:
            user_domain_id = context.user_domain_id
    elif hasattr(context, 'user_domain'):
        if context.user_domain is None:
            user_domain_id = 'default'
        else:
            user_domain_id = context.user_domain
    else:
        user_domain_id = 'default'

    workload_api = workloadAPI.API()
    trust = workload_api.trust_list(context, get_hidden=True, is_cloud_admin=False)

    trust_details = None
    if trust:
        trust_details = workload_api.trust_show(context,trust[0].name)

    if not trust_details:
        trust = workload_api.trust_create(context, vault.CONF.trustee_role)

    if len(trust):
        trust_id = trust[0].value

        if refresh_token:
            context = wlm_context.RequestContext(
                username=CONF.keystone_authtoken.admin_user,
                password=CONF.keystone_authtoken.admin_password,
                trust_id=trust_id,
                tenant_id=context.project_id,
                trustor_user_id=context.user_id,
                user_domain_id=CONF.triliovault_user_domain_id,
                is_admin=False)
        else:
            context = wlm_context.RequestContext(
                trustor_user_id=context.user_id,
                project_id=context.project_id,
                auth_token=context.auth_token,
                trust_id=trust_id,
                user_domain_id=user_domain_id,
                is_admin=False)

        clients.initialise()
        client_plugin = clients.Clients(context)
        barbicanclient = client_plugin.client("barbican")
        barbicanclient.client_plugin = barbicanclient

    return barbicanclient


class BarbicanRestClient:
    """
        REST Client Implementation of Barbican.
    """
    secret_uri = '/v1/secrets/'

    def __init__(self, context, auth, management_url):
        self.context = context
        self.auth_token = auth.auth_token
        self.management_url = barbicanclient(context[1]).client.management_url + \
                              BarbicanRestClient.secret_uri
        self.headers = self.to_dict(self.auth_token)
        self.verify = CONF.clients.get('cafile', False)

    def to_dict(self, auth_keystone_token):
        headers = {
            "X-Auth-Token": auth_keystone_token,
            "Content-Type": "application/json; charset=utf-8"
        }
        return headers

    def get(self, api, retry=CONF.clients.get('client_retry_limit', 3)):
        res = None
        try:
            while retry:
                res = requests.get(
                    self.management_url + api,
                    headers=self.headers,
                    verify=self.verify)
                if 200 == res.status_code:
                    break
                retry -= 1
            return res.text

        except Exception as ex:
            LOG.exception(ex)
            raise ex

    def post(self):
        pass

    def put(self, api, metadata, retry=CONF.clients.get('client_retry_limit', 3)):
        res = None
        while retry:
            res = requests.put(
                self.management_url + api,
                headers=self.headers,
                data=json.dumps(metadata),
                verify=self.verify)

            if 201 == res.status_code:
                return True
            retry -= 1
        return False


def exception_handler(ignore_exception=False, refresh_token=True, http=False):
    def exception_handler_decorator(func):
        @wraps(func)
        def func_wrapper(*args, **argv):
            try:
                try:
                    if not http:
                        client = barbicanclient(args[1])
                        argv.update({'client': client})
                        return func(*args, **argv)
                    else:
                        client = BarbicanRestClient(args, args[1], args[2])
                        argv.update({'client': client})
                        return func(*args, **argv)
                except Exception as ex:
                    if ignore_exception is True:
                        LOG.exception(ex)
                    else:
                        raise ex
            except barbican_exception.HTTPAuthError as autherror:
                LOG.error(autherror)
                raise autherror
            except barbican_exception.HTTPClientError as clienterror:
                LOG.error(clienterror)
                raise clienterror
            except ks_exceptions.EndpointNotFound as ex:
                LOG.error(ex)
                raise ex
            except exception.MissingCredentialError as ex:
                LOG.error(ex)
                raise ex
            except Exception as ex:
                LOG.exception(ex)

        return func_wrapper

    return exception_handler_decorator


class API(base.Base):
    """API for interacting with the key manager."""

    @exception_handler()
    def get_payload_from_secret_href(self, context, secret_href, **kwargs):
        client = kwargs['client']
        secret_obj = client.secrets.get(secret_href)
        res = secret_obj.payload
        try:
            ret = res.decode('utf-8') if isinstance(res, bytes) else res
        except UnicodeDecodeError:
            res = binascii.hexlify(res)
            ret = res.decode('utf-8') if isinstance(res, bytes) else res
        except:
            raise
        return ret

    @exception_handler(http=True)
    def get_secret_metadata(self, context, secret_href, **kwargs):
        client = kwargs['client']
        status = client.get(secret_href + '/metadata')
        return status

    @exception_handler(http=True)
    def update_secret_metadata(self, context, secret_href, metadata, **kwargs):
        client = kwargs['client']
        status = client.put(secret_href + '/metadata', metadata)
        if status:
            return status
        else:
            raise Exception('Could not update metadata of secret.')

    @exception_handler()
    def create_secret(self, context, name=None, payload=None, algorithm=None, bit_length=None, mode=None,
                      secret_type=None, expiration=None):
        client = kwargs['client']
        return client.secrets.create()

    @exception_handler()
    def list_all_secret(self, context, limit=100, offset=0, name=None, algorithm=None, mode=None, **kwargs):
        """ searches for all secrets for that user and project with limit and offset. """
        list_secrets = []
        client = kwargs['client']
        while True:
            res = client.secrets.list(limit=limit, offset=offset)
            list_secrets += res
            if len(res) < 100:
                return list_secrets
            offset += limit

    @exception_handler()
    def get_secret(self, context, secret_id=None, secret_href=None, **kwargs):
        client = kwargs['client']
        if not secret_href:
            endpoint = client.client.get_endpoint()
            secret_href = endpoint + 'secrets/%s' % (secret_id)
        return client.secrets.get(secret_href)

    @exception_handler()
    def get_barbican_endpoint(self, context, **kwargs):
        client = kwargs['client']
        return client.client.get_endpoint()