Beispiel #1
0
 def _enforce(self, req, action):
     """Authorize an action against the policy.json."""
     try:
         self.policy.enforce(req.context, action)
     except heat_exception.Forbidden:
         msg = _("Action %s not allowed for user") % action
         raise exception.HeatAccessDeniedError(msg)
     except Exception:
         # We expect policy.enforce to either pass or raise Forbidden
         # however, if anything else happens, we want to raise
         # HeatInternalFailureError, failure to do this results in
         # the user getting a big stacktrace spew as an API response
         msg = _("Error authorizing action %s") % action
         raise exception.HeatInternalFailureError(msg)
Beispiel #2
0
    def _authorize(self, req, auth_uri):
        # Read request signature and access id.
        # If we find X-Auth-User in the headers we ignore a key error
        # here so that we can use both authentication methods.
        # Returning here just means the user didn't supply AWS
        # authentication and we'll let the app try native keystone next.
        logger.info(_("Checking AWS credentials.."))

        signature = self._get_signature(req)
        if not signature:
            if 'X-Auth-User' in req.headers:
                return self.application
            else:
                logger.info(_("No AWS Signature found."))
                raise exception.HeatIncompleteSignatureError()

        access = self._get_access(req)
        if not access:
            if 'X-Auth-User' in req.headers:
                return self.application
            else:
                logger.info(_("No AWSAccessKeyId/Authorization Credential"))
                raise exception.HeatMissingAuthenticationTokenError()

        logger.info(_("AWS credentials found, checking against keystone."))

        if not auth_uri:
            logger.error(_("Ec2Token authorization failed, no auth_uri "
                         "specified in config file"))
            raise exception.HeatInternalFailureError(_('Service '
                                                       'misconfigured'))
        # Make a copy of args for authentication and signature verification.
        auth_params = dict(req.params)
        # 'Signature' param Not part of authentication args
        auth_params.pop('Signature', None)

        # Authenticate the request.
        # AWS v4 authentication requires a hash of the body
        body_hash = hashlib.sha256(req.body).hexdigest()
        creds = {'ec2Credentials': {'access': access,
                                    'signature': signature,
                                    'host': req.host,
                                    'verb': req.method,
                                    'path': req.path,
                                    'params': auth_params,
                                    'headers': req.headers,
                                    'body_hash': body_hash
                                    }}
        creds_json = json.dumps(creds)
        headers = {'Content-Type': 'application/json'}

        keystone_ec2_uri = self._conf_get_keystone_ec2_uri(auth_uri)
        logger.info(_('Authenticating with %s') % keystone_ec2_uri)
        response = requests.post(keystone_ec2_uri, data=creds_json,
                                 headers=headers)
        result = response.json()
        try:
            token_id = result['access']['token']['id']
            tenant = result['access']['token']['tenant']['name']
            tenant_id = result['access']['token']['tenant']['id']
            logger.info(_("AWS authentication successful."))
        except (AttributeError, KeyError):
            logger.info(_("AWS authentication failure."))
            # Try to extract the reason for failure so we can return the
            # appropriate AWS error via raising an exception
            try:
                reason = result['error']['message']
            except KeyError:
                reason = None

            if reason == "EC2 access key not found.":
                raise exception.HeatInvalidClientTokenIdError()
            elif reason == "EC2 signature not supplied.":
                raise exception.HeatSignatureError()
            else:
                raise exception.HeatAccessDeniedError()

        # Authenticated!
        ec2_creds = {'ec2Credentials': {'access': access,
                                        'signature': signature}}
        req.headers['X-Auth-EC2-Creds'] = json.dumps(ec2_creds)
        req.headers['X-Auth-Token'] = token_id
        req.headers['X-Tenant-Name'] = tenant
        req.headers['X-Tenant-Id'] = tenant_id
        req.headers['X-Auth-URL'] = auth_uri

        metadata = result['access'].get('metadata', {})
        roles = metadata.get('roles', [])
        req.headers['X-Roles'] = ','.join(roles)

        return self.application
Beispiel #3
0
    def create_or_update(self, req, action=None):
        """
        Implements CreateStack and UpdateStack API actions.
        Create or update stack as defined in template file.
        """
        def extract_args(params):
            """
            Extract request parameters/arguments and reformat them to match
            the engine API.  FIXME: we currently only support a subset of
            the AWS defined parameters (both here and in the engine)
            """
            # TODO(shardy) : Capabilities, NotificationARNs
            keymap = {'TimeoutInMinutes': engine_api.PARAM_TIMEOUT,
                      'DisableRollback': engine_api.PARAM_DISABLE_ROLLBACK}

            if 'DisableRollback' in params and 'OnFailure' in params:
                msg = _('DisableRollback and OnFailure '
                        'may not be used together')
                raise exception.HeatInvalidParameterCombinationError(
                    detail=msg)

            result = {}
            for k in keymap:
                if k in params:
                    result[keymap[k]] = params[k]

            if 'OnFailure' in params:
                value = params['OnFailure']
                if value == 'DO_NOTHING':
                    result[engine_api.PARAM_DISABLE_ROLLBACK] = 'true'
                elif value in ('ROLLBACK', 'DELETE'):
                    result[engine_api.PARAM_DISABLE_ROLLBACK] = 'false'

            return result

        if action not in self.CREATE_OR_UPDATE_ACTION:
            msg = _("Unexpected action %(action)s") % ({'action': action})
            # This should not happen, so return HeatInternalFailureError
            return exception.HeatInternalFailureError(detail=msg)

        engine_action = {self.CREATE_STACK: self.engine_rpcapi.create_stack,
                         self.UPDATE_STACK: self.engine_rpcapi.update_stack}

        con = req.context

        # Extract the stack input parameters
        stack_parms = self._extract_user_params(req.params)

        # Extract any additional arguments ("Request Parameters")
        create_args = extract_args(req.params)

        try:
            templ = self._get_template(req)
        except socket.gaierror:
            msg = _('Invalid Template URL')
            return exception.HeatInvalidParameterValueError(detail=msg)

        if templ is None:
            msg = _("TemplateBody or TemplateUrl were not given.")
            return exception.HeatMissingParameterError(detail=msg)

        try:
            stack = template_format.parse(templ)
        except ValueError:
            msg = _("The Template must be a JSON or YAML document.")
            return exception.HeatInvalidParameterValueError(detail=msg)

        args = {'template': stack,
                'params': stack_parms,
                'files': {},
                'args': create_args}
        try:
            stack_name = req.params['StackName']
            if action == self.CREATE_STACK:
                args['stack_name'] = stack_name
            else:
                args['stack_identity'] = self._get_identity(con, stack_name)

            result = engine_action[action](con, **args)
        except Exception as ex:
            return exception.map_remote_error(ex)

        try:
            identity = identifier.HeatIdentifier(**result)
        except (ValueError, TypeError):
            response = result
        else:
            response = {'StackId': identity.arn()}

        return api_utils.format_response(action, response)
Beispiel #4
0
    def create_or_update(self, req, action=None):
        """
        Implements CreateStack and UpdateStack API actions
        Create or update stack as defined in template file
        """
        def extract_args(params):
            """
            Extract request parameters/arguments and reformat them to match
            the engine API.  FIXME: we currently only support a subset of
            the AWS defined parameters (both here and in the engine)
            """
            # TODO : Capabilities, DisableRollback, NotificationARNs
            keymap = {'TimeoutInMinutes': engine_api.PARAM_TIMEOUT, }

            result = {}
            for k in keymap:
                if k in req.params:
                    result[keymap[k]] = params[k]

            return result

        if action not in self.CREATE_OR_UPDATE_ACTION:
            msg = _("Unexpected action %s" % action)
            # This should not happen, so return HeatInternalFailureError
            return exception.HeatInternalFailureError(detail=msg)

        engine_action = {self.CREATE_STACK: self.engine_rpcapi.create_stack,
                         self.UPDATE_STACK: self.engine_rpcapi.update_stack}

        con = req.context

        # Extract the stack input parameters
        stack_parms = self._extract_user_params(req.params)

        # Extract any additional arguments ("Request Parameters")
        create_args = extract_args(req.params)

        try:
            templ = self._get_template(req)
        except socket.gaierror:
            msg = _('Invalid Template URL')
            return exception.HeatInvalidParameterValueError(detail=msg)

        if templ is None:
            msg = _("TemplateBody or TemplateUrl were not given.")
            return exception.HeatMissingParameterError(detail=msg)

        try:
            stack = json.loads(templ)
        except ValueError:
            msg = _("The Template must be a JSON document.")
            return exception.HeatInvalidParameterValueError(detail=msg)

        args = {'template': stack,
                'params': stack_parms,
                'args': create_args}
        try:
            stack_name = req.params['StackName']
            if action == self.CREATE_STACK:
                args['stack_name'] = stack_name
            else:
                args['stack_identity'] = self._get_identity(con, stack_name)

            result = engine_action[action](con, **args)
        except rpc_common.RemoteError as ex:
            return exception.map_remote_error(ex)

        try:
            identity = identifier.HeatIdentifier(**result)
        except (ValueError, TypeError):
            response = result
        else:
            response = {'StackId': identity.arn()}

        return api_utils.format_response(action, response)