Example #1
0
    def __call__(self, req):
        """Ensures valid path and tenant

        Handle incoming requests by checking tenant info from the
        headers and url ({tenant_id} url attribute), if using v1 or v1.1
        APIs. If using the v2 API, this function just makes sure that
        keystonemiddleware has populated the WSGI environment.

        Pass request downstream on success.
        Reject request if tenant_id from headers is not equal to the
        tenant_id from url in the case of v1.
        """
        path = req.environ['PATH_INFO']
        if path != '/':
            token_tenant = req.environ.get("HTTP_X_TENANT_ID")
            if not token_tenant:
                LOG.warning("Can't get tenant_id from env")
                raise ex.HTTPServiceUnavailable()

            try:
                if path.startswith('/v2'):
                    version, rest = strutils.split_path(path, 2, 2, True)
                else:
                    version, requested_tenant, rest = strutils.split_path(
                        path, 3, 3, True)
            except ValueError:
                LOG.warning("Incorrect path: {path}".format(path=path))
                raise ex.HTTPNotFound(_("Incorrect path"))

            if path.startswith('/v1'):
                if token_tenant != requested_tenant:
                    LOG.debug("Unauthorized: token tenant != requested tenant")
                    raise ex.HTTPUnauthorized(
                        _('Token tenant != requested tenant'))
        return self.application
Example #2
0
    def __call__(self, req):
        """Ensures that the requested and token tenants match

        Handle incoming requests by checking tenant info from the
        headers and url ({tenant_id} url attribute), if using v1 or v1.1
        APIs. If using the v2 API, this function will check the token
        tenant and the requested tenant in the headers.

        Pass request downstream on success.
        Reject request if tenant_id from headers is not equal to the
        tenant_id from url or v2 project header.
        """
        path = req.environ['PATH_INFO']
        if path != '/':
            token_tenant = req.environ.get("HTTP_X_TENANT_ID")
            if not token_tenant:
                LOG.warning(_LW("Can't get tenant_id from env"))
                raise ex.HTTPServiceUnavailable()

            try:
                if path.startswith('/v2'):
                    version, rest = strutils.split_path(path, 2, 2, True)
                    requested_tenant = req.headers.get('OpenStack-Project-ID')
                else:
                    version, requested_tenant, rest = strutils.split_path(
                        path, 3, 3, True)
            except ValueError:
                LOG.warning(_LW("Incorrect path: {path}").format(path=path))
                raise ex.HTTPNotFound(_("Incorrect path"))

            if token_tenant != requested_tenant:
                LOG.debug("Unauthorized: token tenant != requested tenant")
                raise ex.HTTPUnauthorized(
                    _('Token tenant != requested tenant'))
        return self.application
Example #3
0
 def test_split_path_invalid_path(self):
     try:
         strutils.split_path('o\nn e', 2)
     except ValueError as err:
         self.assertEqual(str(err), 'Invalid path: o%0An%20e')
     try:
         strutils.split_path('o\nn e', 2, 3, True)
     except ValueError as err:
         self.assertEqual(str(err), 'Invalid path: o%0An%20e')
Example #4
0
 def test_split_path_invalid_path(self):
     try:
         strutils.split_path('o\nn e', 2)
     except ValueError as err:
         self.assertEqual(str(err), 'Invalid path: o%0An%20e')
     try:
         strutils.split_path('o\nn e', 2, 3, True)
     except ValueError as err:
         self.assertEqual(str(err), 'Invalid path: o%0An%20e')
Example #5
0
    def __call__(self, req):
        """Ensures that tenants in url and token are equal.

        Handle incoming request by checking tenant info prom the headers and
        url ({tenant_id} url attribute).

        Pass request downstream on success.
        Reject request if tenant_id from headers not equals to tenant_id from
        url.
        """
        token_tenant = req.environ.get("HTTP_X_TENANT_ID")
        if not token_tenant:
            LOG.warning("Can't get tenant_id from env")
            raise ex.HTTPServiceUnavailable()

        path = req.environ['PATH_INFO']
        if path != '/':
            try:
                version, url_tenant, rest = strutils.split_path(path, 3, 3,
                                                                True)
            except ValueError:
                LOG.warning("Incorrect path: {path}".format(path=path))
                raise ex.HTTPNotFound(_("Incorrect path"))

            if token_tenant != url_tenant:
                LOG.debug("Unauthorized: token tenant != requested tenant")
                raise ex.HTTPUnauthorized(
                    _('Token tenant != requested tenant'))
        return self.application
Example #6
0
    def __call__(self, req):
        """Ensures that tenants in url and token are equal.

        Handle incoming request by checking tenant info prom the headers and
        url ({tenant_id} url attribute).

        Pass request downstream on success.
        Reject request if tenant_id from headers not equals to tenant_id from
        url.
        """
        token_tenant = req.environ.get("HTTP_X_TENANT_ID")
        if not token_tenant:
            LOG.warning("Can't get tenant_id from env")
            raise ex.HTTPServiceUnavailable()

        path = req.environ['PATH_INFO']
        if path != '/':
            try:
                version, possibly_url_tenant, rest = (
                    strutils.split_path(path, 2, 3, True)
                )
            except ValueError:
                LOG.warning("Incorrect path: {path}".format(path=path))
                raise ex.HTTPNotFound(_("Incorrect path"))

            if uuidutils.is_uuid_like(possibly_url_tenant):
                url_tenant = possibly_url_tenant
                if token_tenant != url_tenant:
                    LOG.debug("Unauthorized: token tenant != requested tenant")
                    raise ex.HTTPUnauthorized(
                        _('Token tenant != requested tenant'))
        return self.application
Example #7
0
 def test_split_path_success(self):
     self.assertEqual(strutils.split_path('/a'), ['a'])
     self.assertEqual(strutils.split_path('/a/'), ['a'])
     self.assertEqual(strutils.split_path('/a/c', 2), ['a', 'c'])
     self.assertEqual(strutils.split_path('/a/c/o', 3), ['a', 'c', 'o'])
     self.assertEqual(strutils.split_path('/a/c/o/r', 3, 3, True),
                      ['a', 'c', 'o/r'])
     self.assertEqual(strutils.split_path('/a/c', 2, 3, True),
                      ['a', 'c', None])
     self.assertEqual(strutils.split_path('/a/c/', 2), ['a', 'c'])
     self.assertEqual(strutils.split_path('/a/c/', 2, 3), ['a', 'c', ''])
Example #8
0
 def test_split_path_success(self):
     self.assertEqual(strutils.split_path('/a'), ['a'])
     self.assertEqual(strutils.split_path('/a/'), ['a'])
     self.assertEqual(strutils.split_path('/a/c', 2), ['a', 'c'])
     self.assertEqual(strutils.split_path('/a/c/o', 3), ['a', 'c', 'o'])
     self.assertEqual(strutils.split_path('/a/c/o/r', 3, 3, True),
                      ['a', 'c', 'o/r'])
     self.assertEqual(strutils.split_path('/a/c', 2, 3, True),
                      ['a', 'c', None])
     self.assertEqual(strutils.split_path('/a/c/', 2), ['a', 'c'])
     self.assertEqual(strutils.split_path('/a/c/', 2, 3), ['a', 'c', ''])
Example #9
0
    def __call__(self, req):
        """Ensures that the requested and token tenants match

        Handle incoming requests by checking tenant info from the
        headers and url ({tenant_id} url attribute), if using v1 or v1.1
        APIs. If using the v2 API, this function will check the token
        tenant and the requested tenent in the headers.

        Pass request downstream on success.
        Reject request if tenant_id from headers is not equal to the
        tenant_id from url or v2 project header.
        """
        path = req.environ['PATH_INFO']
        if path != '/':
            token_tenant = req.environ.get("HTTP_X_TENANT_ID")
            if not token_tenant:
                LOG.warning(_LW("Can't get tenant_id from env"))
                raise ex.HTTPServiceUnavailable()

            try:
                if path.startswith('/v2'):
                    version, rest = strutils.split_path(path, 2, 2, True)
                    requested_tenant = req.headers.get('OpenStack-Project-ID')
                else:

                    version, requested_tenant, rest = strutils.split_path(
                        path, 3, 3, True)
            except ValueError:
                LOG.warning(_LW("Incorrect path: {path}").format(path=path))
                raise ex.HTTPNotFound(_("Incorrect path"))

            if token_tenant != requested_tenant:
                LOG.debug("Unauthorized: token tenant != requested tenant")
                raise ex.HTTPUnauthorized(
                    _('Token tenant != requested tenant'))
        return self.application
Example #10
0
    def __call__(self, environ, start_response):
        """Handle incoming request. authenticate and send downstream."""
        req = webob.Request(environ)
        self._logger.debug('Calling S3Token middleware.')

        try:
            parts = strutils.split_path(req.path, 1, 4, True)
            version, account, container, obj = parts
        except ValueError:
            msg = 'Not a path query, skipping.'
            self._logger.debug(msg)
            return self._app(environ, start_response)

        # Read request signature and access id.
        if 'Authorization' not in req.headers:
            msg = 'No Authorization header. skipping.'
            self._logger.debug(msg)
            return self._app(environ, start_response)

        token = req.headers.get('X-Auth-Token',
                                req.headers.get('X-Storage-Token'))
        if not token:
            msg = 'You did not specify an auth or a storage token. skipping.'
            self._logger.debug(msg)
            return self._app(environ, start_response)

        auth_header = req.headers['Authorization']
        try:
            access, signature = auth_header.split(' ')[-1].rsplit(':', 1)
        except ValueError:
            msg = 'You have an invalid Authorization header: %s'
            self._logger.debug(msg, auth_header)
            return self._deny_request('InvalidURI')(environ, start_response)

        # NOTE(chmou): This is to handle the special case with nova
        # when we have the option s3_affix_tenant. We will force it to
        # connect to another account than the one
        # authenticated. Before people start getting worried about
        # security, I should point that we are connecting with
        # username/token specified by the user but instead of
        # connecting to its own account we will force it to go to an
        # another account. In a normal scenario if that user don't
        # have the reseller right it will just fail but since the
        # reseller account can connect to every account it is allowed
        # by the swift_auth middleware.
        force_tenant = None
        if ':' in access:
            access, force_tenant = access.split(':')

        # Authenticate request.
        creds = {'credentials': {'access': access,
                                 'token': token,
                                 'signature': signature}}
        creds_json = jsonutils.dumps(creds)
        self._logger.debug('Connecting to Keystone sending this JSON: %s',
                           creds_json)
        # NOTE(vish): We could save a call to keystone by having
        #             keystone return token, tenant, user, and roles
        #             from this call.
        #
        # NOTE(chmou): We still have the same problem we would need to
        #              change token_auth to detect if we already
        #              identified and not doing a second query and just
        #              pass it through to swiftauth in this case.
        try:
            resp = self._json_request(creds_json)
        except ServiceError as e:
            resp = e.args[0]
            msg = 'Received error, exiting middleware with error: %s'
            self._logger.debug(msg, resp.status_code)
            return resp(environ, start_response)

        self._logger.debug('Keystone Reply: Status: %d, Output: %s',
                           resp.status_code, resp.content)

        try:
            identity_info = resp.json()
            token_id = str(identity_info['access']['token']['id'])
            tenant = identity_info['access']['token']['tenant']
        except (ValueError, KeyError):
            error = 'Error on keystone reply: %d %s'
            self._logger.debug(error, resp.status_code, resp.content)
            return self._deny_request('InvalidURI')(environ, start_response)

        req.headers['X-Auth-Token'] = token_id
        tenant_to_connect = force_tenant or tenant['id']
        if six.PY2 and isinstance(tenant_to_connect, six.text_type):
            tenant_to_connect = tenant_to_connect.encode('utf-8')
        self._logger.debug('Connecting with tenant: %s', tenant_to_connect)
        new_tenant_name = '%s%s' % (self._reseller_prefix, tenant_to_connect)
        environ['PATH_INFO'] = environ['PATH_INFO'].replace(account,
                                                            new_tenant_name)
        return self._app(environ, start_response)