Wrap v3 API with RBAC (bug 1023943)

Change-Id: Ie77be83054ea88bb0860260e1750196ac5ded650
This commit is contained in:
Dolph Mathews 2012-08-30 05:37:26 -05:00
parent 715a17b71d
commit 84cd8ff7f3
5 changed files with 151 additions and 82 deletions

View File

@ -1,3 +1,57 @@
{
"admin_required": [["role:admin"], ["is_admin:1"]]
"admin_required": [["role:admin"], ["is_admin:1"]],
"identity:get_service": [["rule:admin_required"]],
"identity:list_services": [["rule:admin_required"]],
"identity:create_service": [["rule:admin_required"]],
"identity:update_service": [["rule:admin_required"]],
"identity:delete_service": [["rule:admin_required"]],
"identity:get_endpoint": [["rule:admin_required"]],
"identity:list_endpoints": [["rule:admin_required"]],
"identity:create_endpoint": [["rule:admin_required"]],
"identity:update_endpoint": [["rule:admin_required"]],
"identity:delete_endpoint": [["rule:admin_required"]],
"identity:get_domain": [["rule:admin_required"]],
"identity:list_domains": [["rule:admin_required"]],
"identity:create_domain": [["rule:admin_required"]],
"identity:update_domain": [["rule:admin_required"]],
"identity:delete_domain": [["rule:admin_required"]],
"identity:get_project": [["rule:admin_required"]],
"identity:list_projects": [["rule:admin_required"]],
"identity:list_user_projects": [["rule:admin_required"], ["user_id:%(user_id)s"]],
"identity:create_project": [["rule:admin_required"]],
"identity:update_project": [["rule:admin_required"]],
"identity:delete_project": [["rule:admin_required"]],
"identity:get_user": [["rule:admin_required"]],
"identity:list_users": [["rule:admin_required"]],
"identity:create_user": [["rule:admin_required"]],
"identity:update_user": [["rule:admin_required"]],
"identity:delete_user": [["rule:admin_required"]],
"identity:get_credential": [["rule:admin_required"]],
"identity:list_credentials": [["rule:admin_required"]],
"identity:create_credential": [["rule:admin_required"]],
"identity:update_credential": [["rule:admin_required"]],
"identity:delete_credential": [["rule:admin_required"]],
"identity:get_role": [["rule:admin_required"]],
"identity:list_roles": [["rule:admin_required"]],
"identity:create_role": [["rule:admin_required"]],
"identity:update_roles": [["rule:admin_required"]],
"identity:delete_roles": [["rule:admin_required"]],
"identity:check_grant": [["rule:admin_required"]],
"identity:list_grants": [["rule:admin_required"]],
"identity:create_grant": [["rule:admin_required"]],
"identity:revoke_grant": [["rule:admin_required"]],
"identity:get_policy": [["rule:admin_required"]],
"identity:list_policies": [["rule:admin_required"]],
"identity:create_policy": [["rule:admin_required"]],
"identity:update_policy": [["rule:admin_required"]],
"identity:delete_policy": [["rule:admin_required"]]
}

View File

@ -278,46 +278,40 @@ class EndpointController(wsgi.Application):
class ServiceControllerV3(controller.V3Controller):
@controller.protected
def create_service(self, context, service):
self.assert_admin(context)
ref = self._assign_unique_id(self._normalize_dict(service))
self._require_attribute(ref, 'type')
ref = self.catalog_api.create_service(context, ref['id'], ref)
return {'service': ref}
@controller.protected
def list_services(self, context):
self.assert_admin(context)
refs = self.catalog_api.list_services(context)
refs = self._filter_by_attribute(context, refs, 'type')
return {'services': self._paginate(context, refs)}
@controller.protected
def get_service(self, context, service_id):
self.assert_admin(context)
ref = self.catalog_api.get_service(context, service_id)
return {'service': ref}
@controller.protected
def update_service(self, context, service_id, service):
self.assert_admin(context)
self._require_matching_id(service_id, service)
ref = self.catalog_api.update_service(context, service_id, service)
return {'service': ref}
@controller.protected
def delete_service(self, context, service_id):
self.assert_admin(context)
return self.catalog_api.delete_service(context, service_id)
class EndpointControllerV3(controller.V3Controller):
@controller.protected
def create_endpoint(self, context, endpoint):
self.assert_admin(context)
ref = self._assign_unique_id(self._normalize_dict(endpoint))
self._require_attribute(ref, 'service_id')
self._require_attribute(ref, 'interface')
@ -326,23 +320,20 @@ class EndpointControllerV3(controller.V3Controller):
ref = self.catalog_api.create_endpoint(context, ref['id'], ref)
return {'endpoint': ref}
@controller.protected
def list_endpoints(self, context):
self.assert_admin(context)
refs = self.catalog_api.list_endpoints(context)
refs = self._filter_by_attribute(context, refs, 'service_id')
refs = self._filter_by_attribute(context, refs, 'interface')
return {'endpoints': self._paginate(context, refs)}
@controller.protected
def get_endpoint(self, context, endpoint_id):
self.assert_admin(context)
ref = self.catalog_api.get_endpoint(context, endpoint_id)
return {'endpoint': ref}
@controller.protected
def update_endpoint(self, context, endpoint_id, endpoint):
self.assert_admin(context)
self._require_matching_id(endpoint_id, endpoint)
if 'service_id' in endpoint:
@ -351,6 +342,6 @@ class EndpointControllerV3(controller.V3Controller):
ref = self.catalog_api.update_endpoint(context, endpoint_id, endpoint)
return {'endpoint': ref}
@controller.protected
def delete_endpoint(self, context, endpoint_id):
self.assert_admin(context)
return self.catalog_api.delete_endpoint(context, endpoint_id)

View File

@ -1,9 +1,60 @@
import uuid
import functools
from keystone.common import logging
from keystone.common import wsgi
from keystone import exception
LOG = logging.getLogger(__name__)
def protected(f):
"""Wraps API calls with role based access controls (RBAC)."""
@functools.wraps(f)
def wrapper(self, context, **kwargs):
if not context['is_admin']:
action = 'identity:%s' % f.__name__
LOG.debug('RBAC: Authorizing %s(%s)' % (
action,
', '.join(['%s=%s' % (k, kwargs[k]) for k in kwargs])))
try:
token_ref = self.token_api.get_token(
context=context, token_id=context['token_id'])
except exception.TokenNotFound:
LOG.warning('RBAC: Invalid token')
raise exception.Unauthorized()
creds = token_ref['metadata'].copy()
try:
creds['user_id'] = token_ref['user'].get('id')
except AttributeError:
LOG.warning('RBAC: Invalid user')
raise exception.Unauthorized()
try:
creds['tenant_id'] = token_ref['tenant'].get('id')
except AttributeError:
LOG.debug('RBAC: Proceeding without tenant')
# NOTE(vish): this is pretty inefficient
creds['roles'] = [self.identity_api.get_role(context, role)['name']
for role in creds.get('roles', [])]
self.policy_api.enforce(context, creds, action, kwargs)
LOG.debug('RBAC: Authorization granted')
else:
LOG.warning('RBAC: Bypassing authorization')
return f(self, context, **kwargs)
return wrapper
class V3Controller(wsgi.Application):
"""Base controller class for Identity API v3."""

View File

@ -859,134 +859,116 @@ class RoleController(wsgi.Application):
class DomainControllerV3(controller.V3Controller):
@controller.protected
def create_domain(self, context, domain):
self.assert_admin(context)
ref = self._assign_unique_id(self._normalize_dict(domain))
ref = self.identity_api.create_domain(context, ref['id'], ref)
return {'domain': ref}
@controller.protected
def list_domains(self, context):
self.assert_admin(context)
refs = self.identity_api.list_domains(context)
return {'domains': self._paginate(context, refs)}
@controller.protected
def get_domain(self, context, domain_id):
self.assert_admin(context)
ref = self.identity_api.get_domain(context, domain_id)
return {'domain': ref}
@controller.protected
def update_domain(self, context, domain_id, domain):
self.assert_admin(context)
self._require_matching_id(domain_id, domain)
ref = self.identity_api.update_domain(context, domain_id, domain)
return {'domain': ref}
@controller.protected
def delete_domain(self, context, domain_id):
self.assert_admin(context)
return self.identity_api.delete_domain(context, domain_id)
class ProjectControllerV3(controller.V3Controller):
@controller.protected
def create_project(self, context, project):
self.assert_admin(context)
ref = self._assign_unique_id(self._normalize_dict(project))
ref = self.identity_api.create_project(context, ref['id'], ref)
return {'project': ref}
@controller.protected
def list_projects(self, context):
self.assert_admin(context)
refs = self.identity_api.list_projects(context)
return {'projects': self._paginate(context, refs)}
@controller.protected
def list_user_projects(self, context, user_id):
# FIXME(dolph): this should also be callable by user_id themselves
self.assert_admin(context)
refs = self.identity_api.list_user_projects(context, user_id)
return {'projects': self._paginate(context, refs)}
@controller.protected
def get_project(self, context, project_id):
self.assert_admin(context)
ref = self.identity_api.get_project(context, project_id)
return {'project': ref}
@controller.protected
def update_project(self, context, project_id, project):
self.assert_admin(context)
self._require_matching_id(project_id, project)
ref = self.identity_api.update_project(context, project_id, project)
return {'project': ref}
@controller.protected
def delete_project(self, context, project_id):
self.assert_admin(context)
return self.identity_api.delete_project(context, project_id)
class UserControllerV3(controller.V3Controller):
@controller.protected
def create_user(self, context, user):
self.assert_admin(context)
ref = self._assign_unique_id(self._normalize_dict(user))
ref = self.identity_api.create_user(context, ref['id'], ref)
return {'user': ref}
@controller.protected
def list_users(self, context):
self.assert_admin(context)
refs = self.identity_api.list_users(context)
return {'users': self._paginate(context, refs)}
@controller.protected
def get_user(self, context, user_id):
self.assert_admin(context)
ref = self.identity_api.get_user(context, user_id)
return {'user': ref}
@controller.protected
def update_user(self, context, user_id, user):
self.assert_admin(context)
self._require_matching_id(user_id, user)
ref = self.identity_api.update_user(context, user_id, user)
return {'user': ref}
@controller.protected
def delete_user(self, context, user_id):
self.assert_admin(context)
return self.identity_api.delete_user(context, user_id)
class CredentialControllerV3(controller.V3Controller):
@controller.protected
def create_credential(self, context, credential):
self.assert_admin(context)
ref = self._assign_unique_id(self._normalize_dict(credential))
ref = self.identity_api.create_credential(context, ref['id'], ref)
return {'credential': ref}
@controller.protected
def list_credentials(self, context):
self.assert_admin(context)
refs = self.identity_api.list_credentials(context)
return {'credentials': self._paginate(context, refs)}
@controller.protected
def get_credential(self, context, credential_id):
self.assert_admin(context)
ref = self.identity_api.get_credential(context, credential_id)
return {'credential': ref}
@controller.protected
def update_credential(self, context, credential_id, credential):
self.assert_admin(context)
self._require_matching_id(credential_id, credential)
ref = self.identity_api.update_credential(
@ -995,41 +977,37 @@ class CredentialControllerV3(controller.V3Controller):
credential)
return {'credential': ref}
@controller.protected
def delete_credential(self, context, credential_id):
self.assert_admin(context)
return self.identity_api.delete_credential(context, credential_id)
class RoleControllerV3(controller.V3Controller):
@controller.protected
def create_role(self, context, role):
self.assert_admin(context)
ref = self._assign_unique_id(self._normalize_dict(role))
ref = self.identity_api.create_role(context, ref['id'], ref)
return {'role': ref}
@controller.protected
def list_roles(self, context):
self.assert_admin(context)
refs = self.identity_api.list_roles(context)
return {'roles': self._paginate(context, refs)}
@controller.protected
def get_role(self, context, role_id):
self.assert_admin(context)
ref = self.identity_api.get_role(context, role_id)
return {'role': ref}
@controller.protected
def update_role(self, context, role_id, role):
self.assert_admin(context)
self._require_matching_id(role_id, role)
ref = self.identity_api.update_role(context, role_id, role)
return {'role': ref}
@controller.protected
def delete_role(self, context, role_id):
self.assert_admin(context)
return self.identity_api.delete_role(context, role_id)
def _require_domain_or_project(self, domain_id, project_id):
@ -1037,41 +1015,37 @@ class RoleControllerV3(controller.V3Controller):
msg = 'Specify a domain or project, not both'
raise exception.ValidationError(msg)
@controller.protected
def create_grant(self, context, role_id, user_id, domain_id=None,
project_id=None):
"""Grants a role to a user on either a domain or project."""
self.assert_admin(context)
self._require_domain_or_project(domain_id, project_id)
return self.identity_api.create_grant(
context, role_id, user_id, domain_id, project_id)
@controller.protected
def list_grants(self, context, user_id, domain_id=None,
project_id=None):
"""Lists roles granted to a user on either a domain or project."""
self.assert_admin(context)
self._require_domain_or_project(domain_id, project_id)
return self.identity_api.list_grants(
context, user_id, domain_id, project_id)
@controller.protected
def check_grant(self, context, role_id, user_id, domain_id=None,
project_id=None):
"""Checks if a role has been granted on either a domain or project."""
self.assert_admin(context)
self._require_domain_or_project(domain_id, project_id)
self.identity_api.get_grant(
context, role_id, user_id, domain_id, project_id)
@controller.protected
def revoke_grant(self, context, role_id, user_id, domain_id=None,
project_id=None):
"""Revokes a role from a user on either a domain or project."""
self.assert_admin(context)
self._require_domain_or_project(domain_id, project_id)
self.identity_api.delete_grant(

View File

@ -105,9 +105,8 @@ class Driver(object):
class PolicyControllerV3(controller.V3Controller):
@controller.protected
def create_policy(self, context, policy):
self.assert_admin(context)
ref = self._assign_unique_id(self._normalize_dict(policy))
self._require_attribute(ref, 'blob')
self._require_attribute(ref, 'type')
@ -115,22 +114,22 @@ class PolicyControllerV3(controller.V3Controller):
ref = self.policy_api.create_policy(context, ref['id'], ref)
return {'policy': ref}
@controller.protected
def list_policies(self, context):
self.assert_admin(context)
refs = self.policy_api.list_policies(context)
refs = self._filter_by_attribute(context, refs, 'type')
return {'policies': self._paginate(context, refs)}
@controller.protected
def get_policy(self, context, policy_id):
self.assert_admin(context)
ref = self.policy_api.get_policy(context, policy_id)
return {'policy': ref}
@controller.protected
def update_policy(self, context, policy_id, policy):
self.assert_admin(context)
ref = self.policy_api.update_policy(context, policy_id, policy)
return {'policy': ref}
@controller.protected
def delete_policy(self, context, policy_id):
self.assert_admin(context)
return self.policy_api.delete_policy(context, policy_id)