Remove v2.0 assignment APIs

bp removed-as-of-queens

Change-Id: I29df48b0df39dc0a97e28a6436278734082d5ec7
This commit is contained in:
Lance Bragstad 2017-08-31 00:00:34 +00:00
parent 70ad022f70
commit 9eef17930a
9 changed files with 1 additions and 703 deletions

View File

@ -5,7 +5,7 @@ OS-KSADM admin extension
========================
Supports create, read, update, and delete (CRUD) operations for
roles, and services. Requires administrator privileges.
users. Requires administrator privileges.
Enable (Disable) user
@ -140,175 +140,3 @@ Response Example
.. literalinclude:: samples/OS-KSADM/user-show-response.json
:language: javascript
Create a role
=============
.. rest_method:: POST /v2.0/OS-KSADM/roles
Creates a role.
Normal response codes: 201
Error response codes: 413,415,405,404,403,401,400,503,409
Request Example
---------------
.. literalinclude:: samples/OS-KSADM/role-create-request.json
:language: javascript
Response Parameters
-------------------
.. rest_parameters:: parameters.yaml
- Location: Location
List all roles
==============
.. rest_method:: GET /v2.0/OS-KSADM/roles
Lists all roles.
Normal response codes: 200,203
Error response codes: 413,405,404,403,401,400,503
Response Parameters
-------------------
.. rest_parameters:: parameters.yaml
- role_links: role_links
- roles: roles
- description: role_description
- name: role_name
- id: role_id
Response Example
----------------
.. literalinclude:: samples/OS-KSADM/roles-list-response.json
:language: javascript
Show a role
===========
.. rest_method:: GET /v2.0/OS-KSADM/roles/{role_id}
Shows details for a role.
Normal response codes: 200,203
Error response codes: 413,415,405,404,403,401,400,503,409
Request
-------
.. rest_parameters:: parameters.yaml
- role_id: role_id_path
Response Parameters
-------------------
.. rest_parameters:: parameters.yaml
- Location: Location
- description: role_description
- name: role_name
- id: role_id
Response Example
----------------
.. literalinclude:: samples/OS-KSADM/role-show-response.json
:language: javascript
Show role information by name
=============================
.. rest_method:: GET /v2.0/OS-KSADM/roles/{role_name}
Shows information for a role, by name.
Normal response codes: 200,203
Error response codes: 413,415,405,404,403,401,400,503,409
Response Parameters
-------------------
.. rest_parameters:: parameters.yaml
- Location: Location
- role_name: role_name_path
- description: role_description
- name: role_name
- id: role_id
Response Example
----------------
.. literalinclude:: samples/OS-KSADM/role-show-response.json
:language: javascript
Delete a role
=============
.. rest_method:: DELETE /v2.0/OS-KSADM/roles/{role_id}
Deletes a role.
Normal response codes: 204
Error response codes: 413,415,405,404,403,401,400,503,409
Request
-------
.. rest_parameters:: parameters.yaml
- role_id: role_id_path
Grant roles to user on tenant
=============================
.. rest_method:: PUT /v2.0/tenants/{tenant_id}/users/{user_id}/roles/OS-KSADM/{role_id}
Grants a role to a user for a tenant.
Normal response codes: 201
Error response codes: 413,415,405,404,403,401,400,503,409
Request
-------
.. rest_parameters:: parameters.yaml
- user_id: user_id_path
- role_id: role_id_path
- tenant_id: tenant_id_path
Revoke role from user on tenant
===============================
.. rest_method:: DELETE /v2.0/tenants/{tenant_id}/users/{user_id}/roles/OS-KSADM/{role_id}
Revokes a role from a user for a tenant.
Normal response codes: 204
Error response codes: 413,415,405,404,403,401,400,503,409
Request
-------
.. rest_parameters:: parameters.yaml
- user_id: user_id_path
- role_id: role_id_path
- tenant_id: tenant_id_path

View File

@ -16,10 +16,8 @@
"""Workflow Logic the Assignment service."""
import functools
import uuid
from oslo_log import log
from six.moves import urllib
from keystone.assignment import schema
from keystone.common import authorization
@ -62,195 +60,6 @@ class TenantAssignment(controller.V2Controller):
}
return self.format_project_list(tenant_refs, **params)
@controller.v2_deprecated
def get_project_users(self, request, tenant_id, **kw):
self.assert_admin(request)
user_refs = []
user_ids = self.assignment_api.list_user_ids_for_project(tenant_id)
for user_id in user_ids:
try:
user_ref = self.identity_api.get_user(user_id)
except exception.UserNotFound:
# Log that user is missing and continue on.
message = ("User %(user_id)s in project %(project_id)s "
"doesn't exist.")
LOG.debug(message,
{'user_id': user_id, 'project_id': tenant_id})
else:
user_refs.append(self.v3_to_v2_user(user_ref))
return {'users': user_refs}
@dependency.requires('assignment_api', 'role_api')
class Role(controller.V2Controller):
"""The Role management APIs."""
@controller.v2_deprecated
def get_role(self, request, role_id):
self.assert_admin(request)
return {'role': self.role_api.get_role(role_id)}
@controller.v2_deprecated
def create_role(self, request, role):
validation.lazy_validate(schema.role_create_v2, role)
role = self._normalize_dict(role)
self.assert_admin(request)
if role['name'] == CONF.member_role_name:
# Use the configured member role ID when creating the configured
# member role name. This avoids the potential of creating a
# "member" role with an unexpected ID.
role_id = CONF.member_role_id
else:
role_id = uuid.uuid4().hex
role['id'] = role_id
role_ref = self.role_api.create_role(role_id,
role,
initiator=request.audit_initiator)
return {'role': role_ref}
@controller.v2_deprecated
def delete_role(self, request, role_id):
self.assert_admin(request)
self.role_api.delete_role(role_id, initiator=request.audit_initiator)
@controller.v2_deprecated
def get_roles(self, request):
self.assert_admin(request)
return {'roles': self.role_api.list_roles()}
@dependency.requires('assignment_api', 'resource_api', 'role_api')
class RoleAssignmentV2(controller.V2Controller):
"""The V2 Role APIs that are processing assignments."""
# COMPAT(essex-3)
@controller.v2_deprecated
def get_user_roles(self, request, user_id, tenant_id=None):
"""Get the roles for a user and tenant pair.
Since we're trying to ignore the idea of user-only roles we're
not implementing them in hopes that the idea will die off.
"""
self.assert_admin(request)
# NOTE(davechen): Router without project id is defined,
# but we don't plan on implementing this.
if tenant_id is None:
raise exception.NotImplemented(
message=_('User roles not supported: tenant_id required'))
roles = self.assignment_api.get_roles_for_user_and_project(
user_id, tenant_id)
return {'roles': [self.role_api.get_role(x)
for x in roles]}
@controller.v2_deprecated
def add_role_to_user(self, request, user_id, role_id, tenant_id=None):
"""Add a role to a user and tenant pair.
Since we're trying to ignore the idea of user-only roles we're
not implementing them in hopes that the idea will die off.
"""
self.assert_admin(request)
if tenant_id is None:
raise exception.NotImplemented(
message=_('User roles not supported: tenant_id required'))
self.assignment_api.add_role_to_user_and_project(
user_id, tenant_id, role_id)
role_ref = self.role_api.get_role(role_id)
return {'role': role_ref}
@controller.v2_deprecated
def remove_role_from_user(self, request, user_id, role_id, tenant_id=None):
"""Remove a role from a user and tenant pair.
Since we're trying to ignore the idea of user-only roles we're
not implementing them in hopes that the idea will die off.
"""
self.assert_admin(request)
if tenant_id is None:
raise exception.NotImplemented(
message=_('User roles not supported: tenant_id required'))
# This still has the weird legacy semantics that adding a role to
# a user also adds them to a tenant, so we must follow up on that
self.assignment_api.remove_role_from_user_and_project(
user_id, tenant_id, role_id)
# COMPAT(diablo): CRUD extension
@controller.v2_deprecated
def get_role_refs(self, request, user_id):
"""Ultimate hack to get around having to make role_refs first-class.
This will basically iterate over the various roles the user has in
all tenants the user is a member of and create fake role_refs where
the id encodes the user-tenant-role information so we can look
up the appropriate data when we need to delete them.
"""
self.assert_admin(request)
tenants = self.assignment_api.list_projects_for_user(user_id)
o = []
for tenant in tenants:
# As a v2 call, we should limit the response to those projects in
# the default domain.
if tenant['domain_id'] != CONF.identity.default_domain_id:
continue
role_ids = self.assignment_api.get_roles_for_user_and_project(
user_id, tenant['id'])
for role_id in role_ids:
ref = {'roleId': role_id,
'tenantId': tenant['id'],
'userId': user_id}
ref['id'] = urllib.parse.urlencode(ref)
o.append(ref)
return {'roles': o}
# COMPAT(diablo): CRUD extension
@controller.v2_deprecated
def create_role_ref(self, request, user_id, role):
"""Used for adding a user to a tenant.
In the legacy data model adding a user to a tenant required setting
a role.
"""
self.assert_admin(request)
# TODO(termie): for now we're ignoring the actual role
tenant_id = role.get('tenantId')
role_id = role.get('roleId')
self.assignment_api.add_role_to_user_and_project(
user_id, tenant_id, role_id)
role_ref = self.role_api.get_role(role_id)
return {'role': role_ref}
# COMPAT(diablo): CRUD extension
@controller.v2_deprecated
def delete_role_ref(self, request, user_id, role_ref_id):
"""Used for deleting a user from a tenant.
In the legacy data model removing a user from a tenant required
deleting a role.
To emulate this, we encode the tenant and role in the role_ref_id,
and if this happens to be the last role for the user-tenant pair,
we remove the user from the tenant.
"""
self.assert_admin(request)
# TODO(termie): for now we're ignoring the actual role
role_ref_ref = urllib.parse.parse_qs(role_ref_id)
tenant_id = role_ref_ref.get('tenantId')[0]
role_id = role_ref_ref.get('roleId')[0]
self.assignment_api.remove_role_from_user_and_project(
user_id, tenant_id, role_id)
@dependency.requires('assignment_api', 'resource_api')
class ProjectAssignmentV3(controller.V3Controller):

View File

@ -37,20 +37,6 @@ class Public(wsgi.ComposableRouter):
conditions=dict(method=['GET']))
class Admin(wsgi.ComposableRouter):
def add_routes(self, mapper):
# Role Operations
roles_controller = controllers.RoleAssignmentV2()
mapper.connect('/tenants/{tenant_id}/users/{user_id}/roles',
controller=roles_controller,
action='get_user_roles',
conditions=dict(method=['GET']))
mapper.connect('/users/{user_id}/roles',
controller=roles_controller,
action='get_user_roles',
conditions=dict(method=['GET']))
class Routers(wsgi.RoutersBase):
def append_v3_routers(self, mapper, routers):

View File

@ -918,33 +918,6 @@ class V2Notifications(BaseNotificationTest):
'user',
cadftaxonomy.SECURITY_ACCOUNT_USER)
def test_role(self):
token = self.get_scoped_token()
resp = self.admin_request(
method='POST',
path='/v2.0/OS-KSADM/roles',
body={
'role': {
'name': uuid.uuid4().hex,
'description': uuid.uuid4().hex,
},
},
token=token,
)
role_id = resp.result.get('role').get('id')
self._assert_initiator_data_is_set(CREATED_OPERATION,
'role',
cadftaxonomy.SECURITY_ROLE)
# test for delete role
self.admin_request(
method='DELETE',
path='/v2.0/OS-KSADM/roles/%s' % role_id,
token=token,
)
self._assert_initiator_data_is_set(DELETED_OPERATION,
'role',
cadftaxonomy.SECURITY_ROLE)
def test_project(self):
token = self.get_scoped_token()
resp = self.admin_request(

View File

@ -26,7 +26,6 @@ import six
from testtools import matchers
import webob
from keystone import assignment
from keystone import auth
from keystone.common import authorization
import keystone.conf
@ -491,38 +490,6 @@ class AuthWithToken(object):
bind = scoped_token['access']['token']['bind']
self.assertEqual('foo', bind['kerberos'])
def test_deleting_role_revokes_token(self):
role_controller = assignment.controllers.Role()
project1 = unit.new_project_ref(
domain_id=CONF.identity.default_domain_id)
self.resource_api.create_project(project1['id'], project1)
role_one = unit.new_role_ref(id=uuid.uuid4().hex)
self.role_api.create_role(role_one['id'], role_one)
self.assignment_api.add_role_to_user_and_project(
self.user_foo['id'], project1['id'], role_one['id'])
# Get a scoped token for the tenant
body_dict = _build_user_auth(
username=self.user_foo['name'],
password=self.user_foo['password'],
tenant_name=project1['name'])
token = self.controller.authenticate(self.empty_request, body_dict)
# Ensure it is valid
token_id = token['access']['token']['id']
self.controller.validate_token(self.make_request(is_admin=True),
token_id=token_id)
# Delete the role, which should invalidate the token
role_controller.delete_role(self.make_request(is_admin=True),
role_one['id'])
# Check the token is now invalid
self.assertRaises(
exception.TokenNotFound,
self.controller.validate_token,
self.make_request(is_admin=True),
token_id=token_id)
def test_deleting_role_assignment_does_not_revoke_unscoped_token(self):
admin_request = self.make_request(is_admin=True)
@ -666,9 +633,6 @@ class FernetAuthWithToken(AuthWithToken, AuthTest):
self.request_with_remote_user,
body_dict)
def test_deleting_role_revokes_token(self):
self.skip_test_overrides('Fernet with v2.0 and revocation is broken')
class AuthWithPasswordCredentials(AuthTest):
def test_auth_invalid_user(self):

View File

@ -263,24 +263,6 @@ class CoreApiTests(object):
token=token)
self.assertValidTenantResponse(r)
def test_get_user_roles_with_tenant(self):
token = self.get_scoped_token()
r = self.admin_request(
path='/v2.0/tenants/%(tenant_id)s/users/%(user_id)s/roles' % {
'tenant_id': self.tenant_bar['id'],
'user_id': self.user_foo['id'],
},
token=token)
self.assertValidRoleListResponse(r)
def test_get_user_roles_without_tenant(self):
token = self.get_scoped_token()
self.admin_request(
path='/v2.0/users/%(user_id)s/roles' % {
'user_id': self.user_foo['id'],
},
token=token, expected_status=http_client.NOT_IMPLEMENTED)
def test_get_user(self):
token = self.get_scoped_token()
r = self.admin_request(
@ -430,86 +412,6 @@ class CoreApiTests(object):
"""
raise NotImplementedError()
def test_update_user_tenant(self):
token = self.get_scoped_token()
# Create a new user
r = self.admin_request(
method='POST',
path='/v2.0/users',
body={
'user': {
'name': uuid.uuid4().hex,
'password': uuid.uuid4().hex,
'tenantId': self.tenant_bar['id'],
'enabled': True,
},
},
token=token,
expected_status=http_client.OK)
user_id = self._get_user_id(r.result)
# Check if member_role is in tenant_bar
r = self.admin_request(
path='/v2.0/tenants/%(project_id)s/users/%(user_id)s/roles' % {
'project_id': self.tenant_bar['id'],
'user_id': user_id
},
token=token,
expected_status=http_client.OK)
self.assertEqual(CONF.member_role_name, self._get_role_name(r.result))
# Create a new tenant
r = self.admin_request(
method='POST',
path='/v2.0/tenants',
body={
'tenant': {
'name': 'test_update_user',
'description': 'A description ...',
'enabled': True,
},
},
token=token,
expected_status=http_client.OK)
project_id = self._get_project_id(r.result)
# Update user's tenant
r = self.admin_request(
method='PUT',
path='/v2.0/users/%(user_id)s' % {
'user_id': user_id,
},
body={
'user': {
'tenantId': project_id,
},
},
token=token,
expected_status=http_client.OK)
# 'member_role' should be in new_tenant
r = self.admin_request(
path='/v2.0/tenants/%(project_id)s/users/%(user_id)s/roles' % {
'project_id': project_id,
'user_id': user_id
},
token=token,
expected_status=http_client.OK)
self.assertEqual('_member_', self._get_role_name(r.result))
# 'member_role' should not be in tenant_bar any more
r = self.admin_request(
path='/v2.0/tenants/%(project_id)s/users/%(user_id)s/roles' % {
'project_id': self.tenant_bar['id'],
'user_id': user_id
},
token=token,
expected_status=http_client.OK)
self.assertNoRoles(r.result)
def test_update_user_with_invalid_tenant(self):
token = self.get_scoped_token()
@ -575,60 +477,6 @@ class CoreApiTests(object):
token=token,
expected_status=http_client.NOT_FOUND)
def test_update_user_with_old_tenant(self):
token = self.get_scoped_token()
# Create a new user
r = self.admin_request(
method='POST',
path='/v2.0/users',
body={
'user': {
'name': uuid.uuid4().hex,
'password': uuid.uuid4().hex,
'tenantId': self.tenant_bar['id'],
'enabled': True,
},
},
token=token,
expected_status=http_client.OK)
user_id = self._get_user_id(r.result)
# Check if member_role is in tenant_bar
r = self.admin_request(
path='/v2.0/tenants/%(project_id)s/users/%(user_id)s/roles' % {
'project_id': self.tenant_bar['id'],
'user_id': user_id
},
token=token,
expected_status=http_client.OK)
self.assertEqual(CONF.member_role_name, self._get_role_name(r.result))
# Update user's tenant with old tenant id
r = self.admin_request(
method='PUT',
path='/v2.0/users/%(user_id)s' % {
'user_id': user_id,
},
body={
'user': {
'tenantId': self.tenant_bar['id'],
},
},
token=token,
expected_status=http_client.OK)
# 'member_role' should still be in tenant_bar
r = self.admin_request(
path='/v2.0/tenants/%(project_id)s/users/%(user_id)s/roles' % {
'project_id': self.tenant_bar['id'],
'user_id': user_id
},
token=token,
expected_status=http_client.OK)
self.assertEqual('_member_', self._get_role_name(r.result))
def test_authenticating_a_user_with_no_password(self):
token = self.get_scoped_token()
@ -1137,19 +985,6 @@ class V2TestCase(object):
def get_user_attribute_from_response(self, r, attribute_name):
return r.result['user'][attribute_name]
def test_user_role_list_requires_auth(self):
"""User role list return unauthorized without an X-Auth-Token."""
# values here don't matter because it will be unauthorized before
# they're checked (bug 1006815).
path = '/v2.0/tenants/%(tenant_id)s/users/%(user_id)s/roles' % {
'tenant_id': uuid.uuid4().hex,
'user_id': uuid.uuid4().hex,
}
r = self.admin_request(path=path,
expected_status=http_client.UNAUTHORIZED)
self.assertValidErrorResponse(r)
def test_fetch_revocation_list_nonadmin_fails(self):
self.admin_request(
method='GET',

View File

@ -39,37 +39,6 @@ class TenantTestCase(unit.TestCase):
self.tenant_controller = resource_controllers.Tenant()
self.assignment_tenant_controller = (
assignment_controllers.TenantAssignment())
self.assignment_role_controller = (
assignment_controllers.RoleAssignmentV2())
def test_get_project_users_no_user(self):
"""Test the user's existence for get_project_users.
When a user that's not known to `identity` has a role on a project,
then `get_project_users` just skips that user.
"""
project_id = self.tenant_bar['id']
orig_project_users = (
self.assignment_tenant_controller.get_project_users(
self.make_request(is_admin=True), project_id))
# Assign a role to a user that doesn't exist to the `bar` project.
user_id = uuid.uuid4().hex
self.assignment_role_controller.add_role_to_user(
self.make_request(is_admin=True), user_id,
self.role_other['id'], project_id)
new_project_users = (
self.assignment_tenant_controller.get_project_users(
self.make_request(is_admin=True), project_id))
# The new user isn't included in the result, so no change.
# asserting that the expected values appear in the list,
# without asserting the order of the results
self.assertEqual(sorted(orig_project_users), sorted(new_project_users))
def test_list_projects_default_domain(self):
"""Test that list projects only returns those in the default domain."""

View File

@ -50,8 +50,6 @@ class Router(wsgi.ComposableRouter):
assignment_tenant_controller = (
assignment.controllers.TenantAssignment())
user_controller = identity.controllers.User()
role_controller = assignment.controllers.Role()
assignment_role_controller = assignment.controllers.RoleAssignmentV2()
# Tenant Operations
mapper.connect(
@ -133,66 +131,3 @@ class Router(wsgi.ComposableRouter):
controller=user_controller,
action='set_user_enabled',
conditions=dict(method=['PUT']))
# User Roles
mapper.connect(
'/users/{user_id}/roles/OS-KSADM/{role_id}',
controller=assignment_role_controller,
action='add_role_to_user',
conditions=dict(method=['PUT']))
mapper.connect(
'/users/{user_id}/roles/OS-KSADM/{role_id}',
controller=assignment_role_controller,
action='remove_role_from_user',
conditions=dict(method=['DELETE']))
# COMPAT(diablo): User Roles
mapper.connect(
'/users/{user_id}/roleRefs',
controller=assignment_role_controller,
action='get_role_refs',
conditions=dict(method=['GET']))
mapper.connect(
'/users/{user_id}/roleRefs',
controller=assignment_role_controller,
action='create_role_ref',
conditions=dict(method=['POST']))
mapper.connect(
'/users/{user_id}/roleRefs/{role_ref_id}',
controller=assignment_role_controller,
action='delete_role_ref',
conditions=dict(method=['DELETE']))
# User-Tenant Roles
mapper.connect(
'/tenants/{tenant_id}/users/{user_id}/roles/OS-KSADM/{role_id}',
controller=assignment_role_controller,
action='add_role_to_user',
conditions=dict(method=['PUT']))
mapper.connect(
'/tenants/{tenant_id}/users/{user_id}/roles/OS-KSADM/{role_id}',
controller=assignment_role_controller,
action='remove_role_from_user',
conditions=dict(method=['DELETE']))
# Role Operations
mapper.connect(
'/OS-KSADM/roles',
controller=role_controller,
action='create_role',
conditions=dict(method=['POST']))
mapper.connect(
'/OS-KSADM/roles',
controller=role_controller,
action='get_roles',
conditions=dict(method=['GET']))
mapper.connect(
'/OS-KSADM/roles/{role_id}',
controller=role_controller,
action='get_role',
conditions=dict(method=['GET']))
mapper.connect(
'/OS-KSADM/roles/{role_id}',
controller=role_controller,
action='delete_role',
conditions=dict(method=['DELETE']))

View File

@ -96,7 +96,6 @@ def admin_app_factory(global_conf, **local_conf):
controllers.register_version('v2.0')
return wsgi.ComposingRouter(routes.Mapper(),
[identity_routers.Admin(),
assignment_routers.Admin(),
token_routers.Router(),
resource_routers.Admin(),
admin_crud.Router(),