Exemplo n.º 1
0
    def _validate_claims(self, claims):
        """Ask keystone (as keystone admin) for information for this user."""

        # TODO(todd): cache

        self.log.debug('Asking keystone to validate token')
        headers = {"Content-type": "application/json",
                    "Accept": "text/json",
                    "X-Auth-Token": self.admin_token}
        self.log.debug('headers: %r', headers)
        self.log.debug('url: %s', self.keystone_url)
        conn = http_connect(self.keystone_url.hostname, self.keystone_url.port,
                            'GET', '/v2.0/tokens/%s' % claims, headers=headers)
        resp = conn.getresponse()
        data = resp.read()
        conn.close()

        # Check http status code for the "OK" family of responses
        if not str(resp.status).startswith('20'):
            return False

        identity_info = json.loads(data)
        roles = []
        role_refs = identity_info["auth"]["user"]["roleRefs"]
        if role_refs is not None:
            for role_ref in role_refs:
                roles.append(role_ref["roleId"])

        # TODO(Ziad): add groups back in
        identity = {'user': identity_info['auth']['user']['username'],
                    'tenant': identity_info['auth']['user']['tenantId'],
                    'roles':roles}

        return identity
Exemplo n.º 2
0
 def _forward_request(self):
     """Token/Auth processed & claims added to headers"""
     self._decorate_request('AUTHORIZATION',
                               "Basic %s" % self.service_pass)
     #now decide how to pass on the call
     if self.app:
         # Pass to downstream WSGI component
         return self.app(self.env, self.start_response)
         #.custom_start_response)
     else:
         # We are forwarding to a remote service (no downstream WSGI app)
         req = Request(self.proxy_headers)
         parsed = urlparse(req.url)
         conn = http_connect(self.service_host,
                             self.service_port,
                             req.method,
                             parsed.path,
                             self.proxy_headers,
                             ssl=(self.service_protocol == 'https'))
         resp = conn.getresponse()
         data = resp.read()
         #TODO(ziad): use a more sophisticated proxy
         # we are rewriting the headers now
         return Response(status=resp.status, body=data)(self.proxy_headers,
                                                        self.start_response)
Exemplo n.º 3
0
    def __call__(self, env, start_response):
        def custom_start_response(status, headers):
            if self.delay_auth_decision:
                headers.append(('WWW-Authenticate', "Basic realm='API Realm'"))
            return start_response(status, headers)

        #TODO(Rasib): PERFORM OPENID AUTH

        #Auth processed, headers added now decide how to pass on the call
        if self.app:
            # Pass to downstream WSGI component
            env['HTTP_AUTHORIZATION'] = "Basic %s" % self.service_pass
            return self.app(env, custom_start_response)

        proxy_headers = []
        proxy_headers['AUTHORIZATION'] = "Basic %s" % self.service_pass
        # We are forwarding to a remote service (no downstream WSGI app)
        req = Request(proxy_headers)
        parsed = urlparse(req.url)
        conn = http_connect(self.service_host, self.service_port, \
             req.method, parsed.path, \
             proxy_headers, \
             ssl=(self.service_protocol == 'https'))
        resp = conn.getresponse()
        data = resp.read()
        #TODO(ziad): use a more sophisticated proxy
        # we are rewriting the headers now
        return Response(status=resp.status, body=data)(env, start_response)
Exemplo n.º 4
0
    def _validate_claims(self, claims):
        """Validate claims, and provide identity information isf applicable """

        # Step 1: We need to auth with the keystone service, so get an
        # admin token
        #TODO(ziad): Need to properly implement this, where to store creds
        # for now using token from ini
        #auth = self.get_admin_auth_token("admin", "secrete", "1")
        #admin_token = json.loads(auth)["auth"]["token"]["id"]

        # Step 2: validate the user's token with the auth service
        # since this is a priviledged op,m we need to auth ourselves
        # by using an admin token
        headers = {"Content-type": "application/json",
                    "Accept": "text/json",
                    "X-Auth-Token": self.admin_token}
                    ##TODO(ziad):we need to figure out how to auth to keystone
                    #since validate_token is a priviledged call
                    #Khaled's version uses creds to get a token
                    # "X-Auth-Token": admin_token}
                    # we're using a test token from the ini file for now
        conn = http_connect(self.auth_host, self.auth_port, 'GET',
                            '/v2.0/tokens/%s' % claims, headers=headers)
        resp = conn.getresponse()
        # data = resp.read()
        conn.close()

        if not str(resp.status).startswith('20'):
            # Keystone rejected claim
            return False
        else:
            #TODO(Ziad): there is an optimization we can do here. We have just
            #received data from Keystone that we can use instead of making
            #another call in _expound_claims
            return True
Exemplo n.º 5
0
    def _expound_claims(self):
        # Valid token. Get user data and put it in to the call
        # so the downstream service can use it
        headers = {"Content-type": "application/json",
                    "Accept": "text/json",
                    "X-Auth-Token": self.admin_token}
                    ##TODO(ziad):we need to figure out how to auth to keystone
                    #since validate_token is a priviledged call
                    #Khaled's version uses creds to get a token
                    # "X-Auth-Token": admin_token}
                    # we're using a test token from the ini file for now
        conn = http_connect(self.auth_host, self.auth_port, 'GET',
                            '/v2.0/tokens/%s' % self.claims, headers=headers)
        resp = conn.getresponse()
        data = resp.read()
        conn.close()

        if not str(resp.status).startswith('20'):
            raise LookupError('Unable to locate claims: %s' % resp.status)

        token_info = json.loads(data)
        roles = []
        role_refs = token_info["auth"]["user"]["roleRefs"]
        if role_refs != None:
            for role_ref in role_refs:
                roles.append(role_ref["roleId"])

        verified_claims = {'user': token_info['auth']['user']['username'],
                    'tenant': token_info['auth']['user']['tenantId'],
                    'roles': roles}
        return verified_claims
Exemplo n.º 6
0
    def _expound_claims(self):
        # Valid token. Get user data and put it in to the call
        # so the downstream service can use it
        headers = {"Content-type": "application/json",
                    "Accept": "text/json",
                    "X-Auth-Token": self.admin_token}
                    ##TODO(ziad):we need to figure out how to auth to keystone
                    #since validate_token is a priviledged call
                    #Khaled's version uses creds to get a token
                    # "X-Auth-Token": admin_token}
                    # we're using a test token from the ini file for now
        conn = http_connect(self.auth_host, self.auth_port, 'GET',
                            '/v1.0/token/%s' % self.claims, headers=headers)
        resp = conn.getresponse()
        data = resp.read()
        conn.close()

        if not str(resp.status).startswith('20'):
            raise LookupError('Unable to locate claims: %s' % resp.status)

        token_info = json.loads(data)
        #TODO(Ziad): make this more robust
        first_group = token_info['auth']['user']['groups']['group'][0]
        verified_claims = {'user': token_info['auth']['user']['username'],
                    'tenant': token_info['auth']['user']['tenantId'],
                    'group': '%s/%s' % (first_group['id'],
                                        first_group['tenantId'])}
        return verified_claims
Exemplo n.º 7
0
    def __call__(self, env, start_response):
        def custom_start_response(status, headers):
            if self.delay_auth_decision:
                headers.append(('WWW-Authenticate', "Basic realm='API Realm'"))
            return start_response(status, headers)

        #TODO(Rasib): PERFORM OPENID AUTH

        #Auth processed, headers added now decide how to pass on the call
        if self.app:
            # Pass to downstream WSGI component
            env['HTTP_AUTHORIZATION'] = "Basic %s" % self.service_pass
            return self.app(env, custom_start_response)

        proxy_headers = []
        proxy_headers['AUTHORIZATION'] = "Basic %s" % self.service_pass
        # We are forwarding to a remote service (no downstream WSGI app)
        req = Request(proxy_headers)
        parsed = urlparse(req.url)
        conn = http_connect(self.service_host, self.service_port, \
             req.method, parsed.path, \
             proxy_headers, \
             ssl=(self.service_protocol == 'https'))
        resp = conn.getresponse()
        data = resp.read()
        #TODO(ziad): use a more sophisticated proxy
        # we are rewriting the headers now
        return Response(status=resp.status, body=data)(env, start_response)
Exemplo n.º 8
0
    def _expound_claims(self):
        # Valid token. Get user data and put it in to the call
        # so the downstream service can use it
        headers = {"Content-type": "application/json",
                    "Accept": "application/json",
                    "X-Auth-Token": self.admin_token}
        conn = http_connect(self.auth_host, self.auth_port, 'GET',
                            self._build_token_uri(self.claims),
                            headers=headers,
                            ssl=(self.auth_protocol == 'https'),
                            key_file=self.key_file, cert_file=self.cert_file,
                            timeout=self.auth_timeout)
        resp = conn.getresponse()
        data = resp.read()
        # pylint: disable=E1103
        conn.close()

        if not str(resp.status).startswith('20'):
            raise LookupError('Unable to locate claims: %s' % resp.status)

        token_info = json.loads(data)
        #TODO(Ziad): make this more robust
        #first_group = token_info['auth']['user']['groups']['group'][0]
        roles = []
        rolegrants = token_info["access"]["user"]["roles"]
        if rolegrants is not None:
            roles = [rolegrant["id"] for rolegrant in rolegrants]

        token_info = json.loads(data)

        roles = [role['name'] for role in token_info[
            "access"]["user"]["roles"]]

        # in diablo, there were two ways to get tenant data
        tenant = token_info['access']['token'].get('tenant')
        if tenant:
            # post diablo
            tenant_id = tenant['id']
            tenant_name = tenant['name']
        else:
            # diablo only
            tenant_id = token_info['access']['user'].get('tenantId')
            tenant_name = token_info['access']['user'].get('tenantName')

        verified_claims = {
            'user': {
                'id': token_info['access']['user']['id'],
                'name': token_info['access']['user']['name'],
            },
            'tenant': {
                'id': tenant_id,
                'name': tenant_name
            },
            'roles': roles}

        return verified_claims
Exemplo n.º 9
0
    def _validate_claims(self, claims, retry=False):
        """Validate claims, and provide identity information if applicable """

        # Step 1: We need to auth with the keystone service, so get an
        # admin token
        # TODO(ziad): Need to properly implement this, where to store creds
        # for now using token from ini
        # NOTE(salvatore-orlando): Temporarily restoring auth token retrieval,
        # with credentials in configuration file
        if not self.admin_token:
            auth = self.get_admin_auth_token(self.admin_user,
                                             self.admin_password)
            self.admin_token = json.loads(auth)["access"]["token"]["id"]

        # Step 2: validate the user's token with the auth service
        # since this is a priviledged op,m we need to auth ourselves
        # by using an admin token
        headers = {
            "Content-type": "application/json",
            "Accept": "application/json",
            "X-Auth-Token": self.admin_token
        }
        conn = http_connect(self.auth_host,
                            self.auth_port,
                            'GET',
                            self._build_token_uri(claims),
                            headers=headers,
                            ssl=(self.auth_protocol == 'https'),
                            key_file=self.key_file,
                            cert_file=self.cert_file,
                            timeout=self.auth_timeout)
        resp = conn.getresponse()
        # pylint: disable=E1103
        conn.close()

        if not str(resp.status).startswith('20'):
            # Keystone rejected claim
            # In case a 404 error it might just be that the token has expired
            # Therefore try and get a new token
            # of course assuming admin credentials have been specified
            # Note(salvatore-orlando): the 404 here is not really
            # what should be returned
            if self.admin_user and self.admin_password and \
               not retry and str(resp.status) == '404':
                LOG.warn("Unable to validate token." +
                         "Admin token possibly expired.")
                self.admin_token = None
                return self._validate_claims(claims, True)
            return False
        else:
            #TODO(Ziad): there is an optimization we can do here. We have just
            #received data from Keystone that we can use instead of making
            #another call in _expound_claims
            LOG.info("Claims successfully validated")
            return True
Exemplo n.º 10
0
    def _expound_claims(self, claims):
        # Valid token. Get user data and put it in to the call
        # so the downstream service can use it
        headers = {
            "Content-type": "application/json",
            "Accept": "application/json",
            "X-Auth-Token": self.admin_token
        }
        ##TODO(ziad):we need to figure out how to auth to keystone
        #since validate_token is a priviledged call
        #Khaled's version uses creds to get a token
        # "X-Auth-Token": admin_token}
        # we're using a test token from the ini file for now
        conn = http_connect(self.auth_host,
                            self.auth_port,
                            'GET',
                            '/v2.0/tokens/%s' % claims,
                            headers=headers)
        resp = conn.getresponse()
        data = resp.read()
        conn.close()

        if not str(resp.status).startswith('20'):
            raise LookupError('Unable to locate claims: %s' % resp.status)

        token_info = json.loads(data)
        roles = []
        role_refs = token_info["access"]["user"]["roles"]
        if role_refs != None:
            for role_ref in role_refs:
                # Engine looks for the non case-sensitive role 'Admin'
                # to determine admin-ness
                roles.append(role_ref["name"])

        try:
            tenant = token_info['access']['token']['tenant']['id']
            tenant_name = token_info['access']['token']['tenant']['name']
        except:
            tenant = None
            tenant_name = None
        if not tenant:
            tenant = token_info['access']['user'].get('tenantId')
            tenant_name = token_info['access']['user'].get('tenantName')
        verified_claims = {
            'user': token_info['access']['user']['username'],
            'tenant': tenant,
            'roles': roles
        }
        if tenant_name:
            verified_claims['tenantName'] = tenant_name
        return verified_claims
Exemplo n.º 11
0
    def _validate_claims(self, claims, retry=False):
        """Validate claims, and provide identity information if applicable """

        # Step 1: We need to auth with the keystone service, so get an
        # admin token
        # TODO(ziad): Need to properly implement this, where to store creds
        # for now using token from ini
        # NOTE(salvatore-orlando): Temporarily restoring auth token retrieval,
        # with credentials in configuration file
        if not self.admin_token:
            auth = self.get_admin_auth_token(self.admin_user,
                                             self.admin_password)
            self.admin_token = json.loads(auth)["access"]["token"]["id"]

        # Step 2: validate the user's token with the auth service
        # since this is a priviledged op,m we need to auth ourselves
        # by using an admin token
        headers = {"Content-type": "application/json",
                    "Accept": "application/json",
                    "X-Auth-Token": self.admin_token}
        conn = http_connect(self.auth_host, self.auth_port, 'GET',
                            self._build_token_uri(claims), headers=headers,
                            ssl=(self.auth_protocol == 'https'),
                            key_file=self.key_file, cert_file=self.cert_file,
                            timeout=self.auth_timeout)
        resp = conn.getresponse()
        # pylint: disable=E1103
        conn.close()

        if not str(resp.status).startswith('20'):
            # Keystone rejected claim
            # In case a 404 error it might just be that the token has expired
            # Therefore try and get a new token
            # of course assuming admin credentials have been specified
            # Note(salvatore-orlando): the 404 here is not really
            # what should be returned
            if self.admin_user and self.admin_password and \
               not retry and str(resp.status) == '404':
                LOG.warn("Unable to validate token." +
                         "Admin token possibly expired.")
                self.admin_token = None
                return self._validate_claims(claims, True)
            return False
        else:
            #TODO(Ziad): there is an optimization we can do here. We have just
            #received data from Keystone that we can use instead of making
            #another call in _expound_claims
            LOG.info("Claims successfully validated")
            return True
Exemplo n.º 12
0
    def _validate_claims(self, claims):
        """Ask keystone (as keystone admin) for information for this user."""

        # TODO(todd): cache

        self.log.debug('Asking keystone to validate token')
        headers = {
            "Content-type": "application/json",
            "Accept": "application/json",
            "X-Auth-Token": self.admin_token
        }
        self.log.debug('headers: %r', headers)
        self.log.debug('url: %s', self.keystone_url)
        conn = http_connect(self.keystone_url.hostname,
                            self.keystone_url.port,
                            'GET',
                            '/v2.0/tokens/%s' % claims,
                            headers=headers)
        resp = conn.getresponse()
        data = resp.read()
        conn.close()

        # Check http status code for the "OK" family of responses
        if not str(resp.status).startswith('20'):
            return False

        identity_info = json.loads(data)
        roles = []
        role_refs = identity_info["access"]["user"]["roles"]

        if role_refs is not None:
            for role_ref in role_refs:
                roles.append(role_ref["id"])

        try:
            tenant = identity_info['access']['token']['tenantId']
        except:
            tenant = None
        if not tenant:
            tenant = identity_info['access']['user']['tenantId']
        # TODO(Ziad): add groups back in
        identity = {
            'user': identity_info['access']['user']['username'],
            'tenant': tenant,
            'roles': roles
        }

        return identity
Exemplo n.º 13
0
    def _expound_claims(self, claims):
        # Valid token. Get user data and put it in to the call
        # so the downstream service can use it
        headers = {'Content-type': 'application/json',
                    'Accept': 'application/json',
                    'X-Auth-Token': self.admin_token}
                    ##TODO(ziad):we need to figure out how to auth to keystone
                    #since validate_token is a priviledged call
                    #Khaled's version uses creds to get a token
                    # 'X-Auth-Token': admin_token}
                    # we're using a test token from the ini file for now
        conn = http_connect(self.auth_host, self.auth_port, 'GET',
                            '/v2.0/tokens/%s' % claims, headers=headers)
        resp = conn.getresponse()
        data = resp.read()
        conn.close()

        if not str(resp.status).startswith('20'):
            raise LookupError('Unable to locate claims: %s' % resp.status)

        token_info = json.loads(data)
        roles = []
        role_refs = token_info['access']['user']['roles']
        if role_refs != None:
            for role_ref in role_refs:
                # Nova looks for the non case-sensitive role 'admin'
                # to determine admin-ness
                roles.append(role_ref['name'])

        try:
            tenant = token_info['access']['token']['tenant']['id']
            tenant_name = token_info['access']['token']['tenant']['name']
        except:
            tenant = None
            tenant_name = None
        if not tenant:
            tenant = token_info['access']['user'].get('tenantId')
            tenant_name = token_info['access']['user'].get('tenantName')
        verified_claims = {
                    'user': token_info['access']['user']['id'],
                    'userName': token_info['access']['user']['username'],
                    'tenant': tenant,
                    'roles': roles}
        if tenant_name:
            verified_claims['tenantName'] = tenant_name
        return verified_claims
Exemplo n.º 14
0
    def _supports_osksvalidate(self):
        """Check if target Keystone server supports OS-KSVALIDATE."""
        if self.tested_for_osksvalidate:
            return self.osksvalidate

        headers = {"Accept": "application/json"}
        logger.debug("Connecting to %s://%s:%s to check extensions" %
                     (self.auth_protocol, self.auth_host, self.auth_port))
        try:
            self.last_test_for_osksvalidate = time.time()
            conn = http_connect(self.auth_host,
                                self.auth_port,
                                'GET',
                                '/v2.0/extensions/',
                                headers=headers,
                                ssl=(self.auth_protocol == 'https'),
                                key_file=self.key_file,
                                cert_file=self.cert_file,
                                timeout=self.auth_timeout)
            resp = conn.getresponse()
            data = resp.read()

            logger.debug("Response received: %s" % resp.status)
            if not str(resp.status).startswith('20'):
                logger.debug("Failed to detect extensions. "
                             "Falling back to core API")
                return False
        except EnvironmentError as exc:
            if exc.errno == errno.ECONNREFUSED:
                logger.warning("Keystone server not responding. Extension "
                               "detection will be retried later.")
            else:
                logger.exception("Unexpected error trying to detect "
                                 "extensions.")
            logger.debug("Falling back to core API behavior (using tokens in "
                         "URL)")
            return False
        except httplib.HTTPException as exc:
            logger.exception("Error trying to detect extensions.")
            logger.debug("Falling back to core API behavior (using tokens in "
                         "URL)")
            return False

        self.tested_for_osksvalidate = True
        return "OS-KSVALIDATE" in data
Exemplo n.º 15
0
    def _supports_osksvalidate(self):
        """Check if target Keystone server supports OS-KSVALIDATE."""
        if self.tested_for_osksvalidate:
            return self.osksvalidate

        headers = {"Accept": "application/json"}
        logger.debug("Connecting to %s://%s:%s to check extensions" % (
                self.auth_protocol, self.auth_host, self.auth_port))
        try:
            self.last_test_for_osksvalidate = time.time()
            conn = http_connect(self.auth_host, self.auth_port, 'GET',
                                '/v2.0/extensions/',
                                headers=headers,
                                ssl=(self.auth_protocol == 'https'),
                                key_file=self.key_file,
                                cert_file=self.cert_file,
                                timeout=self.auth_timeout)
            resp = conn.getresponse()
            data = resp.read()

            logger.debug("Response received: %s" % resp.status)
            if not str(resp.status).startswith('20'):
                logger.debug("Failed to detect extensions. "
                             "Falling back to core API")
                return False
        except EnvironmentError as exc:
            if exc.errno == errno.ECONNREFUSED:
                logger.warning("Keystone server not responding. Extension "
                            "detection will be retried later.")
            else:
                logger.exception("Unexpected error trying to detect "
                                 "extensions.")
            logger.debug("Falling back to core API behavior (using tokens in "
                         "URL)")
            return False
        except httplib.HTTPException as exc:
            logger.exception("Error trying to detect extensions.")
            logger.debug("Falling back to core API behavior (using tokens in "
                         "URL)")
            return False

        self.tested_for_osksvalidate = True
        return "OS-KSVALIDATE" in data
Exemplo n.º 16
0
    def _forward_request(self, env, start_response, proxy_headers):
        """Token/Auth processed & claims added to headers"""
        self._decorate_request('AUTHORIZATION',
            "Basic %s" % self.service_pass, env, proxy_headers)
        #now decide how to pass on the call
        if self.app:
            # Pass to downstream WSGI component
            logger.debug("Sending request to next app in WSGI pipeline")
            return self.app(env, start_response)
            #.custom_start_response)
        else:
            # We are forwarding to a remote service (no downstream WSGI app)
            logger.debug("Sending request to %s" % self.service_url)
            req = Request(proxy_headers)
            parsed = urlparse(req.url)

            # pylint: disable=E1101
            conn = http_connect(self.service_host,
                                self.service_port,
                                req.method,
                                parsed.path,
                                proxy_headers,
                                ssl=(self.service_protocol == 'https'),
                                timeout=self.service_timeout)
            resp = conn.getresponse()
            data = resp.read()
            logger.debug("Response was %s" % resp.status)

            #TODO(ziad): use a more sophisticated proxy
            # we are rewriting the headers now

            if resp.status in (401, 305):
                # Add our own headers to the list
                headers = [("WWW_AUTHENTICATE",
                   "Keystone uri='%s'" % self.auth_location)]
                return Response(status=resp.status, body=data,
                            headerlist=headers)(env,
                                                start_response)
            else:
                return Response(status=resp.status, body=data)(env,
                                                start_response)
Exemplo n.º 17
0
    def _forward_request(self, env, start_response, proxy_headers):
        """Token/Auth processed & claims added to headers"""
        self._decorate_request('AUTHORIZATION', "Basic %s" % self.service_pass,
                               env, proxy_headers)
        #now decide how to pass on the call
        if self.app:
            # Pass to downstream WSGI component
            logger.debug("Sending request to next app in WSGI pipeline")
            return self.app(env, start_response)
            #.custom_start_response)
        else:
            # We are forwarding to a remote service (no downstream WSGI app)
            logger.debug("Sending request to %s" % self.service_url)
            req = Request(proxy_headers)
            parsed = urlparse(req.url)

            # pylint: disable=E1101
            conn = http_connect(self.service_host,
                                self.service_port,
                                req.method,
                                parsed.path,
                                proxy_headers,
                                ssl=(self.service_protocol == 'https'),
                                timeout=self.service_timeout)
            resp = conn.getresponse()
            data = resp.read()
            logger.debug("Response was %s" % resp.status)

            #TODO(ziad): use a more sophisticated proxy
            # we are rewriting the headers now

            if resp.status in (401, 305):
                # Add our own headers to the list
                headers = [("WWW_AUTHENTICATE",
                            "Keystone uri='%s'" % self.auth_location)]
                return Response(status=resp.status,
                                body=data,
                                headerlist=headers)(env, start_response)
            else:
                return Response(status=resp.status, body=data)(env,
                                                               start_response)
Exemplo n.º 18
0
 def _forward_request(self):
     """Token/Auth processed & claims added to headers"""
     #now decide how to pass on the call
     if self.app:
         # Pass to downstream WSGI component
         return self.app(self.env, self.start_response)
         #.custom_start_response)
     else:
         # We are forwarding to a remote service (no downstream WSGI app)
         req = Request(self.proxy_headers)
         parsed = urlparse(req.url)
         conn = http_connect(self.service_host,
                             self.service_port,
                             req.method,
                             parsed.path,
                             self.proxy_headers,
                             ssl=(self.service_protocol == 'https'))
         resp = conn.getresponse()
         data = resp.read()
         return Response(status=resp.status, body=data)(self.proxy_headers,
                                                        self.start_response)
Exemplo n.º 19
0
    def _validate_claims(self, claims):
        """Validate claims, and provide identity information isf applicable """

        # Step 1: We need to auth with the keystone service, so get an
        # admin token
        #TODO(ziad): Need to properly implement this, where to store creds
        # for now using token from ini
        #auth = self.get_admin_auth_token("admin", "secrete", "1")
        #admin_token = json.loads(auth)["auth"]["token"]["id"]

        # Step 2: validate the user's token with the auth service
        # since this is a priviledged op,m we need to auth ourselves
        # by using an admin token
        headers = {
            "Content-type": "application/json",
            "Accept": "application/json",
            "X-Auth-Token": self.admin_token
        }
        ##TODO(ziad):we need to figure out how to auth to keystone
        #since validate_token is a priviledged call
        #Khaled's version uses creds to get a token
        # "X-Auth-Token": admin_token}
        # we're using a test token from the ini file for now
        conn = http_connect(self.auth_host,
                            self.auth_port,
                            'GET',
                            '/v2.0/tokens/%s' % claims,
                            headers=headers)
        resp = conn.getresponse()
        # data = resp.read()
        conn.close()

        if not str(resp.status).startswith('20'):
            # Keystone rejected claim
            return False
        else:
            #TODO(Ziad): there is an optimization we can do here. We have just
            #received data from Keystone that we can use instead of making
            #another call in _expound_claims
            return True
Exemplo n.º 20
0
 def _forward_request(self):
     """Token/Auth processed & claims added to headers"""
     #now decide how to pass on the call
     if self.app:
         # Pass to downstream WSGI component
         return self.app(self.env, self.start_response)
         #.custom_start_response)
     else:
         # We are forwarding to a remote service (no downstream WSGI app)
         req = Request(self.proxy_headers)
         # pylint: disable=E1101
         parsed = urlparse(req.url)
         conn = http_connect(self.service_host,
                             self.service_port,
                             req.method,
                             parsed.path,
                             self.proxy_headers,
                             ssl=(self.service_protocol == 'https'))
         resp = conn.getresponse()
         data = resp.read()
         return Response(status=resp.status, body=data)(self.proxy_headers,
                                                        self.start_response)
Exemplo n.º 21
0
    def _validate_claims(self, claims):
        """Ask keystone (as keystone admin) for information for this user."""

        # TODO(todd): cache

        self.log.debug("Asking keystone to validate token")
        headers = {"Content-type": "application/json", "Accept": "application/json", "X-Auth-Token": self.admin_token}
        self.log.debug("headers: %r", headers)
        self.log.debug("url: %s", self.keystone_url)
        conn = http_connect(
            self.keystone_url.hostname, self.keystone_url.port, "GET", "/v2.0/tokens/%s" % claims, headers=headers
        )
        resp = conn.getresponse()
        data = resp.read()
        conn.close()

        # Check http status code for the "OK" family of responses
        if not str(resp.status).startswith("20"):
            return False

        identity_info = json.loads(data)
        roles = []
        role_refs = identity_info["access"]["user"]["roles"]

        if role_refs is not None:
            for role_ref in role_refs:
                roles.append(role_ref["id"])

        try:
            tenant = identity_info["access"]["token"]["tenantId"]
        except:
            tenant = None
        if not tenant:
            tenant = identity_info["access"]["user"]["tenantId"]
        # TODO(Ziad): add groups back in
        identity = {"user": identity_info["access"]["user"]["username"], "tenant": tenant, "roles": roles}

        return identity
Exemplo n.º 22
0
    def _validate_claims(self, claims, retry=True):
        """Validate claims, and provide identity information isf applicable """

        # Step 1: We need to auth with the keystone service, so get an
        # admin token
        if not self.admin_token:
            self.admin_token = self._get_admin_auth_token(self.admin_user,
                                                          self.admin_password)

        # Step 2: validate the user's token with the auth service
        # since this is a priviledged op,m we need to auth ourselves
        # by using an admin token
        headers = {'Content-type': 'application/json',
                    'Accept': 'application/json',
                    'X-Auth-Token': self.admin_token}
                    ##TODO(ziad):we need to figure out how to auth to keystone
                    #since validate_token is a priviledged call
                    #Khaled's version uses creds to get a token
                    # 'X-Auth-Token': admin_token}
                    # we're using a test token from the ini file for now
        conn = http_connect(self.auth_host, self.auth_port, 'GET',
                            '/v2.0/tokens/%s' % claims, headers=headers)
        resp = conn.getresponse()
        # data = resp.read()
        conn.close()

        if not str(resp.status).startswith('20'):
            if retry:
                self.admin_token = None
                return self._validate_claims(claims, False)
            else:
                return False
        else:
            #TODO(Ziad): there is an optimization we can do here. We have just
            #received data from Keystone that we can use instead of making
            #another call in _expound_claims
            return True
Exemplo n.º 23
0
    def _forward_request(self, env, start_response, proxy_headers):
        """Token/Auth processed & claims added to headers"""
        self._decorate_request('AUTHORIZATION', "Basic %s" % self.service_pass,
                               env, proxy_headers)
        #now decide how to pass on the call
        if self.app:
            # Pass to downstream WSGI component
            return self.app(env, start_response)
            #.custom_start_response)
        else:
            # We are forwarding to a remote service (no downstream WSGI app)
            req = webob.Request(proxy_headers)
            parsed = urlparse(req.url)

            conn = http_connect(self.service_host,
                                self.service_port,
                                req.method,
                                parsed.path,
                                proxy_headers,
                                ssl=(self.service_protocol == 'https'))
            resp = conn.getresponse()
            data = resp.read()

            #TODO(ziad): use a more sophisticated proxy
            # we are rewriting the headers now

            if resp.status == 401 or resp.status == 305:
                # Add our own headers to the list
                headers = [("WWW_AUTHENTICATE",
                            "Keystone uri='%s'" % self.auth_location)]
                return webob.Response(status=resp.status,
                                      body=data,
                                      headerlist=headers)(env, start_response)
            else:
                return webob.Response(status=resp.status,
                                      body=data)(env, start_response)
Exemplo n.º 24
0
    def _expound_claims(self):
        # Valid token. Get user data and put it in to the call
        # so the downstream service can use it
        headers = {"Content-type": "application/json", "Accept": "text/json", "X-Auth-Token": self.admin_token}
        ##TODO(ziad):we need to figure out how to auth to keystone
        # since validate_token is a priviledged call
        # Khaled's version uses creds to get a token
        # "X-Auth-Token": admin_token}
        # we're using a test token from the ini file for now
        conn = http_connect(self.auth_host, self.auth_port, "GET", "/v2.0/tokens/%s" % self.claims, headers=headers)
        resp = conn.getresponse()
        data = resp.read()
        conn.close()

        if not str(resp.status).startswith("20"):
            raise LookupError("Unable to locate claims: %s" % resp.status)

        token_info = json.loads(data)
        # TODO(Ziad): make this more robust
        # first_group = token_info['auth']['user']['groups']['group'][0]
        roles = []
        role_refs = token_info["auth"]["user"]["roleRefs"]
        if role_refs != None:
            for role_ref in role_refs:
                roles.append(role_ref["roleId"])

        verified_claims = {
            "user": token_info["auth"]["user"]["username"],
            "tenant": token_info["auth"]["user"]["tenantId"],
            "roles": roles,
        }

        # TODO(Ziad): removed groups for now
        #            ,'group': '%s/%s' % (first_group['id'],
        #                                first_group['tenantId'])}
        return verified_claims
Exemplo n.º 25
0
    def _forward_request(self, env, start_response, proxy_headers):
        """Token/Auth processed & claims added to headers"""
        self._decorate_request('AUTHORIZATION',
            'Basic %s' % self.service_pass, env, proxy_headers)
        #now decide how to pass on the call
        if self.app:
            # Pass to downstream WSGI component
            return self.app(env, start_response)
            #.custom_start_response)
        else:
            # We are forwarding to a remote service (no downstream WSGI app)
            req = webob.Request(proxy_headers)
            parsed = urlparse(req.url)

            conn = http_connect(self.service_host,
                                self.service_port,
                                req.method,
                                parsed.path,
                                proxy_headers,
                                ssl=(self.service_protocol == 'https'))
            resp = conn.getresponse()
            data = resp.read()

            #TODO(ziad): use a more sophisticated proxy
            # we are rewriting the headers now

            if resp.status == 401 or resp.status == 305:
                # Add our own headers to the list
                headers = [('WWW_AUTHENTICATE',
                   "Keystone uri='%s'" % self.auth_location)]
                return webob.Response(status=resp.status,
                                      body=data,
                                      headerlist=headers)(env, start_response)
            else:
                return webob.Response(status=resp.status,
                                      body=data)(env, start_response)
Exemplo n.º 26
0
    def __call__(self, env, start_response):
        def custom_start_response(status, headers):
            if self.delay_auth_decision:
                headers.append(('WWW-Authenticate',
                                "Basic realm='Use guest/guest'"))
            return start_response(status, headers)

        #Prep headers to proxy request to remote service
        proxy_headers = env.copy()
        user = ''

        #Look for authentication
        if 'HTTP_AUTHORIZATION' not in env:
            #No credentials were provided
            if self.delay_auth_decision:
                _decorate_request_headers("X_IDENTITY_STATUS", "Invalid",
                                          proxy_headers, env)
            else:
                # If the user isn't authenticated, we reject the request and
                # return 401 indicating we need Basic Auth credentials.
                ret = HTTPUnauthorized("Authentication required",
                        [('WWW-Authenticate',
                          'Basic realm="Use guest/guest"')])
                return ret(env, start_response)
        else:
            # Claims were provided - validate them
            import base64
            auth_header = env['HTTP_AUTHORIZATION']
            _auth_type, encoded_creds = auth_header.split(None, 1)
            user, password = base64.b64decode(encoded_creds).split(':', 1)
            if not self.validateCreds(user, password):
                #Claims were rejected
                if not self.delay_auth_decision:
                    # Reject request (or ask for valid claims)
                    ret = HTTPUnauthorized("Authentication required",
                            [('WWW-Authenticate',
                              'Basic realm="Use guest/guest"')])
                    return ret(env, start_response)
                else:
                    # Claims are valid, forward request
                    _decorate_request_headers("X_IDENTITY_STATUS", "Invalid",
                                              proxy_headers, env)

            # TODO(Ziad): add additional details we may need,
            #             like tenant and group info
            _decorate_request_headers('X_AUTHORIZATION', "Proxy %s" % user,
                                      proxy_headers, env)
            _decorate_request_headers("X_IDENTITY_STATUS", "Confirmed",
                                      proxy_headers, env)
            _decorate_request_headers('X_TENANT', 'blank',
                                      proxy_headers, env)
            #Auth processed, headers added now decide how to pass on the call
            if self.app:
                # Pass to downstream WSGI component
                env['HTTP_AUTHORIZATION'] = "Basic %s" % self.service_pass
                return self.app(env, custom_start_response)

            proxy_headers['AUTHORIZATION'] = "Basic %s" % self.service_pass
            # We are forwarding to a remote service (no downstream WSGI app)
            req = Request(proxy_headers)
            parsed = urlparse(req.url)
            conn = http_connect(self.service_host, self.service_port, \
                                req.method, parsed.path, \
                                proxy_headers, \
                                ssl=(self.service_protocol == 'https'))
            resp = conn.getresponse()
            data = resp.read()
            #TODO(ziad): use a more sophisticated proxy
            # we are rewriting the headers now
            return Response(status=resp.status, body=data)(env, start_response)
Exemplo n.º 27
0
    def _verify_claims(self, env, claims):
        """Verify claims and extract identity information, if applicable."""

        cached_claims = self._cache_get(env, claims)
        if cached_claims:
            LOG.debug("Found cached claims")
            claims, expires, valid = cached_claims
            if not valid:
                LOG.debug("Claims not valid (according to cache)")
                raise ValidationFailed()
            if expires <= datetime.now():
                LOG.debug("Claims (token) expired (according to cache)")
                raise TokenExpired()
            return claims

        # Step 1: We need to auth with the keystone service, so get an
        # admin token
        #TODO(ziad): Need to properly implement this, where to store creds
        # for now using token from ini
        #auth = self.get_admin_auth_token("admin", "secrete", "1")
        #admin_token = json.loads(auth)["auth"]["token"]["id"]

        # Step 2: validate the user's token with the auth service
        # since this is a priviledged op,m we need to auth ourselves
        # by using an admin token
        headers = {
            "Content-type": "application/json",
            "Accept": "application/json",
            "X-Auth-Token": self.admin_token
        }
        ##TODO(ziad):we need to figure out how to auth to keystone
        #since validate_token is a priviledged call
        #Khaled's version uses creds to get a token
        # "X-Auth-Token": admin_token}
        # we're using a test token from the ini file for now
        LOG.debug("Connecting to %s://%s:%s to check claims" %
                  (self.auth_protocol, self.auth_host, self.auth_port))
        conn = http_connect(self.auth_host,
                            self.auth_port,
                            'GET',
                            '/v2.0/tokens/%s%s' % (claims, self.serviceId_qs),
                            headers=headers,
                            ssl=(self.auth_protocol == 'https'),
                            key_file=self.key_file,
                            cert_file=self.cert_file,
                            timeout=self.auth_timeout)
        resp = conn.getresponse()
        data = resp.read()

        LOG.debug("Response received: %s" % resp.status)
        if not str(resp.status).startswith('20'):
            # Cache it if there is a cache available
            if self.cache:
                LOG.debug("Caching that results were invalid")
                self._cache_put(env,
                                claims,
                                claims={
                                    'expires':
                                    datetime.strftime(datetime.now(),
                                                      EXPIRE_TIME_FORMAT)
                                },
                                valid=False)
            # Keystone rejected claim
            LOG.debug("Failing the validation")
            raise ValidationFailed()

        token_info = json.loads(data)

        roles = [
            role['name'] for role in token_info["access"]["user"]["roles"]
        ]

        # in diablo, there were two ways to get tenant data
        tenant = token_info['access']['token'].get('tenant')
        if tenant:
            # post diablo
            tenant_id = tenant['id']
            tenant_name = tenant['name']
        else:
            # diablo only
            tenant_id = token_info['access']['user'].get('tenantId')
            tenant_name = token_info['access']['user'].get('tenantName')
        LOG.debug("Tenant identified: id=%s, name=%s" %
                  (tenant_id, tenant_name))

        verified_claims = {
            'user': {
                'id': token_info['access']['user']['id'],
                'name': token_info['access']['user']['name'],
            },
            'tenant': {
                'id': tenant_id,
                'name': tenant_name
            },
            'roles': roles,
            'expires': token_info['access']['token']['expires']
        }
        LOG.debug("User identified: id=%s, name=%s" %
                  (token_info['access']['user']['id'],
                   token_info['access']['user']['name']))

        expires = get_datetime(verified_claims['expires'])
        if expires <= datetime.now():
            LOG.debug("Claims (token) expired: %s" % str(expires))
            # Cache it if there is a cache available (we also cached bad
            # claims)
            if self.cache:
                LOG.debug("Caching expired claim (token)")
                self._cache_put(env, claims, verified_claims, valid=False)
            raise TokenExpired()

        # Cache it if there is a cache available
        if self.cache:
            LOG.debug("Caching validated claim")
            self._cache_put(env, claims, verified_claims, valid=True)
        LOG.debug("Returning successful validation")
        return verified_claims
Exemplo n.º 28
0
    def _verify_claims(self, claims):
        """Verify claims and extract identity information, if applicable."""

        # Step 1: We need to auth with the keystone service, so get an
        # admin token
        #TODO(ziad): Need to properly implement this, where to store creds
        # for now using token from ini
        #auth = self.get_admin_auth_token("admin", "secrete", "1")
        #admin_token = json.loads(auth)["auth"]["token"]["id"]

        # Step 2: validate the user's token with the auth service
        # since this is a priviledged op,m we need to auth ourselves
        # by using an admin token
        headers = {"Content-type": "application/json",
                    "Accept": "application/json",
                    "X-Auth-Token": self.admin_token}
                    ##TODO(ziad):we need to figure out how to auth to keystone
                    #since validate_token is a priviledged call
                    #Khaled's version uses creds to get a token
                    # "X-Auth-Token": admin_token}
                    # we're using a test token from the ini file for now
        conn = http_connect(self.auth_host, self.auth_port, 'GET',
                            '/v2.0/tokens/%s' % claims, headers=headers,
                            ssl=(self.auth_protocol == 'https'),
                            key_file=self.key_file, cert_file=self.cert_file,
                            timeout=self.auth_timeout)
        resp = conn.getresponse()
        data = resp.read()
        conn.close()

        if not str(resp.status).startswith('20'):
            # Keystone rejected claim
            raise ValidationFailed()

        token_info = json.loads(data)

        roles = [role['name'] for role in token_info[
            "access"]["user"]["roles"]]

        # in diablo, there were two ways to get tenant data
        tenant = token_info['access']['token'].get('tenant')
        if tenant:
            # post diablo
            tenant_id = tenant['id']
            tenant_name = tenant['name']
        else:
            # diablo only
            tenant_id = token_info['access']['user'].get('tenantId')
            tenant_name = token_info['access']['user'].get('tenantName')

        verified_claims = {
            'user': {
                'id': token_info['access']['user']['id'],
                'name': token_info['access']['user']['name'],
            },
            'tenant': {
                'id': tenant_id,
                'name': tenant_name
            },
            'roles': roles,
            'expires': token_info['access']['token']['expires']}

        return verified_claims
Exemplo n.º 29
0
    def __call__(self, env, start_response):
        def custom_start_response(status, headers):
            if self.delay_auth_decision:
                headers.append(('WWW-Authenticate',
                                "Basic realm='Use guest/guest'"))
            return start_response(status, headers)

        #Prep headers to proxy request to remote service
        proxy_headers = env.copy()
        user = ''

        #Look for authentication
        if 'HTTP_AUTHORIZATION' not in env:
            #No credentials were provided
            if self.delay_auth_decision:
                _decorate_request_headers("X_IDENTITY_STATUS", "Invalid",
                                          proxy_headers, env)
            else:
                # If the user isn't authenticated, we reject the request and
                # return 401 indicating we need Basic Auth credentials.
                return HTTPUnauthorized("Authentication required",
                                        [('WWW-Authenticate',
                                        'Basic realm="Use guest/guest"')])\
                                                (env, start_response)
        else:
            # Claims were provided - validate them
            import base64
            auth_header = env['HTTP_AUTHORIZATION']
            _auth_type, encoded_creds = auth_header.split(None, 1)
            user, password = base64.b64decode(encoded_creds).split(':', 1)
            if not self.validateCreds(user, password):
                #Claims were rejected
                if not self.delay_auth_decision:
                    # Reject request (or ask for valid claims)
                    return HTTPUnauthorized("Authentication required",
                                [('WWW-Authenticate',
                                'Basic realm="Use guest/guest"')])\
                                        (env, start_response)
                else:
                    # Claims are valid, forward request
                    _decorate_request_headers("X_IDENTITY_STATUS", "Invalid",
                                              proxy_headers, env)

            # TODO(Ziad): add additional details we may need,
            #             like tenant and group info
            _decorate_request_headers('X_AUTHORIZATION', "Proxy %s" % user,
                                      proxy_headers, env)
            _decorate_request_headers("X_IDENTITY_STATUS", "Confirmed",
                                      proxy_headers, env)
            _decorate_request_headers('X_TENANT', 'blank',
                                      proxy_headers, env)
            #Auth processed, headers added now decide how to pass on the call
            if self.app:
                # Pass to downstream WSGI component
                env['HTTP_AUTHORIZATION'] = "Basic %s" % self.service_pass
                return self.app(env, custom_start_response)

            proxy_headers['AUTHORIZATION'] = "Basic %s" % self.service_pass
            # We are forwarding to a remote service (no downstream WSGI app)
            req = Request(proxy_headers)
            parsed = urlparse(req.url)
            conn = http_connect(self.service_host, self.service_port, \
                                req.method, parsed.path, \
                                proxy_headers, \
                                ssl=(self.service_protocol == 'https'))
            resp = conn.getresponse()
            data = resp.read()
            #TODO(ziad): use a more sophisticated proxy
            # we are rewriting the headers now
            return Response(status=resp.status, body=data)(env, start_response)
Exemplo n.º 30
0
    def _verify_claims(self, env, claims, retry=True):
        """Verify claims and extract identity information, if applicable."""

        cached_claims = self._cache_get(env, claims)
        if cached_claims:
            logger.debug("Found cached claims")
            claims, expires, valid = cached_claims
            if not valid:
                logger.debug("Claims not valid (according to cache)")
                raise ValidationFailed()
            if expires <= time.time():
                logger.debug("Claims (token) expired (according to cache)")
                raise TokenExpired()
            return claims

        # Step 1: We need to auth with the keystone service, so get an
        # admin token
        if not self.admin_token:
            auth = self._get_admin_auth_token(self.admin_user,
                                              self.admin_password)
            self.admin_token = json.loads(auth)["access"]["token"]["id"]

        # Step 2: validate the user's token with the auth service
        # since this is a priviledged op,m we need to auth ourselves
        # by using an admin token
        headers = {
            "Content-type": "application/json",
            "Accept": "application/json",
            "X-Auth-Token": self.admin_token
        }
        if self.osksvalidate:
            headers['X-Subject-Token'] = claims
            path = '/v2.0/OS-KSVALIDATE/token/validate/%s' % \
                   self.service_id_querystring
            logger.debug("Connecting to %s://%s:%s to check claims using the"
                         "OS-KSVALIDATE extension" %
                         (self.auth_protocol, self.auth_host, self.auth_port))
        else:
            path = '/v2.0/tokens/%s%s' % (claims, self.service_id_querystring)
            logger.debug("Connecting to %s://%s:%s to check claims" %
                         (self.auth_protocol, self.auth_host, self.auth_port))

        try:
            conn = http_connect(self.auth_host,
                                self.auth_port,
                                'GET',
                                path,
                                headers=headers,
                                ssl=(self.auth_protocol == 'https'),
                                key_file=self.key_file,
                                cert_file=self.cert_file,
                                timeout=self.auth_timeout)
            resp = conn.getresponse()
            data = resp.read()
        except EnvironmentError as exc:
            if exc.errno == errno.ECONNREFUSED:
                logger.error(
                    "Keystone server not responding on %s://%s:%s "
                    "to check claims" %
                    (self.auth_protocol, self.auth_host, self.auth_port))
                raise KeystoneUnreachable("Unable to connect to authentication"
                                          " server")
            else:
                logger.exception(exc)
                raise

        logger.debug("Response received: %s" % resp.status)
        if not str(resp.status).startswith('20'):
            # Cache it if there is a cache available
            if self.cache:
                logger.debug("Caching that results were invalid")
                self._cache_put(env,
                                claims,
                                claims={
                                    'expires':
                                    datetime.strftime(time.time(),
                                                      EXPIRE_TIME_FORMAT)
                                },
                                valid=False)
            if retry:
                self.admin_token = None
                return self._verify_claims(env, claims, False)
            else:
                # Keystone rejected claim
                logger.debug("Failing the validation")
                raise ValidationFailed()

        token_info = json.loads(data)

        roles = [
            role['name'] for role in token_info["access"]["user"]["roles"]
        ]

        # in diablo, there were two ways to get tenant data
        tenant = token_info['access']['token'].get('tenant')
        if tenant:
            # post diablo
            tenant_id = tenant['id']
            tenant_name = tenant['name']
        else:
            # diablo only
            tenant_id = token_info['access']['user'].get('tenantId')
            tenant_name = token_info['access']['user'].get('tenantName')
        logger.debug("Tenant identified: id=%s, name=%s" %
                     (tenant_id, tenant_name))

        verified_claims = {
            'user': {
                'id': token_info['access']['user']['id'],
                'name': token_info['access']['user']['name'],
            },
            'tenant': {
                'id': tenant_id,
                'name': tenant_name
            },
            'roles': roles,
            'expires': token_info['access']['token']['expires']
        }
        logger.debug("User identified: id=%s, name=%s" %
                     (token_info['access']['user']['id'],
                      token_info['access']['user']['name']))

        expires = self._convert_date(verified_claims['expires'])
        if expires <= time.time():
            logger.debug("Claims (token) expired: %s" % str(expires))
            # Cache it if there is a cache available (we also cached bad
            # claims)
            if self.cache:
                logger.debug("Caching expired claim (token)")
                self._cache_put(env, claims, verified_claims, valid=False)
            raise TokenExpired()

        # Cache it if there is a cache available
        if self.cache:
            logger.debug("Caching validated claim")
            self._cache_put(env, claims, verified_claims, valid=True)
        logger.debug("Returning successful validation")
        return verified_claims
Exemplo n.º 31
0
    def _verify_claims(self, env, claims):
        """Verify claims and extract identity information, if applicable."""

        cached_claims = self._cache_get(env, claims)
        if cached_claims:
            LOG.debug("Found cached claims")
            claims, expires, valid = cached_claims
            if not valid:
                LOG.debug("Claims not valid (according to cache)")
                raise ValidationFailed()
            if expires <= datetime.now():
                LOG.debug("Claims (token) expired (according to cache)")
                raise TokenExpired()
            return claims

        # Step 1: We need to auth with the keystone service, so get an
        # admin token
        #TODO(ziad): Need to properly implement this, where to store creds
        # for now using token from ini
        #auth = self.get_admin_auth_token("admin", "secrete", "1")
        #admin_token = json.loads(auth)["auth"]["token"]["id"]

        # Step 2: validate the user's token with the auth service
        # since this is a priviledged op,m we need to auth ourselves
        # by using an admin token
        headers = {"Content-type": "application/json",
                    "Accept": "application/json",
                    "X-Auth-Token": self.admin_token}
                    ##TODO(ziad):we need to figure out how to auth to keystone
                    #since validate_token is a priviledged call
                    #Khaled's version uses creds to get a token
                    # "X-Auth-Token": admin_token}
                    # we're using a test token from the ini file for now
        LOG.debug("Connecting to %s://%s:%s to check claims" % (
                self.auth_protocol, self.auth_host, self.auth_port))
        conn = http_connect(self.auth_host, self.auth_port, 'GET',
                            '/v2.0/tokens/%s%s' % (claims, self.serviceId_qs),
                            headers=headers,
                            ssl=(self.auth_protocol == 'https'),
                            key_file=self.key_file, cert_file=self.cert_file,
                            timeout=self.auth_timeout)
        resp = conn.getresponse()
        data = resp.read()

        LOG.debug("Response received: %s" % resp.status)
        if not str(resp.status).startswith('20'):
            # Cache it if there is a cache available
            if self.cache:
                LOG.debug("Caching that results were invalid")
                self._cache_put(env, claims,
                                claims={'expires':
                                datetime.strftime(datetime.now(),
                                                  EXPIRE_TIME_FORMAT)},
                                valid=False)
            # Keystone rejected claim
            LOG.debug("Failing the validation")
            raise ValidationFailed()

        token_info = json.loads(data)

        roles = [role['name'] for role in token_info[
            "access"]["user"]["roles"]]

        # in diablo, there were two ways to get tenant data
        tenant = token_info['access']['token'].get('tenant')
        if tenant:
            # post diablo
            tenant_id = tenant['id']
            tenant_name = tenant['name']
        else:
            # diablo only
            tenant_id = token_info['access']['user'].get('tenantId')
            tenant_name = token_info['access']['user'].get('tenantName')
        LOG.debug("Tenant identified: id=%s, name=%s" % (tenant_id,
                                                                tenant_name))

        verified_claims = {
            'user': {
                'id': token_info['access']['user']['id'],
                'name': token_info['access']['user']['name'],
            },
            'tenant': {
                'id': tenant_id,
                'name': tenant_name
            },
            'roles': roles,
            'expires': token_info['access']['token']['expires']}
        LOG.debug("User identified: id=%s, name=%s" % (
                token_info['access']['user']['id'],
                token_info['access']['user']['name']))

        expires = get_datetime(verified_claims['expires'])
        if expires <= datetime.now():
            LOG.debug("Claims (token) expired: %s" % str(expires))
            # Cache it if there is a cache available (we also cached bad
            # claims)
            if self.cache:
                LOG.debug("Caching expired claim (token)")
                self._cache_put(env, claims, verified_claims, valid=False)
            raise TokenExpired()

        # Cache it if there is a cache available
        if self.cache:
            LOG.debug("Caching validated claim")
            self._cache_put(env, claims, verified_claims, valid=True)
        LOG.debug("Returning successful validation")
        return verified_claims
Exemplo n.º 32
0
    def _verify_claims(self, env, claims, retry=True):
        """Verify claims and extract identity information, if applicable."""

        cached_claims = self._cache_get(env, claims)
        if cached_claims:
            logger.debug("Found cached claims")
            claims, expires, valid = cached_claims
            if not valid:
                logger.debug("Claims not valid (according to cache)")
                raise ValidationFailed()
            if expires <= time.time():
                logger.debug("Claims (token) expired (according to cache)")
                raise TokenExpired()
            return claims

        # Step 1: We need to auth with the keystone service, so get an
        # admin token
        if not self.admin_token:
            auth = self._get_admin_auth_token(self.admin_user,
                                                  self.admin_password)
            self.admin_token = json.loads(auth)["access"]["token"]["id"]

        # Step 2: validate the user's token with the auth service
        # since this is a priviledged op,m we need to auth ourselves
        # by using an admin token
        headers = {"Content-type": "application/json",
                    "Accept": "application/json",
                    "X-Auth-Token": self.admin_token}
        if self.osksvalidate:
            headers['X-Subject-Token'] = claims
            path = '/v2.0/OS-KSVALIDATE/token/validate/%s' % \
                   self.service_id_querystring
            logger.debug("Connecting to %s://%s:%s to check claims using the"
                      "OS-KSVALIDATE extension" % (self.auth_protocol,
                            self.auth_host, self.auth_port))
        else:
            path = '/v2.0/tokens/%s%s' % (claims, self.service_id_querystring)
            logger.debug("Connecting to %s://%s:%s to check claims" % (
                    self.auth_protocol, self.auth_host, self.auth_port))

        try:
            conn = http_connect(self.auth_host, self.auth_port, 'GET',
                                path,
                                headers=headers,
                                ssl=(self.auth_protocol == 'https'),
                                key_file=self.key_file,
                                cert_file=self.cert_file,
                                timeout=self.auth_timeout)
            resp = conn.getresponse()
            data = resp.read()
        except EnvironmentError as exc:
            if exc.errno == errno.ECONNREFUSED:
                logger.error("Keystone server not responding on %s://%s:%s "
                             "to check claims" % (self.auth_protocol,
                                                  self.auth_host,
                                                  self.auth_port))
                raise KeystoneUnreachable("Unable to connect to authentication"
                                          " server")
            else:
                logger.exception(exc)
                raise

        logger.debug("Response received: %s" % resp.status)
        if not str(resp.status).startswith('20'):
            # Cache it if there is a cache available
            if self.cache:
                logger.debug("Caching that results were invalid")
                self._cache_put(env, claims,
                                claims={'expires':
                                datetime.strftime(time.time(),
                                                  EXPIRE_TIME_FORMAT)},
                                valid=False)
            if retry:
                self.admin_token = None
                return self._verify_claims(env, claims, False)
            else:
                # Keystone rejected claim
                logger.debug("Failing the validation")
                raise ValidationFailed()

        token_info = json.loads(data)

        roles = [role['name'] for role in token_info[
            "access"]["user"]["roles"]]

        # in diablo, there were two ways to get tenant data
        tenant = token_info['access']['token'].get('tenant')
        if tenant:
            # post diablo
            tenant_id = tenant['id']
            tenant_name = tenant['name']
        else:
            # diablo only
            tenant_id = token_info['access']['user'].get('tenantId')
            tenant_name = token_info['access']['user'].get('tenantName')
        logger.debug("Tenant identified: id=%s, name=%s" % (tenant_id,
                                                                tenant_name))

        verified_claims = {
            'user': {
                'id': token_info['access']['user']['id'],
                'name': token_info['access']['user']['name'],
            },
            'tenant': {
                'id': tenant_id,
                'name': tenant_name
            },
            'roles': roles,
            'expires': token_info['access']['token']['expires']}
        logger.debug("User identified: id=%s, name=%s" % (
                token_info['access']['user']['id'],
                token_info['access']['user']['name']))

        expires = self._convert_date(verified_claims['expires'])
        if expires <= time.time():
            logger.debug("Claims (token) expired: %s" % str(expires))
            # Cache it if there is a cache available (we also cached bad
            # claims)
            if self.cache:
                logger.debug("Caching expired claim (token)")
                self._cache_put(env, claims, verified_claims, valid=False)
            raise TokenExpired()

        # Cache it if there is a cache available
        if self.cache:
            logger.debug("Caching validated claim")
            self._cache_put(env, claims, verified_claims, valid=True)
        logger.debug("Returning successful validation")
        return verified_claims