summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDolph Mathews <dolph.mathews@gmail.com>2012-08-30 05:37:26 -0500
committerDolph Mathews <dolph.mathews@gmail.com>2012-11-20 14:05:45 -0600
commit84cd8ff7f31a123a16114c8e1de963ede646d913 (patch)
tree4bbbf91a6d88bb9ead9491f43b7a45302a8f7d4d
parent715a17b71d065efe93a39721a40a4d58508d0cb6 (diff)
downloadkeystone-84cd8ff7f31a123a16114c8e1de963ede646d913.tar.gz
keystone-84cd8ff7f31a123a16114c8e1de963ede646d913.tar.xz
keystone-84cd8ff7f31a123a16114c8e1de963ede646d913.zip
Wrap v3 API with RBAC (bug 1023943)
Change-Id: Ie77be83054ea88bb0860260e1750196ac5ded650
-rw-r--r--etc/policy.json56
-rw-r--r--keystone/catalog/core.py29
-rw-r--r--keystone/common/controller.py51
-rw-r--r--keystone/identity/core.py86
-rw-r--r--keystone/policy/core.py11
5 files changed, 151 insertions, 82 deletions
diff --git a/etc/policy.json b/etc/policy.json
index 4a1482f8..aaf20924 100644
--- a/etc/policy.json
+++ b/etc/policy.json
@@ -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"]]
}
diff --git a/keystone/catalog/core.py b/keystone/catalog/core.py
index 2c396b5d..79b3df2a 100644
--- a/keystone/catalog/core.py
+++ b/keystone/catalog/core.py
@@ -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)
diff --git a/keystone/common/controller.py b/keystone/common/controller.py
index cb8d90d8..5db57b61 100644
--- a/keystone/common/controller.py
+++ b/keystone/common/controller.py
@@ -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."""
diff --git a/keystone/identity/core.py b/keystone/identity/core.py
index 5bd2f446..107dcaa9 100644
--- a/keystone/identity/core.py
+++ b/keystone/identity/core.py
@@ -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(
diff --git a/keystone/policy/core.py b/keystone/policy/core.py
index 36b982c8..2e5676fc 100644
--- a/keystone/policy/core.py
+++ b/keystone/policy/core.py
@@ -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)