def fake_auth_ref(fake_token, fake_service=None): """Create an auth_ref using keystoneauth's fixtures""" token_copy = copy.deepcopy(fake_token) token_copy['token_id'] = token_copy.pop('id') token = fixture.V2Token(**token_copy) # An auth_ref is actually an access info object auth_ref = access.create(body=token) # Create a service catalog if fake_service: service = token.add_service( fake_service.type, fake_service.name, ) # TODO(dtroyer): Add an 'id' element to KSA's _Service fixure service['id'] = fake_service.id for e in fake_service.endpoints: # KSA's _Service fixture copies publicURL to internalURL and # adminURL if they do not exist. Soooo helpful... internal = e.get('internalURL', None) admin = e.get('adminURL', None) region = e.get('region_id') or e.get('region', '<none>') endpoint = service.add_endpoint( public=e['publicURL'], internal=internal, admin=admin, region=region, ) # ...so undo that helpfulness if not internal: endpoint['internalURL'] = None if not admin: endpoint['adminURL'] = None return auth_ref
def _create_auth_plugin(self): if self.auth_token_info: access_info = access.create(body=self.auth_token_info, auth_token=self.auth_token) return access_plugin.AccessInfoPlugin( auth_ref=access_info, auth_url=self.keystone_v3_endpoint) if self.auth_token: # FIXME(jamielennox): This is broken but consistent. If you # only have a token but don't load a service catalog then # url_for wont work. Stub with the keystone endpoint so at # least it might be right. return token_endpoint.Token(endpoint=self.keystone_v3_endpoint, token=self.auth_token) if self.password: return generic.Password(username=self.username, password=self.password, project_id=self.tenant_id, user_domain_id=self.user_domain, auth_url=self.keystone_v3_endpoint) LOG.error("Keystone API connection failed, no password " "trust or auth_token!") raise exception.AuthorizationFailure()
def fake_auth_ref(fake_token, fake_service=None): """Create an auth_ref using keystoneauth's fixtures""" token_copy = copy.deepcopy(fake_token) token_id = token_copy.pop('id') token = fixture.V3Token(**token_copy) # An auth_ref is actually an access info object auth_ref = access.create( body=token, auth_token=token_id, ) # Create a service catalog if fake_service: service = token.add_service( fake_service['type'], fake_service['name'], ) # TODO(dtroyer): Add an 'id' element to KSA's _Service fixure service['id'] = fake_service['id'] for e in fake_service['endpoints']: region = e.get('region_id') or e.get('region', '<none>') service.add_endpoint( e['interface'], e['url'], region=region, ) return auth_ref
def test_will_expire_soon(self): expires = timeutils.utcnow() + datetime.timedelta(minutes=5) token = fixture.V3Token(expires=expires) auth_ref = access.create(body=token) self.assertFalse(auth_ref.will_expire_soon(stale_duration=120)) self.assertTrue(auth_ref.will_expire_soon(stale_duration=301)) self.assertFalse(auth_ref.will_expire_soon())
def test_building_unscoped_accessinfo(self): token = fixture.V2Token(expires='2012-10-03T16:58:01Z') auth_ref = access.create(body=token) self.assertIsInstance(auth_ref, access.AccessInfoV2) self.assertFalse(auth_ref.has_service_catalog()) self.assertEqual(auth_ref.auth_token, token.token_id) self.assertEqual(auth_ref.username, token.user_name) self.assertEqual(auth_ref.user_id, token.user_id) self.assertEqual(auth_ref.role_ids, []) self.assertEqual(auth_ref.role_names, []) self.assertIsNone(auth_ref.tenant_name) self.assertIsNone(auth_ref.tenant_id) self.assertFalse(auth_ref.domain_scoped) self.assertFalse(auth_ref.project_scoped) self.assertFalse(auth_ref.trust_scoped) self.assertIsNone(auth_ref.project_domain_id) self.assertIsNone(auth_ref.project_domain_name) self.assertIsNone(auth_ref.user_domain_id) self.assertIsNone(auth_ref.user_domain_name) self.assertEqual(auth_ref.expires, token.expires) self.assertEqual(auth_ref.issued, token.issued) self.assertEqual(token.audit_id, auth_ref.audit_id) self.assertIsNone(auth_ref.audit_chain_id) self.assertIsNone(token.audit_chain_id) self.assertIsNone(auth_ref.bind)
def test_multi_region(self): token = fixture.V3Token() s = token.add_service(type='identity') s.add_endpoint('internal', self.INTERNAL_URL, region=self.REGION_ONE) s.add_endpoint('public', self.PUBLIC_URL, region=self.REGION_TWO) s.add_endpoint('admin', self.ADMIN_URL, region=self.REGION_THREE) auth_ref = access.create(body=token) catalog_data = auth_ref.service_catalog.catalog catalog = _request._v3_to_v2_catalog(catalog_data) self.assertEqual(1, len(catalog)) service = catalog[0] # the 3 regions will come through as 3 separate endpoints expected = [{'internalURL': self.INTERNAL_URL, 'region': self.REGION_ONE}, {'publicURL': self.PUBLIC_URL, 'region': self.REGION_TWO}, {'adminURL': self.ADMIN_URL, 'region': self.REGION_THREE}] self.assertEqual('identity', service['type']) self.assertEqual(3, len(service['endpoints'])) for e in expected: self.assertIn(e, expected)
def _plugin(self, **kwargs): token = fixture.V3Token() s = token.add_service('identity') s.add_standard_endpoints(public=self.TEST_ROOT_URL) auth_ref = access.create(body=token, auth_token=self.auth_token) return access_plugin.AccessInfoPlugin(auth_ref, **kwargs)
def test_service_catalog_get_endpoints_region_names(self): sc = access.create(auth_token=uuid.uuid4().hex, body=self.AUTH_RESPONSE_BODY).service_catalog endpoints = sc.get_endpoints(service_type='image', region_name='North') self.assertEqual(len(endpoints), 1) for endpoint in endpoints['image']: self.assertEqual(endpoint['url'], self.north_endpoints[endpoint['interface']]) endpoints = sc.get_endpoints(service_type='image', region_name='South') self.assertEqual(len(endpoints), 1) for endpoint in endpoints['image']: self.assertEqual(endpoint['url'], self.south_endpoints[endpoint['interface']]) endpoints = sc.get_endpoints(service_type='compute') self.assertEqual(len(endpoints['compute']), 3) endpoints = sc.get_endpoints(service_type='compute', region_name='North') self.assertEqual(len(endpoints['compute']), 3) endpoints = sc.get_endpoints(service_type='compute', region_name='West') self.assertEqual(len(endpoints['compute']), 0)
def get_unscoped_auth_ref(self, session): """Authenticate with OpenID Connect and get back claims. This is a multi-step process. First an access token must be retrieved, to do this, an authorization code and redirect URL must be given. Secondly, we then exchange the access token upon accessing the protected Keystone endpoint (federated auth URL). This will trigger the OpenID Connect Provider to perform a user introspection and retrieve information (specified in the scope) about the user in the form of an OpenID Connect Claim. These claims will be sent to Keystone in the form of environment variables. :param session: a session object to send out HTTP requests. :type session: keystoneauth1.session.Session :returns: a token data representation :rtype: :py:class:`keystoneauth1.access.AccessInfoV3` """ # get an access token payload = {'grant_type': self.grant_type, 'redirect_uri': self.redirect_uri, 'code': self.code} access_token = self._get_access_token(session, payload) response = self._get_keystone_token(session, access_token) # grab the unscoped token return access.create(resp=response)
def test_password_change_auth_state(self): self.stub_auth(json=self.TEST_RESPONSE_DICT) expired = ksa_utils.before_utcnow(days=2) token = fixture.V2Token(expires=expired) auth_ref = access.create(body=token) a = v2.Password(self.TEST_URL, username=self.TEST_USER, password=self.TEST_PASS, tenant_id=uuid.uuid4().hex) initial_cache_id = a.get_cache_id() state = a.get_auth_state() self.assertIsNone(state) state = json.dumps({'auth_token': auth_ref.auth_token, 'body': auth_ref._data}) a.set_auth_state(state) self.assertEqual(token.token_id, a.auth_ref.auth_token) s = session.Session() self.assertEqual(self.TEST_TOKEN, a.get_token(s)) # updates expired self.assertEqual(initial_cache_id, a.get_cache_id())
def test_service_catalog_service_name(self): sc = access.create(auth_token=uuid.uuid4().hex, body=self.AUTH_RESPONSE_BODY).service_catalog url = sc.url_for(service_name='glance', interface='public', service_type='image', region_name='North') self.assertEqual('http://glance.north.host/glanceapi/public', url) url = sc.url_for(service_name='glance', interface='public', service_type='image', region_name='South') self.assertEqual('http://glance.south.host/glanceapi/public', url) self.assertRaises(exceptions.EndpointNotFound, sc.url_for, service_name='glance', service_type='compute') urls = sc.get_urls(service_type='image', service_name='glance', interface='public') self.assertIn('http://glance.north.host/glanceapi/public', urls) self.assertIn('http://glance.south.host/glanceapi/public', urls) urls = sc.get_urls(service_type='image', service_name='Servers', interface='public') self.assertEqual(0, len(urls))
def test_service_catalog_endpoint_id(self): token = fixture.V2Token() token.set_scope() endpoint_id = uuid.uuid4().hex public_url = uuid.uuid4().hex s = token.add_service('compute') s.add_endpoint(public=public_url, id=endpoint_id) s.add_endpoint(public=uuid.uuid4().hex) auth_ref = access.create(body=token) # initially assert that we get back all our urls for a simple filter urls = auth_ref.service_catalog.get_urls(interface='public') self.assertEqual(2, len(urls)) urls = auth_ref.service_catalog.get_urls(endpoint_id=endpoint_id, interface='public') self.assertEqual((public_url, ), urls) # with bad endpoint_id nothing should be found urls = auth_ref.service_catalog.get_urls(endpoint_id=uuid.uuid4().hex, interface='public') self.assertEqual(0, len(urls)) # we ignore a service_id because v2 doesn't know what it is urls = auth_ref.service_catalog.get_urls(endpoint_id=endpoint_id, service_id=uuid.uuid4().hex, interface='public') self.assertEqual((public_url, ), urls)
def test_service_catalog_empty(self): self.AUTH_RESPONSE_BODY['access']['serviceCatalog'] = [] auth_ref = access.create(body=self.AUTH_RESPONSE_BODY) self.assertRaises(exceptions.EmptyCatalog, auth_ref.service_catalog.url_for, service_type='image', interface='internalURL')
def get_unscoped_auth_ref(self, session): """Authenticate with OpenID Connect and get back claims. This is a multi-step process. First an access token must be retrieved, to do this, the username and password, the OpenID Connect client ID and secret, and the access token endpoint must be known. Secondly, we then exchange the access token upon accessing the protected Keystone endpoint (federated auth URL). This will trigger the OpenID Connect Provider to perform a user introspection and retrieve information (specified in the scope) about the user in the form of an OpenID Connect Claim. These claims will be sent to Keystone in the form of environment variables. :param session: a session object to send out HTTP requests. :type session: keystoneauth1.session.Session :returns: a token data representation :rtype: :py:class:`keystoneauth1.access.AccessInfoV3` """ # get an access token client_auth = (self.client_id, self.client_secret) payload = {'grant_type': self.grant_type, 'username': self.username, 'password': self.password, 'scope': self.scope} response = self._get_access_token(session, client_auth, payload, self.access_token_endpoint) access_token = response.json()[self.access_token_type] # use access token against protected URL headers = {'Authorization': 'Bearer ' + access_token} response = self._get_keystone_token(session, headers, self.federated_token_url) # grab the unscoped token return access.create(resp=response)
def test_grizzly_token(self): grizzly_token = { 'access': { 'token': { 'id': uuid.uuid4().hex, 'expires': '2020-01-01T00:00:10.000123Z', }, 'user': { 'id': 'user_id1', 'name': 'user_name1', 'tenantId': 'tenant_id1', 'tenantName': 'tenant_name1', 'roles': [ {'name': 'role1'}, {'name': 'role2'}, ], }, }, } auth_ref = access.create(body=grizzly_token) self.assertIsInstance(auth_ref, access.AccessInfoV2) self.assertEqual(auth_ref.project_id, 'tenant_id1') self.assertEqual(auth_ref.project_name, 'tenant_name1') self.assertIsNone(auth_ref.project_domain_id) self.assertIsNone(auth_ref.project_domain_name) self.assertIsNone(auth_ref.user_domain_id, 'default') self.assertIsNone(auth_ref.user_domain_name, 'Default') self.assertEqual(auth_ref.role_names, ['role1', 'role2'])
def get_unscoped_auth_ref(self, session, **kwargs): sp_auth_url = self._local_cloud_plugin.get_sp_auth_url(session, self._sp_id) sp_url = self._local_cloud_plugin.get_sp_url(session, self._sp_id) self.auth_url = self._remote_auth_url(sp_auth_url) response = self._send_service_provider_ecp_authn_response(session, sp_url, sp_auth_url) return access.create(resp=response)
def test_service_catalog_regions(self): self.AUTH_RESPONSE_BODY['token']['region_name'] = "North" auth_ref = access.create(auth_token=uuid.uuid4().hex, body=self.AUTH_RESPONSE_BODY) sc = auth_ref.service_catalog url = sc.url_for(service_type='image', interface='public') self.assertEqual(url, "http://glance.north.host/glanceapi/public") self.AUTH_RESPONSE_BODY['token']['region_name'] = "South" auth_ref = access.create(auth_token=uuid.uuid4().hex, body=self.AUTH_RESPONSE_BODY) sc = auth_ref.service_catalog url = sc.url_for(service_type='image', region_name="South", interface='internal') self.assertEqual(url, "http://glance.south.host/glanceapi/internal")
def test_service_catalog_endpoints(self): auth_ref = access.create(body=self.AUTH_RESPONSE_BODY) sc = auth_ref.service_catalog public_ep = sc.get_endpoints(service_type='compute', interface='publicURL') self.assertEqual(public_ep['compute'][1]['tenantId'], '2') self.assertEqual(public_ep['compute'][1]['versionId'], '1.1') self.assertEqual(public_ep['compute'][1]['internalURL'], "https://compute.north.host/v1.1/3456")
def test_project_scoped_service_headers(self): token = fixture.V3Token() token.set_project_scope() token_id = uuid.uuid4().hex auth_ref = access.create(auth_token=token_id, body=token) self.request.set_service_headers(auth_ref) self._test_v3_headers(token, '-Service')
def _do_fetch_token(self, token): """Helper method to fetch a token and convert it into an AccessInfo""" data = self.fetch_token(token) try: return data, access.create(body=data, auth_token=token) except Exception: self.log.warning(_LW('Invalid token contents.'), exc_info=True) raise ksm_exceptions.InvalidToken(_('Token authorization failed'))
def test_will_expire_soon(self): token = fixture.V2Token() expires = timeutils.utcnow() + datetime.timedelta(minutes=5) token.expires = expires auth_ref = access.create(body=token) self.assertIsInstance(auth_ref, access.AccessInfoV2) self.assertFalse(auth_ref.will_expire_soon(stale_duration=120)) self.assertTrue(auth_ref.will_expire_soon(stale_duration=300)) self.assertFalse(auth_ref.will_expire_soon())
def test_service_catalog_endpoints(self): sc = access.create(auth_token=uuid.uuid4().hex, body=self.AUTH_RESPONSE_BODY).service_catalog public_ep = sc.get_endpoints(service_type='compute', interface='public') self.assertEqual(public_ep['compute'][0]['region_id'], 'North') self.assertEqual(public_ep['compute'][0]['url'], 'https://compute.north.host/novapi/public')
def test_binding(self): token = fixture.V3Token() principal = uuid.uuid4().hex token.set_bind('kerberos', principal) auth_ref = access.create(body=token) self.assertIsInstance(auth_ref, access.AccessInfoV3) self.assertEqual({'kerberos': principal}, auth_ref.bind)
def test_building_a_service_catalog(self): auth_ref = access.create(body=self.AUTH_RESPONSE_BODY) sc = auth_ref.service_catalog self.assertEqual(sc.url_for(service_type='compute'), "https://compute.north.host/v1/1234") self.assertRaises(exceptions.EndpointNotFound, sc.url_for, region_name="South", service_type='compute')
def test_service_catalog_alias_find_exact_match(self): auth_ref = access.create(auth_token=uuid.uuid4().hex, body=self.AUTH_RESPONSE_BODY) sc = auth_ref.service_catalog # Tests that we find the volumev3 endpoint when we request it. public_ep = sc.get_endpoints(service_type='volumev3', interface='public') self.assertEqual(public_ep['volumev3'][0]['region'], 'South') self.assertEqual(public_ep['volumev3'][0]['url'], "http://cinder.south.host/cinderapi/public/v3")
def test_service_catalog_url_for_region_names(self): sc = access.create(auth_token=uuid.uuid4().hex, body=self.AUTH_RESPONSE_BODY).service_catalog url = sc.url_for(service_type='image', region_name='North') self.assertEqual(url, self.north_endpoints['public']) url = sc.url_for(service_type='image', region_name='South') self.assertEqual(url, self.south_endpoints['public']) self.assertRaises(exceptions.EndpointNotFound, sc.url_for, service_type='image', region_name='West')
def _extract_service_catalog(self, body): """Set the client's service catalog from the response data.""" self.auth_ref = access.create(body=body) self.service_catalog = self.auth_ref.service_catalog self.auth_token = self.auth_ref.auth_token self.auth_tenant_id = self.auth_ref.tenant_id self.auth_user_id = self.auth_ref.user_id if not self.endpoint_url: self.endpoint_url = self.service_catalog.url_for( region_name=self.region_name, service_type=self.service_type, interface=self.endpoint_type )
def _do_fetch_token(self, token, **kwargs): """Helper method to fetch a token and convert it into an AccessInfo.""" # NOTE(edmondsw): strip the token to remove any whitespace that may # have been passed along in the header per bug 1689468 token = token.strip() data = self.fetch_token(token, **kwargs) try: return data, access.create(body=data, auth_token=token) except Exception: self.log.warning('Invalid token contents.', exc_info=True) raise ksm_exceptions.InvalidToken(_('Token authorization failed'))
def _create_expired_auth_plugin(self, **kwargs): expires = _utils.before_utcnow(minutes=20) expired_token = self.get_auth_data(expires=expires) expired_auth_ref = access.create(body=expired_token) body = 'SUCCESS' self.stub_url('GET', ['path'], base_url=self.TEST_COMPUTE_ADMIN, text=body) a = self.create_auth_plugin(**kwargs) a.auth_ref = expired_auth_ref return a
def set_auth_state(self, data): """Install existing authentication state for a plugin. Take the output of get_auth_state and install that authentication state into the current authentication plugin. """ if data: auth_data = json.loads(data) self.auth_ref = access.create(body=auth_data['body'], auth_token=auth_data['auth_token']) else: self.auth_ref = None
def test_building_project_scoped_accessinfo(self): token = fixture.V3Token() token.set_project_scope() s = token.add_service(type='identity') s.add_standard_endpoints(public='http://url') token_id = uuid.uuid4().hex auth_ref = access.create(body=token, auth_token=token_id) self.assertIn('methods', auth_ref._data['token']) self.assertIn('catalog', auth_ref._data['token']) self.assertTrue(auth_ref.has_service_catalog()) self.assertTrue(auth_ref._data['token']['catalog']) self.assertEqual(token_id, auth_ref.auth_token) self.assertEqual(token.user_name, auth_ref.username) self.assertEqual(token.user_id, auth_ref.user_id) self.assertEqual(token.role_ids, auth_ref.role_ids) self.assertEqual(token.role_names, auth_ref.role_names) self.assertIsNone(auth_ref.domain_name) self.assertIsNone(auth_ref.domain_id) self.assertEqual(token.project_name, auth_ref.project_name) self.assertEqual(token.project_id, auth_ref.project_id) self.assertEqual(auth_ref.tenant_name, auth_ref.project_name) self.assertEqual(auth_ref.tenant_id, auth_ref.project_id) self.assertEqual(token.project_domain_id, auth_ref.project_domain_id) self.assertEqual(token.project_domain_name, auth_ref.project_domain_name) self.assertEqual(token.user_domain_id, auth_ref.user_domain_id) self.assertEqual(token.user_domain_name, auth_ref.user_domain_name) self.assertFalse(auth_ref.domain_scoped) self.assertTrue(auth_ref.project_scoped) self.assertIsNone(auth_ref.project_is_domain) self.assertEqual(token.audit_id, auth_ref.audit_id) self.assertEqual(token.audit_chain_id, auth_ref.audit_chain_id)
def test_v2_roles(self): role_id = 'a' role_name = 'b' token = fixture.V2Token() token.set_scope() token.add_role(id=role_id, name=role_name) auth_ref = access.create(body=token) self.assertIsInstance(auth_ref, access.AccessInfoV2) self.assertEqual([role_id], auth_ref.role_ids) self.assertEqual([role_id], auth_ref._data['access']['metadata']['roles']) self.assertEqual([role_name], auth_ref.role_names) self.assertEqual([{ 'name': role_name }], auth_ref._data['access']['user']['roles'])
def get_unscoped_auth_ref(self, session): """Authenticate with OpenID Connect and get back claims. We exchange the access token upon accessing the protected Keystone endpoint (federated auth URL). This will trigger the OpenID Connect Provider to perform a user introspection and retrieve information (specified in the scope) about the user in the form of an OpenID Connect Claim. These claims will be sent to Keystone in the form of environment variables. :param session: a session object to send out HTTP requests. :type session: keystoneauth1.session.Session :returns: a token data representation :rtype: :py:class:`keystoneauth1.access.AccessInfoV3` """ response = self._get_keystone_token(session, self.access_token) return access.create(resp=response)
def test_oauth_access(self): consumer_id = uuid.uuid4().hex access_token_id = uuid.uuid4().hex token = fixture.V3Token() token.set_project_scope() token.set_oauth(access_token_id=access_token_id, consumer_id=consumer_id) auth_ref = access.create(body=token) self.assertEqual(consumer_id, auth_ref.oauth_consumer_id) self.assertEqual(access_token_id, auth_ref.oauth_access_token_id) self.assertEqual(consumer_id, auth_ref._data['token']['OS-OAUTH1']['consumer_id']) self.assertEqual( access_token_id, auth_ref._data['token']['OS-OAUTH1']['access_token_id'])
def get_unscoped_auth_ref(self, session, *kwargs): """Retrieve unscoped token after authentcation with ADFS server. This is a multistep process: * Prepare ADFS Request Securty Token - build an etree.XML object filling certain attributes with proper user credentials, created/expires dates (ticket is be valid for 120 seconds as currently we don't handle reusing ADFS issued security tokens). * Send ADFS Security token to the ADFS server. Step handled by * Receive and parse security token, extract actual SAML assertion and prepare a request addressed for the Service Provider endpoint. This also includes changing namespaces in the XML document. Step handled by ``ADFSPassword._prepare_sp_request()`` method. * Send prepared assertion to the Service Provider endpoint. Usually the server will respond with HTTP 301 code which should be ignored as the 'location' header doesn't contain protected area. The goal of this operation is fetching the session cookie which later allows for accessing protected URL endpoints. Step handed by ``ADFSPassword._send_assertion_to_service_provider()`` method. * Once the session cookie is issued, the protected endpoint can be accessed and an unscoped token can be retrieved. Step handled by ``ADFSPassword._access_service_provider()`` method. :param session: a session object to send out HTTP requests. :type session: keystoneauth1.session.Session :returns: AccessInfo :rtype: :py:class:`keystoneauth1.access.AccessInfo` """ self._prepare_adfs_request() self._get_adfs_security_token(session) self._prepare_sp_request() self._send_assertion_to_service_provider(session) self._access_service_provider(session) return access.create(resp=self.authenticated_response)
def _do_fetch_token(self, token, **kwargs): """Helper method to fetch a token and convert it into an AccessInfo.""" # NOTE(edmondsw): strip the token to remove any whitespace that may # have been passed along in the header per bug 1689468 token = token.strip() if self.kwargs_to_fetch_token: data = self.fetch_token(token, **kwargs) else: m = _('Implementations of auth_token must set ' 'kwargs_to_fetch_token this will be the required and ' 'assumed in Queens.') warnings.warn(m) data = self.fetch_token(token) try: return data, access.create(body=data, auth_token=token) except Exception: self.log.warning('Invalid token contents.', exc_info=True) raise ksm_exceptions.InvalidToken(_('Token authorization failed'))
def test_building_unscoped_accessinfo(self): token = fixture.V3Token() token_id = uuid.uuid4().hex auth_ref = access.create(body=token, auth_token=token_id) self.assertIn('methods', auth_ref._data['token']) self.assertFalse(auth_ref.has_service_catalog()) self.assertNotIn('catalog', auth_ref._data['token']) self.assertEqual(token_id, auth_ref.auth_token) self.assertEqual(token.user_name, auth_ref.username) self.assertEqual(token.user_id, auth_ref.user_id) self.assertEqual(auth_ref.role_ids, []) self.assertEqual(auth_ref.role_names, []) self.assertIsNone(auth_ref.project_name) self.assertIsNone(auth_ref.project_id) self.assertFalse(auth_ref.domain_scoped) self.assertFalse(auth_ref.project_scoped) self.assertIsNone(auth_ref.project_is_domain) self.assertEqual(token.user_domain_id, auth_ref.user_domain_id) self.assertEqual(token.user_domain_name, auth_ref.user_domain_name) self.assertIsNone(auth_ref.project_domain_id) self.assertIsNone(auth_ref.project_domain_name) self.assertEqual(auth_ref.expires, timeutils.parse_isotime(token['token']['expires_at'])) self.assertEqual(auth_ref.issued, timeutils.parse_isotime(token['token']['issued_at'])) self.assertEqual(auth_ref.expires, token.expires) self.assertEqual(auth_ref.issued, token.issued) self.assertEqual(auth_ref.audit_id, token.audit_id) self.assertIsNone(auth_ref.audit_chain_id) self.assertIsNone(token.audit_chain_id) self.assertIsNone(auth_ref.bind)
def test_service_catalog_without_service_type(self): token = fixture.V3Token() token.set_project_scope() public_urls = [] for i in range(0, 3): public_url = uuid.uuid4().hex public_urls.append(public_url) s = token.add_service(uuid.uuid4().hex) s.add_endpoint('public', public_url) auth_ref = access.create(body=token) urls = auth_ref.service_catalog.get_urls(interface='public') self.assertEqual(3, len(urls)) for p in public_urls: self.assertIn(p, urls)
def get_unscoped_auth_ref(self, session): """Authenticate with OpenID Connect and get back claims. This is a multi-step process: 1.- An access token must be retrieved from the server. In order to do so, we need to exchange an authorization grant or refresh token with the token endpoint in order to obtain an access token. The authorization grant varies from plugin to plugin. 2.- We then exchange the access token upon accessing the protected Keystone endpoint (federated auth URL). This will trigger the OpenID Connect Provider to perform a user introspection and retrieve information (specified in the scope) about the user in the form of an OpenID Connect Claim. These claims will be sent to Keystone in the form of environment variables. :param session: a session object to send out HTTP requests. :type session: keystoneauth1.session.Session :returns: a token data representation :rtype: :py:class:`keystoneauth1.access.AccessInfoV3` """ # First of all, check if the grant type is supported discovery = self._get_discovery_document(session) grant_types = discovery.get("grant_types_supported") if (grant_types and self.grant_type is not None and self.grant_type not in grant_types): raise exceptions.OidcPluginNotSupported() # Get the payload payload = self.get_payload(session) payload.setdefault('grant_type', self.grant_type) # get an access token access_token = self._get_access_token(session, payload) response = self._get_keystone_token(session, access_token) # grab the unscoped token return access.create(resp=response)
def create_trust_to_karbor(self, context): if not context.auth_token_info: msg = ("user=%s, project=%s" % (context.user_id, context.project_id)) raise exception.AuthorizationFailure(obj=msg) auth_ref = access.create(body=context.auth_token_info, auth_token=context.auth_token) user_auth_plugin = access_plugin.AccessInfoPlugin( auth_url=self._auth_uri, auth_ref=auth_ref) l_kc_v3 = self._get_keystone_client(user_auth_plugin) try: trust = l_kc_v3.trusts.create(trustor_user=context.user_id, trustee_user=self._karbor_user_id, project=context.project_id, impersonation=True, role_names=context.roles) return trust.id except Exception as e: raise exception.AuthorizationFailure(obj=str(e))
def _extract_service_catalog(self, url, resp, body, extract_token=True): """See what the auth service told us and process the response. We may get redirected to another site, fail or actually get back a service catalog with a token and our endpoints. """ # content must always present if resp.status_code == 200 or resp.status_code == 201: try: self.auth_url = url self.auth_ref = access.create(resp=resp, body=body) self.service_catalog = self.auth_ref.service_catalog if extract_token: self.auth_token = self.auth_ref.auth_token management_url = self.service_catalog.url_for( region_name=self.region_name, interface=self.endpoint_type, service_type=self.service_type, service_name=self.service_name) self.management_url = management_url.rstrip('/') return None except exceptions.AmbiguousEndpoints: print("Found more than one valid endpoint. Use a more " "restrictive filter") raise except ValueError: # ValueError is raised when you pass an invalid response to # access.create. This should never happen in reality if the # status code is 200. raise exceptions.AuthorizationFailure() except exceptions.EndpointNotFound: print("Could not find any suitable endpoint. Correct region?") raise elif resp.status_code == 305: return resp.headers['location'] else: raise exceptions.from_response(resp, body)
def get_unscoped_auth_ref(self, session): """Authenticate with OpenID Connect and get back claims. This is a multi-step process. First an access token must be retrieved, to do this, the username and password, the OpenID Connect client ID and secret, and the access token endpoint must be known. Secondly, we then exchange the access token upon accessing the protected Keystone endpoint (federated auth URL). This will trigger the OpenID Connect Provider to perform a user introspection and retrieve information (specified in the scope) about the user in the form of an OpenID Connect Claim. These claims will be sent to Keystone in the form of environment variables. :param session: a session object to send out HTTP requests. :type session: keystoneauth1.session.Session :returns: a token data representation :rtype: :py:class:`keystoneauth1.access.AccessInfoV3` """ # get an access token client_auth = (self.client_id, self.client_secret) payload = { 'grant_type': self.grant_type, 'username': self.username, 'password': self.password, 'scope': self.scope } response = self._get_access_token(session, client_auth, payload, self.access_token_endpoint) access_token = response.json()[self.access_token_type] # use access token against protected URL headers = {'Authorization': 'Bearer ' + access_token} response = self._get_keystone_token(session, headers, self.federated_token_url) # grab the unscoped token return access.create(resp=response)
def get_unscoped_auth_ref(self, session): """Authenticate with OpenID Connect and get back the access token. Exchange the refresh token to get a new access token issued by the authentication server. :param session: a session object to send out HTTP requests. :type session: keystoneclient.session.Session :returns: a token data representation :rtype: :py:class:`keystoneauth1.access.AccessInfoV3` """ discovery = self._get_discovery_document(session) grant_types = discovery.get("grant_types_supported") if (grant_types and self.grant_type is not None and self.grant_type not in grant_types): raise exceptions.OidcPluginNotSupported() payload = self.get_payload(session) access_token = self._get_access_token(session, payload) response = self._get_keystone_token(session, access_token) return access.create(resp=response)
def test_basic_convert(self): token = fixture.V3Token() s = token.add_service(type='identity') s.add_standard_endpoints(public=self.PUBLIC_URL, admin=self.ADMIN_URL, internal=self.INTERNAL_URL, region=self.REGION_ONE) auth_ref = access.create(body=token) catalog_data = auth_ref.service_catalog.catalog catalog = _request._v3_to_v2_catalog(catalog_data) self.assertEqual(1, len(catalog)) service = catalog[0] self.assertEqual(1, len(service['endpoints'])) endpoints = service['endpoints'][0] self.assertEqual('identity', service['type']) self.assertEqual(4, len(endpoints)) self.assertEqual(self.PUBLIC_URL, endpoints['publicURL']) self.assertEqual(self.ADMIN_URL, endpoints['adminURL']) self.assertEqual(self.INTERNAL_URL, endpoints['internalURL']) self.assertEqual(self.REGION_ONE, endpoints['region'])
def test_service_catalog_multiple_service_types(self): token = fixture.V2Token() token.set_scope() for i in range(3): s = token.add_service('compute') s.add_endpoint(public='public-%d' % i, admin='admin-%d' % i, internal='internal-%d' % i, region='region-%d' % i) auth_ref = access.create(body=token) urls = auth_ref.service_catalog.get_urls(service_type='compute', interface='publicURL') self.assertEqual(set(['public-0', 'public-1', 'public-2']), set(urls)) urls = auth_ref.service_catalog.get_urls(service_type='compute', interface='publicURL', region_name='region-1') self.assertEqual(('public-1', ), urls)
def test_service_catalog_get_endpoints_region_names(self): auth_ref = access.create(body=self.AUTH_RESPONSE_BODY) sc = auth_ref.service_catalog endpoints = sc.get_endpoints(service_type='image', region_name='North') self.assertEqual(len(endpoints), 1) self.assertEqual(endpoints['image'][0]['publicURL'], 'https://image.north.host/v1/') endpoints = sc.get_endpoints(service_type='image', region_name='South') self.assertEqual(len(endpoints), 1) self.assertEqual(endpoints['image'][0]['publicURL'], 'https://image.south.host/v1/') endpoints = sc.get_endpoints(service_type='compute') self.assertEqual(len(endpoints['compute']), 2) endpoints = sc.get_endpoints(service_type='compute', region_name='North') self.assertEqual(len(endpoints['compute']), 2) endpoints = sc.get_endpoints(service_type='compute', region_name='West') self.assertEqual(len(endpoints['compute']), 0)
def get_unscoped_auth_ref(self, session, **kwargs): """Obtain unscoped token after authenticating with SAML IdP. :param session: :type session: keystoneauth1.session.Session """ saml_response = self._authenticate(session) # Exit if authentication failed if saml_response is False: sys.exit("Invalid username / password provided.") self._get_auth_cookie(session, saml_response) if self._cookies(session) is False: raise exceptions.AuthorizationFailure( "Session object doesn't contain a cookie, therefore you are " "not allowed to enter the Identity Provider's protected area.") resp = session.get(self.federated_token_url, authenticated=False, cookies=session.session.cookies.get_dict()) return access.create(body=resp.json(), resp=resp)
def test_building_scoped_accessinfo(self): token = fixture.V2Token() token.set_scope() s = token.add_service('identity') s.add_endpoint('http://url') role_data = token.add_role() auth_ref = access.create(body=token) self.assertIsInstance(auth_ref, access.AccessInfoV2) self.assertTrue(auth_ref.has_service_catalog()) self.assertEqual(auth_ref.auth_token, token.token_id) self.assertEqual(auth_ref.username, token.user_name) self.assertEqual(auth_ref.user_id, token.user_id) self.assertEqual(auth_ref.role_ids, [role_data['id']]) self.assertEqual(auth_ref.role_names, [role_data['name']]) self.assertEqual(auth_ref.tenant_name, token.tenant_name) self.assertEqual(auth_ref.tenant_id, token.tenant_id) self.assertEqual(auth_ref.tenant_name, auth_ref.project_name) self.assertEqual(auth_ref.tenant_id, auth_ref.project_id) self.assertIsNone(auth_ref.project_domain_id, 'default') self.assertIsNone(auth_ref.project_domain_name, 'Default') self.assertIsNone(auth_ref.user_domain_id, 'default') self.assertIsNone(auth_ref.user_domain_name, 'Default') self.assertTrue(auth_ref.project_scoped) self.assertFalse(auth_ref.domain_scoped) self.assertEqual(token.audit_id, auth_ref.audit_id) self.assertEqual(token.audit_chain_id, auth_ref.audit_chain_id)
def get_unscoped_auth_ref(self, session, **kwargs): with utils.BundleBuilder(session, self.x509_user_proxy) as p: auth_response = session.post(self.federated_token_url, authenticated=False) return access.create(auth_response)
def get_unscoped_auth_ref(self, session, **kwargs): resp = session.get(self.federated_token_url, requests_auth=_requests_auth(), authenticated=False) return access.create(body=resp.json(), resp=resp)
def test_token_without_catalog(self): token = fixture.V3Token() auth_ref = access.create(body=token) self.request.set_service_catalog_headers(auth_ref) self.assertNotIn('X-Service-Catalog', self.request.headers)
def get_unscoped_auth_ref(self, sess, **kwargs): # This would go and talk to an idp or something resp = sess.post(self.federated_token_url, authenticated=False) return access.create(resp=resp)
def test_service_catalog_without_name(self): f = fixture.V3Token(audit_chain_id=uuid.uuid4().hex) if not f.project_id: f.set_project_scope() f.add_role(name='admin') f.add_role(name='member') region = 'RegionOne' tenant = '225da22d3ce34b15877ea70b2a575f58' s = f.add_service('volume') s.add_standard_endpoints( public='http://public.com:8776/v1/%s' % tenant, internal='http://internal:8776/v1/%s' % tenant, admin='http://admin:8776/v1/%s' % tenant, region=region) s = f.add_service('image') s.add_standard_endpoints(public='http://public.com:9292/v1', internal='http://internal:9292/v1', admin='http://admin:9292/v1', region=region) s = f.add_service('compute') s.add_standard_endpoints( public='http://public.com:8774/v2/%s' % tenant, internal='http://internal:8774/v2/%s' % tenant, admin='http://admin:8774/v2/%s' % tenant, region=region) s = f.add_service('ec2') s.add_standard_endpoints( public='http://public.com:8773/services/Cloud', internal='http://internal:8773/services/Cloud', admin='http://admin:8773/services/Admin', region=region) s = f.add_service('identity') s.add_standard_endpoints(public='http://public.com:5000/v3', internal='http://internal:5000/v3', admin='http://admin:35357/v3', region=region) pr_auth_ref = access.create(body=f) pr_sc = pr_auth_ref.service_catalog # this will work because there are no service names on that token url_ref = 'http://public.com:8774/v2/225da22d3ce34b15877ea70b2a575f58' url = pr_sc.url_for(service_type='compute', service_name='NotExist', interface='public') self.assertEqual(url_ref, url) ab_auth_ref = access.create(body=self.AUTH_RESPONSE_BODY) ab_sc = ab_auth_ref.service_catalog # this won't work because there is a name and it's not this one self.assertRaises(exceptions.EndpointNotFound, ab_sc.url_for, service_type='compute', service_name='NotExist', interface='public')
def test_federated_property_standard_token(self): """Check if is_federated property returns expected value.""" token = fixture.V3Token() token.set_project_scope() auth_ref = access.create(body=token) self.assertFalse(auth_ref.is_federated)
def test_is_admin_project_false(self): token = fixture.V3Token(is_admin_project=False) auth_ref = access.create(body=token) self.assertIsInstance(auth_ref, access.AccessInfoV3) self.assertIs(False, auth_ref.is_admin_project)
def test_is_admin_project_unset(self): token = fixture.V3Token() auth_ref = access.create(body=token) self.assertIsInstance(auth_ref, access.AccessInfoV3) self.assertIs(True, auth_ref.is_admin_project)
def get_unscoped_auth_ref(self, session): """Get unscoped OpenStack token after federated authentication. This is a multi-step process including multiple HTTP requests. The federated authentication consists of: * HTTP GET request to the Identity Service (acting as a Service Provider). It's crucial to include HTTP headers indicating we are expecting SOAP message in return. Service Provider should respond with such SOAP message. This step is handed by a method ``Saml2Password_send_service_provider_request()``. * HTTP POST request to the external Identity Provider service with ECP extension enabled. The content sent is a header removed SOAP message returned from the Service Provider. It's also worth noting that ECP extension to the SAML2 doesn't define authentication method. The most popular is HttpBasicAuth with just user and password. Other possibilities could be X509 certificates or Kerberos. Upon successful authentication the user should receive a SAML2 assertion. This step is handed by a method ``Saml2Password_send_idp_saml2_authn_request(session)`` * HTTP POST request again to the Service Provider. The body of the request includes SAML2 assertion issued by a trusted Identity Provider. The request should be sent to the Service Provider consumer url specified in the SAML2 assertion. Providing the authentication was successful and both Service Provider and Identity Providers are trusted to each other, the Service Provider will issue an unscoped token with a list of groups the federated user is a member of. This step is handed by a method ``Saml2Password_send_service_provider_saml2_authn_response()`` Unscoped token example:: { "token": { "methods": [ "saml2" ], "user": { "id": "username%40example.com", "name": "*****@*****.**", "OS-FEDERATION": { "identity_provider": "ACME", "protocol": "saml2", "groups": [ {"id": "abc123"}, {"id": "bcd234"} ] } } } } :param session : a session object to send out HTTP requests. :type session: keystoneauth1.session.Session :returns: AccessInfo :rtype: :py:class:`keystoneauth1.access.AccessInfo` """ saml_authenticated = self._send_service_provider_request(session) if not saml_authenticated: self._send_idp_saml2_authn_request(session) self._send_service_provider_saml2_authn_response(session) return access.create(resp=self.authenticated_response)