def check_credential_exists(ec2credential, credential_table, session): credential = session.query(credential_table).filter_by( id=utils.hash_access_key(ec2credential.access)).first() if credential is None: return False blob = utils.get_blob_from_credential(credential) # check if credential with same access key but different # secret key already exists in credential table. # If exists raise an exception if blob['secret'] != ec2credential.secret: msg = _('Credential %(access)s already exists with different secret' ' in %(table)s table') message = msg % {'access': ec2credential.access, 'table': credential_table.name} raise exception.Conflict(type='credential', details=message) # check if credential with same access and secret key but # associated with a different project exists. If exists raise # an exception elif credential.project_id is not None and ( credential.project_id != ec2credential.tenant_id): msg = _('Credential %(access)s already exists with different project' ' in %(table)s table') message = msg % {'access': ec2credential.access, 'table': credential_table.name} raise exception.Conflict(type='credential', details=message) # if credential with same access and secret key and not associated # with any projects already exists in the credential table, then # return true. else: return True
def create_credential(self, context, user_id, tenant_id): """Create a secret/access pair for use with ec2 style auth. Generates a new set of credentials that map the user/tenant pair. :param context: standard context :param user_id: id of user :param tenant_id: id of tenant :returns: credential: dict of ec2 credential """ if not self._is_admin(context): self._assert_identity(context, user_id) self._assert_valid_user_id(user_id) self._assert_valid_project_id(tenant_id) trust_id = self._get_trust_id_for_request(context) blob = {'access': uuid.uuid4().hex, 'secret': uuid.uuid4().hex, 'trust_id': trust_id} credential_id = utils.hash_access_key(blob['access']) cred_ref = {'user_id': user_id, 'project_id': tenant_id, 'blob': jsonutils.dumps(blob), 'id': credential_id, 'type': 'ec2'} self.credential_api.create_credential(credential_id, cred_ref) return {'credential': self._convert_v3_to_ec2_credential(cred_ref)}
def post(self, user_id): """Create EC2 Credential for user. POST /v3/users/{user_id}/credentials/OS-EC2 """ ENFORCER.enforce_call(action='identity:ec2_create_credential') PROVIDERS.identity_api.get_user(user_id) tenant_id = self.request_body_json.get('tenant_id') PROVIDERS.resource_api.get_project(tenant_id) blob = dict( access=uuid.uuid4().hex, secret=uuid.uuid4().hex, trust_id=self.oslo_context.trust_id ) credential_id = utils.hash_access_key(blob['access']) cred_data = dict( user_id=user_id, project_id=tenant_id, blob=jsonutils.dumps(blob), id=credential_id, type=CRED_TYPE_EC2 ) PROVIDERS.credential_api.create_credential(credential_id, cred_data) ref = _convert_v3_to_ec2_credential(cred_data) return self.wrap_member(ref), http_client.CREATED
def create_credential(self, context, user_id, tenant_id): """Create a secret/access pair for use with ec2 style auth. Generates a new set of credentials that map the user/tenant pair. :param context: standard context :param user_id: id of user :param tenant_id: id of tenant :returns: credential: dict of ec2 credential """ if not self._is_admin(context): self._assert_identity(context, user_id) self._assert_valid_user_id(user_id) self._assert_valid_project_id(tenant_id) blob = {'access': uuid.uuid4().hex, 'secret': uuid.uuid4().hex} credential_id = utils.hash_access_key(blob['access']) cred_ref = {'user_id': user_id, 'project_id': tenant_id, 'blob': blob, 'id': credential_id, 'type': 'ec2'} self.credential_api.create_credential(credential_id, cred_ref) return {'credential': self._convert_v3_to_ec2_credential(cred_ref)}
def create_credential(self, context, user_id, tenant_id): """Create a secret/access pair for use with ec2 style auth. Generates a new set of credentials that map the user/tenant pair. :param context: standard context :param user_id: id of user :param tenant_id: id of tenant :returns: credential: dict of ec2 credential """ self.identity_api.get_user(user_id) self.resource_api.get_project(tenant_id) trust_id = self._get_trust_id_for_request(context) blob = {"access": uuid.uuid4().hex, "secret": uuid.uuid4().hex, "trust_id": trust_id} credential_id = utils.hash_access_key(blob["access"]) cred_ref = { "user_id": user_id, "project_id": tenant_id, "blob": jsonutils.dumps(blob), "id": credential_id, "type": "ec2", } self.credential_api.create_credential(credential_id, cred_ref) return {"credential": self._convert_v3_to_ec2_credential(cred_ref)}
def create_credential(self, context, user_id, tenant_id): """Create a secret/access pair for use with ec2 style auth. Generates a new set of credentials that map the user/tenant pair. :param context: standard context :param user_id: id of user :param tenant_id: id of tenant :returns: credential: dict of ec2 credential """ self.identity_api.get_user(user_id) self.assignment_api.get_project(tenant_id) trust_id = self._get_trust_id_for_request(context) blob = {'access': uuid.uuid4().hex, 'secret': uuid.uuid4().hex, 'trust_id': trust_id} credential_id = utils.hash_access_key(blob['access']) cred_ref = {'user_id': user_id, 'project_id': tenant_id, 'blob': jsonutils.dumps(blob), 'id': credential_id, 'type': 'ec2'} self.credential_api.create_credential(credential_id, cred_ref) return {'credential': self._convert_v3_to_ec2_credential(cred_ref)}
def create_credential(self, request, user_id, tenant_id): """Create a secret/access pair for use with ec2 style auth. Generates a new set of credentials that map the user/tenant pair. :param request: current request :param user_id: id of user :param tenant_id: id of tenant :returns: credential: dict of ec2 credential """ self.identity_api.get_user(user_id) self.resource_api.get_project(tenant_id) blob = { 'access': uuid.uuid4().hex, 'secret': uuid.uuid4().hex, 'trust_id': request.context.trust_id } credential_id = utils.hash_access_key(blob['access']) cred_ref = { 'user_id': user_id, 'project_id': tenant_id, 'blob': jsonutils.dumps(blob), 'id': credential_id, 'type': CRED_TYPE_EC2 } self.credential_api.create_credential(credential_id, cred_ref) return {'credential': self._convert_v3_to_ec2_credential(cred_ref)}
def check_credential_exists(ec2credential, credential_table, session): credential = session.query(credential_table).filter_by( id=utils.hash_access_key(ec2credential.access)).first() if credential is None: return False blob = utils.get_blob_from_credential(credential) # check if credential with same access key but different # secret key already exists in credential table. # If exists raise an exception if blob['secret'] != ec2credential.secret: msg = _('Credential %(access)s already exists with different secret' ' in %(table)s table') message = msg % { 'access': ec2credential.access, 'table': credential_table.name } raise exception.Conflict(type='credential', details=message) # check if credential with same access and secret key but # associated with a different project exists. If exists raise # an exception elif credential.project_id is not None and (credential.project_id != ec2credential.tenant_id): msg = _('Credential %(access)s already exists with different project' ' in %(table)s table') message = msg % { 'access': ec2credential.access, 'table': credential_table.name } raise exception.Conflict(type='credential', details=message) # if credential with same access and secret key and not associated # with any projects already exists in the credential table, then # return true. else: return True
def _build_enforcer_target_data_owner_and_user_id_match(): ref = {} if flask.request.view_args: credential_id = flask.request.view_args.get('credential_id') if credential_id is not None: hashed_id = utils.hash_access_key(credential_id) ref['credential'] = PROVIDERS.credential_api.get_credential( hashed_id) return ref
def test_ec2_cannot_get_non_ec2_credential(self): access_key = uuid.uuid4().hex cred_id = utils.hash_access_key(access_key) non_ec2_cred = unit.new_credential_ref(user_id=self.user_id, project_id=self.project_id) non_ec2_cred["id"] = cred_id self.credential_api.create_credential(cred_id, non_ec2_cred) uri = "/".join([self._get_ec2_cred_uri(), access_key]) # if access_key is not found, ec2 controller raises Unauthorized # exception self.get(uri, expected_status=http_client.UNAUTHORIZED)
def test_ec2_cannot_get_non_ec2_credential(self): access_key = uuid.uuid4().hex cred_id = utils.hash_access_key(access_key) non_ec2_cred = unit.new_credential_ref(user_id=self.user_id, project_id=self.project_id) non_ec2_cred['id'] = cred_id self.credential_api.create_credential(cred_id, non_ec2_cred) uri = '/'.join([self._get_ec2_cred_uri(), access_key]) # if access_key is not found, ec2 controller raises Unauthorized # exception self.get(uri, expected_status=http_client.UNAUTHORIZED)
def _assert_owner(self, user_id, credential_id): """Ensure the provided user owns the credential. :param user_id: expected credential owner :param credential_id: id of credential object :raises keystone.exception.Forbidden: on failure """ ec2_credential_id = utils.hash_access_key(credential_id) cred_ref = self.credential_api.get_credential(ec2_credential_id) if user_id != cred_ref['user_id']: raise exception.Forbidden(_('Credential belongs to another user'))
def _get_credentials(self, credential_id): """Return credentials from an ID. :param credential_id: id of credential :raises exception.Unauthorized: when credential id is invalid :returns: credential: dict of ec2 credential. """ ec2_credential_id = utils.hash_access_key(credential_id) creds = self.credential_api.get_credential(ec2_credential_id) if not creds: raise exception.Unauthorized(message='EC2 access key not found.') return self._convert_v3_to_ec2_credential(creds)
def _assert_owner(self, user_id, credential_id): """Ensure the provided user owns the credential. :param user_id: expected credential owner :param credential_id: id of credential object :raises exception.Forbidden: on failure """ ec2_credential_id = utils.hash_access_key(credential_id) cred_ref = self.credential_api.get_credential(ec2_credential_id) if user_id != cred_ref['user_id']: raise exception.Forbidden(_('Credential belongs to another user'))
def get(self, user_id, credential_id): """Get a specific EC2 credential. GET/HEAD /users/{user_id}/credentials/OS-EC2/{credential_id} """ func = _build_enforcer_target_data_owner_and_user_id_match ENFORCER.enforce_call(action='identity:ec2_get_credential', build_target=func) PROVIDERS.identity_api.get_user(user_id) ec2_cred_id = utils.hash_access_key(credential_id) cred_data = self._get_cred_data(ec2_cred_id) return self.wrap_member(cred_data)
def delete_credential(self, user_id, credential_id): """Delete a user's access/secret pair. Used to revoke a user's access/secret pair :param user_id: id of user :param credential_id: access key for credentials :returns: bool: success """ self.identity_api.get_user(user_id) self._get_credentials(credential_id) ec2_credential_id = utils.hash_access_key(credential_id) return self.credential_api.delete_credential(ec2_credential_id)
def _get_credentials(self, credential_id): """Return credentials from an ID. :param credential_id: id of credential :raises keystone.exception.Unauthorized: when credential id is invalid or when the credential type is not ec2 :returns: credential: dict of ec2 credential. """ ec2_credential_id = utils.hash_access_key(credential_id) cred = self.credential_api.get_credential(ec2_credential_id) if not cred or cred["type"] != CRED_TYPE_EC2: raise exception.Unauthorized(message=_("EC2 access key not found.")) return self._convert_v3_to_ec2_credential(cred)
def delete(self, user_id, credential_id): """Delete a specific EC2 credential. DELETE /users/{user_id}/credentials/OS-EC2/{credential_id} """ func = _build_enforcer_target_data_owner_and_user_id_match ENFORCER.enforce_call(action='identity:ec2_delete_credential', build_target=func) PROVIDERS.identity_api.get_user(user_id) ec2_cred_id = utils.hash_access_key(credential_id) self._get_cred_data(ec2_cred_id) PROVIDERS.credential_api.delete_credential(ec2_cred_id) return None, http_client.NO_CONTENT
def get(self, user_id, credential_id): """Get a specific EC2 credential. GET/HEAD /users/{user_id}/credentials/OS-EC2/{credential_id} """ func = _build_enforcer_target_data_owner_and_user_id_match ENFORCER.enforce_call( action='identity:ec2_get_credential', build_target=func) PROVIDERS.identity_api.get_user(user_id) ec2_cred_id = utils.hash_access_key(credential_id) cred_data = self._get_cred_data(ec2_cred_id) return self.wrap_member(cred_data)
def _check_credential_owner_and_user_id_match(self, context, prep_info, user_id, credential_id): # NOTE(morganfainberg): this method needs to capture the arguments of # the method that is decorated with @controller.protected() (with # exception of the first argument ('context') since the protected # method passes in *args, **kwargs. In this case, it is easier to see # the expected input if the argspec is `user_id` and `credential_id` # explicitly (matching the :class:`.ec2_delete_credential()` method # below). ref = {} credential_id = utils.hash_access_key(credential_id) ref["credential"] = self.credential_api.get_credential(credential_id) # NOTE(morganfainberg): policy_api is required for this # check_protection to properly be able to perform policy enforcement. self.check_protection(context, prep_info, ref)
def _get_credentials(self, credential_id): """Return credentials from an ID. :param credential_id: id of credential :raises keystone.exception.Unauthorized: when credential id is invalid or when the credential type is not ec2 :returns: credential: dict of ec2 credential. """ ec2_credential_id = utils.hash_access_key(credential_id) cred = self.credential_api.get_credential(ec2_credential_id) if not cred or cred['type'] != CRED_TYPE_EC2: raise exception.Unauthorized( message=_('EC2 access key not found.')) return self._convert_v3_to_ec2_credential(cred)
def test_ec2_cannot_get_non_ec2_credential(self): access_key = uuid.uuid4().hex cred_id = utils.hash_access_key(access_key) non_ec2_cred = unit.new_credential_ref( user_id=self.user_id, project_id=self.project_id) non_ec2_cred['id'] = cred_id self.credential_api.create_credential(cred_id, non_ec2_cred) # if access_key is not found, ec2 controller raises Unauthorized # exception path = '/'.join([self._get_ec2_cred_uri(), access_key]) self.public_request(method='GET', token=self.get_scoped_token(), path=path, expected_status=http_client.UNAUTHORIZED)
def _check_credential_owner_and_user_id_match(self, request, prep_info, user_id, credential_id): # NOTE(morganfainberg): this method needs to capture the arguments of # the method that is decorated with @controller.protected() (with # exception of the first argument ('context') since the protected # method passes in *args, **kwargs. In this case, it is easier to see # the expected input if the argspec is `user_id` and `credential_id` # explicitly (matching the :class:`.ec2_delete_credential()` method # below). ref = {} credential_id = utils.hash_access_key(credential_id) ref['credential'] = self.credential_api.get_credential(credential_id) # NOTE(morganfainberg): policy_api is required for this # check_protection to properly be able to perform policy enforcement. self.check_protection(request, prep_info, ref)
def test_ec2_cannot_get_non_ec2_credential(self): access_key = uuid.uuid4().hex cred_id = utils.hash_access_key(access_key) non_ec2_cred = unit.new_credential_ref(user_id=self.user_id, project_id=self.project_id) non_ec2_cred['id'] = cred_id PROVIDERS.credential_api.create_credential(cred_id, non_ec2_cred) # if access_key is not found, ec2 controller raises Unauthorized # exception path = '/'.join([self._get_ec2_cred_uri(), access_key]) self.public_request(method='GET', token=self.get_scoped_token(), path=path, expected_status=http_client.UNAUTHORIZED)
def _migrate_ec2(self): for x in self._data["credentials"]: blob = {"access": x["key"], "secret": x["secret"]} credential_id = utils.hash_access_key(blob["access"]) new_dict = { "user_id": x["user_id"], "blob": json.dumps(blob), "project_id": x["tenant_id"], "id": credential_id, "type": "ec2", } try: self.ec2_driver.create_credential(credential_id, new_dict) except exc.IntegrityError: LOG.exception(_("Cannot migrate EC2 credential: %s") % x)
def _assert_owner(self, user_id, credential_id): """Ensure the provided user owns the credential. :param user_id: expected credential owner :param credential_id: id of credential object :raises exception.Forbidden: on failure """ ec2_credential_id = utils.hash_access_key(credential_id) cred_ref = self.credential_api.get_credential(ec2_credential_id) if user_id != cred_ref['user_id']: raise exception.Forbidden(_('Credential belongs to another user')) #class AccessKeyController(identity.controllers.UserV3): #@controller.protected() #def reset_access_key(self, context, user_id): '''token_id = context.get('token_id')
def delete_credential(self, context, user_id, credential_id): """Delete a user's access/secret pair. Used to revoke a user's access/secret pair :param context: standard context :param user_id: id of user :param credential_id: access key for credentials :returns: bool: success """ if not self._is_admin(context): self._assert_identity(context, user_id) self._assert_owner(user_id, credential_id) self._assert_valid_user_id(user_id) self._get_credentials(credential_id) ec2_credential_id = utils.hash_access_key(credential_id) return self.credential_api.delete_credential(ec2_credential_id)
def _create_ec2_creds(ec2_api, assignment_api, ec2_creds, user_map): for ec2_cred in ec2_creds: user_id = user_map[ec2_cred['user_id']] for tenant_id in assignment_api.get_projects_for_user(user_id): blob = { 'access': '%s:%s' % (tenant_id, ec2_cred['access_key']), 'secret': ec2_cred['secret_key'], } credential_id = utils.hash_access_key(blob['access']) cred_dict = { 'user_id': user_id, 'blob': json.dumps(blob), 'project_id': tenant_id, 'id': credential_id, 'type': 'ec2', } LOG.debug(_( 'Creating ec2 cred for user %(user_id)s and tenant ' '%(tenant_id)s') % { 'user_id': user_id, 'tenant_id': tenant_id}) ec2_api.create_credential(credential_id, cred_dict)
def create_credential(self, context, user_id, tenant_id): """Create a secret/access pair for use with ec2 style auth. Generates a new set of credentials that map the user/tenant pair. :param context: standard context :param user_id: id of user :param tenant_id: id of tenant :returns: credential: dict of ec2 credential """ if not self._is_admin(context): self._assert_identity(context, user_id) self._assert_valid_user_id(user_id) self._assert_valid_project_id(tenant_id) trust_id = self._context_trust_id(context) blob = {"access": uuid.uuid4().hex, "secret": uuid.uuid4().hex, "trust_id": trust_id} credential_id = utils.hash_access_key(blob["access"]) cred_ref = {"user_id": user_id, "project_id": tenant_id, "blob": blob, "id": credential_id, "type": "ec2"} self.credential_api.create_credential(credential_id, cred_ref) return {"credential": self._convert_v3_to_ec2_credential(cred_ref)}
def create_credential(self, request, user_id, tenant_id): """Create a secret/access pair for use with ec2 style auth. Generates a new set of credentials that map the user/tenant pair. :param request: current request :param user_id: id of user :param tenant_id: id of tenant :returns: credential: dict of ec2 credential """ self.identity_api.get_user(user_id) self.resource_api.get_project(tenant_id) blob = {'access': uuid.uuid4().hex, 'secret': uuid.uuid4().hex, 'trust_id': request.context.trust_id} credential_id = utils.hash_access_key(blob['access']) cred_ref = {'user_id': user_id, 'project_id': tenant_id, 'blob': jsonutils.dumps(blob), 'id': credential_id, 'type': CRED_TYPE_EC2} self.credential_api.create_credential(credential_id, cred_ref) return {'credential': self._convert_v3_to_ec2_credential(cred_ref)}
def handle_authenticate(self): # TODO(morgan): convert this dirty check to JSON Schema validation # this mirrors the previous behavior of the webob system where an # empty request body for s3 and ec2 tokens would result in a BAD # REQUEST. Almost all other APIs use JSON Schema and therefore would # catch this early on. S3 and EC2 did not ever get json schema # implemented for them. if not self.request_body_json: msg = _('request must include a request body') raise ks_exceptions.ValidationError(msg) # NOTE(morgan): THIS IS SLOPPY! Apparently... keystone passed values # as "credential" and "credentials" in into the s3/ec2 authenticate # methods. There is no reason the multiple names should have worked # except that we totally did something wonky in the past... so now # there are 2 dirty "acceptable" body hacks for compatibility.... # Try "credentials" then "credential" and THEN ec2Credentials. Final # default is {} credentials = ( self.request_body_json.get('credentials') or self.request_body_json.get('credential') or self.request_body_json.get('ec2Credentials') ) if not credentials: credentials = {} if 'access' not in credentials: raise ks_exceptions.Unauthorized(_('EC2 Signature not supplied')) # Load the credential from the backend credential_id = utils.hash_access_key(credentials['access']) cred = PROVIDERS.credential_api.get_credential(credential_id) if not cred or cred['type'] != CRED_TYPE_EC2: raise ks_exceptions.Unauthorized(_('EC2 access key not found.')) # load from json if needed try: loaded = jsonutils.loads(cred['blob']) except TypeError: loaded = cred['blob'] # Convert to the legacy format cred_data = dict( user_id=cred.get('user_id'), project_id=cred.get('project_id'), access=loaded.get('access'), secret=loaded.get('secret'), trust_id=loaded.get('trust_id') ) # validate the signature self._check_signature(cred_data, credentials) project_ref = PROVIDERS.resource_api.get_project( cred_data['project_id']) user_ref = PROVIDERS.identity_api.get_user(cred_data['user_id']) # validate that the auth info is valid and nothing is disabled try: PROVIDERS.identity_api.assert_user_enabled( user_id=user_ref['id'], user=user_ref) PROVIDERS.resource_api.assert_project_enabled( project_id=project_ref['id'], project=project_ref) except AssertionError as e: six.reraise( ks_exceptions.Unauthorized, ks_exceptions.Unauthorized(e), sys.exc_info()[2]) roles = PROVIDERS.assignment_api.get_roles_for_user_and_project( user_ref['id'], project_ref['id']) if not roles: raise ks_exceptions.Unauthorized(_('User not valid for project.')) for r_id in roles: # Assert all roles exist. PROVIDERS.role_api.get_role(r_id) method_names = ['ec2credential'] token = PROVIDERS.token_provider_api.issue_token( user_id=user_ref['id'], method_names=method_names, project_id=project_ref['id']) return token
def handle_authenticate(self): # TODO(morgan): convert this dirty check to JSON Schema validation # this mirrors the previous behavior of the webob system where an # empty request body for s3 and ec2 tokens would result in a BAD # REQUEST. Almost all other APIs use JSON Schema and therefore would # catch this early on. S3 and EC2 did not ever get json schema # implemented for them. if not self.request_body_json: msg = _('request must include a request body') raise ks_exceptions.ValidationError(msg) # NOTE(morgan): THIS IS SLOPPY! Apparently... keystone passed values # as "credential" and "credentials" in into the s3/ec2 authenticate # methods. There is no reason the multiple names should have worked # except that we totally did something wonky in the past... so now # there are 2 dirty "acceptable" body hacks for compatibility.... # Try "credentials" then "credential" and THEN ec2Credentials. Final # default is {} credentials = (self.request_body_json.get('credentials') or self.request_body_json.get('credential') or self.request_body_json.get('ec2Credentials')) if not credentials: credentials = {} if 'access' not in credentials: raise ks_exceptions.Unauthorized(_('EC2 Signature not supplied')) # Load the credential from the backend credential_id = utils.hash_access_key(credentials['access']) cred = PROVIDERS.credential_api.get_credential(credential_id) if not cred or cred['type'] != CRED_TYPE_EC2: raise ks_exceptions.Unauthorized(_('EC2 access key not found.')) # load from json if needed try: loaded = jsonutils.loads(cred['blob']) except TypeError: loaded = cred['blob'] # Convert to the legacy format cred_data = dict(user_id=cred.get('user_id'), project_id=cred.get('project_id'), access=loaded.get('access'), secret=loaded.get('secret'), trust_id=loaded.get('trust_id')) # validate the signature self._check_signature(cred_data, credentials) project_ref = PROVIDERS.resource_api.get_project( cred_data['project_id']) user_ref = PROVIDERS.identity_api.get_user(cred_data['user_id']) # validate that the auth info is valid and nothing is disabled try: PROVIDERS.identity_api.assert_user_enabled(user_id=user_ref['id'], user=user_ref) PROVIDERS.resource_api.assert_project_enabled( project_id=project_ref['id'], project=project_ref) except AssertionError as e: raise ks_exceptions.Unauthorized from e self._check_timestamp(credentials) roles = PROVIDERS.assignment_api.get_roles_for_user_and_project( user_ref['id'], project_ref['id']) if not roles: raise ks_exceptions.Unauthorized(_('User not valid for project.')) for r_id in roles: # Assert all roles exist. PROVIDERS.role_api.get_role(r_id) method_names = ['ec2credential'] token = PROVIDERS.token_provider_api.issue_token( user_id=user_ref['id'], method_names=method_names, project_id=project_ref['id']) return token