Esempio n. 1
0
    def test_registry_invalid_token_exception_handling(self):
        self.image_controller = acontroller()
        request = webob.Request.blank('/images')
        request.method = 'GET'
        request.context = context.RequestContext()

        with patch.object(rapi, 'get_images_detail') as mock_detail:
            mock_detail.side_effect = exception.NotAuthenticated()
            self.assertRaises(webob.exc.HTTPUnauthorized,
                              self.image_controller.detail, request)
Esempio n. 2
0
    def _query(self, location, method, depth=0):
        if depth > MAX_REDIRECTS:
            msg = ("The HTTP URL exceeded %(max_redirects)s maximum "
                   "redirects.", {
                       'max_redirects': MAX_REDIRECTS
                   })
            LOG.debug(msg)
            raise exception.MaxRedirectsExceeded(redirects=MAX_REDIRECTS)
        loc = location.store_location
        cookie = self._build_vim_cookie_header(
            self._session.vim.client.options.transport.cookiejar)
        headers = {'Cookie': cookie}
        try:
            conn = self._get_http_conn(method, loc, headers)
            resp = conn.getresponse()
        except Exception:
            with excutils.save_and_reraise_exception():
                LOG.exception(_LE('Failed to access image %(image)s content.'),
                              {'image': location.image_id})
        if resp.status >= 400:
            if resp.status == httplib.UNAUTHORIZED:
                self._create_session()
                raise exception.NotAuthenticated()
            if resp.status == httplib.NOT_FOUND:
                msg = 'VMware datastore could not find image at URI.'
                LOG.debug(msg)
                raise exception.NotFound(msg)
            reason = (_('HTTP request returned a %(status)s status code.') % {
                'status': resp.status
            })
            LOG.info(reason)
            raise exception.BadStoreUri(message=reason)
        location_header = resp.getheader('location')
        if location_header:
            if resp.status not in (301, 302):
                reason = (_("The HTTP URL %(path)s attempted to redirect "
                            "with an invalid %(status)s status code.") % {
                                'path': loc.path,
                                'status': resp.status
                            })
                LOG.info(reason)
                raise exception.BadStoreUri(message=reason)
            location_class = glance.store.location.Location
            new_loc = location_class(location.store_name,
                                     location.store_location.__class__,
                                     uri=location_header,
                                     image_id=location.image_id,
                                     store_specs=location.store_specs)
            return self._query(new_loc, method, depth + 1)
        content_length = int(resp.getheader('content-length', 0))

        return (conn, resp, content_length)
Esempio n. 3
0
    def process_request(self, req):
        """
        Extract any authentication information in the request and
        construct an appropriate context from it.

        A few scenarios exist:

        1. If X-Auth-Token is passed in, then consult TENANT and ROLE headers
           to determine permissions.

        2. An X-Auth-Token was passed in, but the Identity-Status is not
           confirmed. For now, just raising a NotAuthenticated exception.

        3. X-Auth-Token is omitted. If we were using Keystone, then the
           tokenauth middleware would have rejected the request, so we must be
           using NoAuth. In that case, assume that is_admin=True.
        """
        auth_tok = req.headers.get('X-Auth-Token',
                                   req.headers.get('X-Storage-Token'))
        if auth_tok:
            if req.headers.get('X-Identity-Status') == 'Confirmed':
                # 1. Auth-token is passed, check other headers
                user = req.headers.get('X-User-Id')
                tenant = req.headers.get('X-Tenant-Id')
                roles = [
                    r.strip()
                    for r in req.headers.get('X-Roles', '').split(',')
                ]
                is_admin = self.conf.admin_role in roles
            else:
                # 2. Indentity-Status not confirmed
                # FIXME(sirp): not sure what the correct behavior in this case
                # is; just raising NotAuthenticated for now
                raise exception.NotAuthenticated()
        else:
            # 3. Auth-token is ommited, assume NoAuth
            user = None
            tenant = None
            roles = []
            is_admin = True

        req.context = self.make_context(auth_tok=auth_tok,
                                        user=user,
                                        tenant=tenant,
                                        roles=roles,
                                        is_admin=is_admin)
Esempio n. 4
0
 def test_upload_with_token_refresh(self, mock_refresher):
     mock_refresher.return_value = mock.MagicMock()
     mocked_save = mock.Mock()
     mocked_save.side_effect = [lambda *a: None,
                                exception.NotAuthenticated(),
                                lambda *a: None]
     request = unit_test_utils.get_fake_request()
     request.environ['keystone.token_info'] = {
         'token': {
             'roles': [{'name': 'member'}]
         }
     }
     image = FakeImage('abcd', owner='tenant1')
     self.image_repo.result = image
     self.image_repo.save = mocked_save
     self.controller.upload(request, unit_test_utils.UUID2, 'YYYY', 4)
     self.assertEqual('YYYY', image.data)
     self.assertEqual(4, image.size)
     self.assertEqual(3, mocked_save.call_count)
Esempio n. 5
0
    def _v2_auth(self, token_url):

        creds = self.creds

        creds = {
            "auth": {
                "tenantName": creds['tenant'],
                "passwordCredentials": {
                    "username": creds['username'],
                    "password": creds['password']
                }
            }
        }

        headers = {}
        headers['Content-Type'] = 'application/json'
        req_body = jsonutils.dumps(creds)

        resp, resp_body = self._do_request(token_url,
                                           'POST',
                                           headers=headers,
                                           body=req_body)

        if resp.status == 200:
            resp_auth = jsonutils.loads(resp_body)['access']
            creds_region = self.creds.get('region')
            if self.configure_via_auth:
                endpoint = get_endpoint(resp_auth['serviceCatalog'],
                                        endpoint_region=creds_region)
                self.management_url = endpoint
            self.auth_token = resp_auth['token']['id']
        elif resp.status == 305:
            raise exception.RedirectException(resp['location'])
        elif resp.status == 400:
            raise exception.AuthBadRequest(url=token_url)
        elif resp.status == 401:
            raise exception.NotAuthenticated()
        elif resp.status == 404:
            raise exception.AuthUrlNotFound(url=token_url)
        else:
            raise Exception(_('Unexpected response: %s') % resp.status)
Esempio n. 6
0
    def _v1_auth(self, token_url):
        creds = self.creds

        headers = {
            'X-Auth-User': creds['username'],
            'X-Auth-Key': creds['password']
        }

        tenant = creds.get('tenant')
        if tenant:
            headers['X-Auth-Tenant'] = tenant

        resp, resp_body = self._do_request(token_url, 'GET', headers=headers)

        def _management_url(self, resp):
            for url_header in ('x-image-management-url',
                               'x-server-management-url',
                               'x-glance'):
                try:
                    return resp[url_header]
                except KeyError as e:
                    not_found = e
            raise not_found

        if resp.status in (200, 204):
            try:
                if self.configure_via_auth:
                    self.management_url = _management_url(self, resp)
                self.auth_token = resp['x-auth-token']
            except KeyError:
                raise exception.AuthorizationFailure()
        elif resp.status == 305:
            raise exception.AuthorizationRedirect(uri=resp['location'])
        elif resp.status == 400:
            raise exception.AuthBadRequest(url=token_url)
        elif resp.status == 401:
            raise exception.NotAuthenticated()
        elif resp.status == 404:
            raise exception.AuthUrlNotFound(url=token_url)
        else:
            raise Exception(_('Unexpected response: %s') % resp.status)
Esempio n. 7
0
    def _do_request(self, method, url, body, headers):
        """
        Connects to the server and issues a request.  Handles converting
        any returned HTTP error status codes to OpenStack/Glance exceptions
        and closing the server connection. Returns the result data, or
        raises an appropriate exception.

        :param method: HTTP method ("GET", "POST", "PUT", etc...)
        :param url: urlparse.ParsedResult object with URL information
        :param body: data to send (as string, filelike or iterable),
                     or None (default)
        :param headers: mapping of key/value pairs to add as headers

        :note

        If the body param has a read attribute, and method is either
        POST or PUT, this method will automatically conduct a chunked-transfer
        encoding and use the body as a file object or iterable, transferring
        chunks of data using the connection's send() method. This allows large
        objects to be transferred efficiently without buffering the entire
        body in memory.
        """
        if url.query:
            path = url.path + "?" + url.query
        else:
            path = url.path

        try:
            connection_type = self.get_connection_type()
            headers = self._encode_headers(headers or {})

            if 'x-auth-token' not in headers and self.auth_tok:
                headers['x-auth-token'] = self.auth_tok

            c = connection_type(url.hostname, url.port, **self.connect_kwargs)

            def _pushing(method):
                return method.lower() in ('post', 'put')

            def _simple(body):
                return body is None or isinstance(body, six.string_types)

            def _filelike(body):
                return hasattr(body, 'read')

            def _sendbody(connection, iter):
                connection.endheaders()
                for sent in iter:
                    # iterator has done the heavy lifting
                    pass

            def _chunkbody(connection, iter):
                connection.putheader('Transfer-Encoding', 'chunked')
                connection.endheaders()
                for chunk in iter:
                    connection.send('%x\r\n%s\r\n' % (len(chunk), chunk))
                connection.send('0\r\n\r\n')

            # Do a simple request or a chunked request, depending
            # on whether the body param is file-like or iterable and
            # the method is PUT or POST
            #
            if not _pushing(method) or _simple(body):
                # Simple request...
                c.request(method, path, body, headers)
            elif _filelike(body) or self._iterable(body):
                c.putrequest(method, path)

                use_sendfile = self._sendable(body)

                # According to HTTP/1.1, Content-Length and Transfer-Encoding
                # conflict.
                for header, value in headers.items():
                    if use_sendfile or header.lower() != 'content-length':
                        c.putheader(header, str(value))

                iter = self.image_iterator(c, headers, body)

                if use_sendfile:
                    # send actual file without copying into userspace
                    _sendbody(c, iter)
                else:
                    # otherwise iterate and chunk
                    _chunkbody(c, iter)
            else:
                raise TypeError('Unsupported image type: %s' % body.__class__)

            res = c.getresponse()

            def _retry(res):
                return res.getheader('Retry-After')

            status_code = self.get_status_code(res)
            if status_code in self.OK_RESPONSE_CODES:
                return res
            elif status_code in self.REDIRECT_RESPONSE_CODES:
                raise exception.RedirectException(res.getheader('Location'))
            elif status_code == httplib.UNAUTHORIZED:
                raise exception.NotAuthenticated(res.read())
            elif status_code == httplib.FORBIDDEN:
                raise exception.Forbidden(res.read())
            elif status_code == httplib.NOT_FOUND:
                raise exception.NotFound(res.read())
            elif status_code == httplib.CONFLICT:
                raise exception.Duplicate(res.read())
            elif status_code == httplib.BAD_REQUEST:
                raise exception.Invalid(res.read())
            elif status_code == httplib.MULTIPLE_CHOICES:
                raise exception.MultipleChoices(body=res.read())
            elif status_code == httplib.REQUEST_ENTITY_TOO_LARGE:
                raise exception.LimitExceeded(retry=_retry(res),
                                              body=res.read())
            elif status_code == httplib.INTERNAL_SERVER_ERROR:
                raise exception.ServerError()
            elif status_code == httplib.SERVICE_UNAVAILABLE:
                raise exception.ServiceUnavailable(retry=_retry(res))
            else:
                raise exception.UnexpectedStatus(status=status_code,
                                                 body=res.read())

        except (socket.error, IOError) as e:
            raise exception.ClientConnectionError(e)
Esempio n. 8
0
    def add(self, image_id, image_file, image_size):
        """Stores an image file with supplied identifier to the backend
        storage system and returns a tuple containing information
        about the stored image.

        :param image_id: The opaque image identifier
        :param image_file: The image data to write, as a file-like object
        :param image_size: The size of the image data to write, in bytes
        :retval tuple of URL in backing store, bytes written, checksum
                and a dictionary with storage system specific information
        :raises `glance.common.exception.Duplicate` if the image already
                existed
                `glance.common.exception.UnexpectedStatus` if the upload
                request returned an unexpected status. The expected responses
                are 201 Created and 200 OK.
        """
        checksum = hashlib.md5()
        image_file = _Reader(image_file, checksum)
        loc = StoreLocation({
            'scheme': self.scheme,
            'server_host': self.server_host,
            'image_dir': self.store_image_dir,
            'datacenter_path': self.datacenter_path,
            'datastore_name': self.datastore_name,
            'image_id': image_id
        })
        cookie = self._build_vim_cookie_header(
            self._session.vim.client.options.transport.cookiejar)
        headers = {
            'Connection': 'Keep-Alive',
            'Cookie': cookie,
            'Transfer-Encoding': 'chunked'
        }
        try:
            conn = self._get_http_conn('PUT', loc, headers, content=image_file)
            res = conn.getresponse()
        except Exception:
            with excutils.save_and_reraise_exception():
                LOG.exception(
                    _LE('Failed to upload content of image '
                        '%(image)s'), {'image': image_id})

        if res.status == httplib.UNAUTHORIZED:
            self._create_session()
            image_file.rewind()
            raise exception.NotAuthenticated()

        if res.status == httplib.CONFLICT:
            raise exception.Duplicate(
                _("Image file %(image_id)s already "
                  "exists!") % {'image_id': image_id})

        if res.status not in (httplib.CREATED, httplib.OK):
            msg = (_LE('Failed to upload content of image %(image)s') % {
                'image': image_id
            })
            LOG.error(msg)
            raise exception.UnexpectedStatus(status=res.status,
                                             body=res.read())

        return (loc.get_uri(), image_file.size, checksum.hexdigest(), {})
Esempio n. 9
0
 def side_effect(image, from_state=None):
     if from_state == 'saving':
         raise exception.NotAuthenticated()
Esempio n. 10
0
    def _v2_auth(self, token_url):
        def get_endpoint(service_catalog):
            """
            Select an endpoint from the service catalog

            We search the full service catalog for services
            matching both type and region. If the client
            supplied no region then any 'image' endpoint
            is considered a match. There must be one -- and
            only one -- successful match in the catalog,
            otherwise we will raise an exception.
            """
            # FIXME(sirp): for now just use the public url.
            endpoint = None
            region = self.creds.get('region')
            for service in service_catalog:
                try:
                    service_type = service['type']
                except KeyError:
                    msg = _('Encountered service with no "type": %s' % service)
                    logger.warn(msg)
                    continue

                if service_type == 'image':
                    for ep in service['endpoints']:
                        if region is None or region == ep['region']:
                            if endpoint is not None:
                                # This is a second match, abort
                                raise exception.RegionAmbiguity(region=region)
                            endpoint = ep
            if endpoint is None:
                raise exception.NoServiceEndpoint()
            return endpoint['publicURL']

        creds = self.creds

        creds = {
            "auth": {
                "tenantName": creds['tenant'],
                "passwordCredentials": {
                    "username": creds['username'],
                    "password": creds['password']
                    }
                }
            }

        headers = {}
        headers['Content-Type'] = 'application/json'
        req_body = json.dumps(creds)

        resp, resp_body = self._do_request(
                token_url, 'POST', headers=headers, body=req_body)

        if resp.status == 200:
            resp_auth = json.loads(resp_body)['access']
            self.management_url = get_endpoint(resp_auth['serviceCatalog'])
            self.auth_token = resp_auth['token']['id']
        elif resp.status == 305:
            raise exception.RedirectException(resp['location'])
        elif resp.status == 400:
            raise exception.AuthBadRequest(url=token_url)
        elif resp.status == 401:
            raise exception.NotAuthenticated()
        elif resp.status == 404:
            raise exception.AuthUrlNotFound(url=token_url)
        else:
            raise Exception(_('Unexpected response: %s') % resp.status)