diff options
author | Dolph Mathews <dolph.mathews@gmail.com> | 2012-09-05 10:15:44 -0500 |
---|---|---|
committer | Dolph Mathews <dolph.mathews@gmail.com> | 2012-09-05 13:07:49 -0500 |
commit | 399cb4cc71c5d48f58a668e0233396de97e65f89 (patch) | |
tree | f4ad905af78ac96f9471eac18c0b9d04f48150d5 /keystone | |
parent | 84da6be591d0cf4702c0728a0fd1e430526c7530 (diff) | |
download | keystone-399cb4cc71c5d48f58a668e0233396de97e65f89.tar.gz keystone-399cb4cc71c5d48f58a668e0233396de97e65f89.tar.xz keystone-399cb4cc71c5d48f58a668e0233396de97e65f89.zip |
Identity API v3 Config, Routers, Controllers
Provides configuration to deploy the v3 API identically across both:
http://[...]:5000/v3/
http://[...]:35357/v3/
Change-Id: I97c5a2f7a84e3fca0adaea020697f958e04f5753
Diffstat (limited to 'keystone')
-rw-r--r-- | keystone/catalog/core.py | 137 | ||||
-rw-r--r-- | keystone/common/controller.py | 45 | ||||
-rw-r--r-- | keystone/exception.py | 4 | ||||
-rw-r--r-- | keystone/identity/core.py | 464 | ||||
-rw-r--r-- | keystone/policy/core.py | 100 | ||||
-rw-r--r-- | keystone/service.py | 177 |
6 files changed, 859 insertions, 68 deletions
diff --git a/keystone/catalog/core.py b/keystone/catalog/core.py index b2b6c24e..686f5f7c 100644 --- a/keystone/catalog/core.py +++ b/keystone/catalog/core.py @@ -19,6 +19,7 @@ import uuid +from keystone.common import controller from keystone.common import manager from keystone.common import wsgi from keystone import config @@ -82,6 +83,14 @@ class Manager(manager.Manager): class Driver(object): """Interface description for an Catalog driver.""" + def create_service(self, service_id, service_ref): + """Creates a new service. + + :raises: keystone.exception.Conflict + + """ + raise exception.NotImplemented() + def list_services(self): """List all service ids in catalog. @@ -90,6 +99,14 @@ class Driver(object): """ raise exception.NotImplemented() + def get_all_services(self): + """List all services. + + :returns: list of service_refs or an empty list. + + """ + raise exception.NotImplemented() + def get_service(self, service_id): """Get service by id. @@ -99,18 +116,19 @@ class Driver(object): """ raise exception.NotImplemented() - def delete_service(self, service_id): - """Deletes an existing service. + def update_service(self, service_id): + """Update service by id. + :returns: service_ref dict :raises: keystone.exception.ServiceNotFound """ raise exception.NotImplemented() - def create_service(self, service_id, service_ref): - """Creates a new service. + def delete_service(self, service_id): + """Deletes an existing service. - :raises: keystone.exception.Conflict + :raises: keystone.exception.ServiceNotFound """ raise exception.NotImplemented() @@ -124,27 +142,45 @@ class Driver(object): """ raise exception.NotImplemented() - def delete_endpoint(self, endpoint_id): - """Deletes an endpoint for a service. + def get_endpoint(self, endpoint_id): + """Get endpoint by id. + :returns: endpoint_ref dict :raises: keystone.exception.EndpointNotFound """ raise exception.NotImplemented() - def get_endpoint(self, endpoint_id): + def list_endpoints(self): + """List all endpoint ids in catalog. + + :returns: list of endpoint_ids or an empty list. + + """ + raise exception.NotImplemented() + + def get_all_endpoints(self): + """List all endpoints. + + :returns: list of endpoint_refs or an empty list. + + """ + raise exception.NotImplemented() + + def update_endpoint(self, endpoint_id, endpoint_ref): """Get endpoint by id. :returns: endpoint_ref dict :raises: keystone.exception.EndpointNotFound + keystone.exception.ServiceNotFound """ raise exception.NotImplemented() - def list_endpoints(self): - """List all endpoint ids in catalog. + def delete_endpoint(self, endpoint_id): + """Deletes an endpoint for a service. - :returns: list of endpoint_ids or an empty list. + :raises: keystone.exception.EndpointNotFound """ raise exception.NotImplemented() @@ -237,3 +273,82 @@ class EndpointController(wsgi.Application): def delete_endpoint(self, context, endpoint_id): self.assert_admin(context) self.catalog_api.delete_endpoint(context, endpoint_id) + + +class ServiceControllerV3(controller.V3Controller): + 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} + + def list_services(self, context): + self.assert_admin(context) + + refs = self.catalog_api.get_all_services(context) + refs = self._filter_by_attribute(context, refs, 'type') + return {'services': self._paginate(context, refs)} + + def get_service(self, context, service_id): + self.assert_admin(context) + + ref = self.catalog_api.get_service(context, service_id) + return {'service': ref} + + 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} + + def delete_service(self, context, service_id): + self.assert_admin(context) + + return self.catalog_api.delete_service(context, service_id) + + +class EndpointControllerV3(controller.V3Controller): + 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') + self.catalog_api.get_service(context, ref['service_id']) + + ref = self.catalog_api.create_endpoint(context, ref['id'], ref) + return {'endpoint': ref} + + def list_endpoints(self, context): + self.assert_admin(context) + + refs = self.catalog_api.get_all_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)} + + def get_endpoint(self, context, endpoint_id): + self.assert_admin(context) + + ref = self.catalog_api.get_endpoint(context, endpoint_id) + return {'endpoint': ref} + + def update_endpoint(self, context, endpoint_id, endpoint): + self.assert_admin(context) + + self._require_matching_id(endpoint_id, endpoint) + + if 'service_id' in endpoint: + self.catalog_api.get_service(context, endpoint['service_id']) + + ref = self.catalog_api.update_endpoint(context, endpoint_id, endpoint) + return {'endpoint': ref} + + 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 new file mode 100644 index 00000000..cb8d90d8 --- /dev/null +++ b/keystone/common/controller.py @@ -0,0 +1,45 @@ +import uuid + +from keystone.common import wsgi +from keystone import exception + + +class V3Controller(wsgi.Application): + """Base controller class for Identity API v3.""" + + def __init__(self, catalog_api, identity_api, token_api, policy_api): + self.catalog_api = catalog_api + self.identity_api = identity_api + self.policy_api = policy_api + self.token_api = token_api + super(V3Controller, self).__init__() + + def _paginate(self, context, refs): + """Paginates a list of references by page & per_page query strings.""" + page = context['query_string'].get('page', 1) + per_page = context['query_string'].get('per_page', 30) + return refs[per_page * (page - 1):per_page * page] + + def _require_attribute(self, ref, attr): + """Ensures the reference contains the specified attribute.""" + if ref.get(attr) is None or ref.get(attr) == '': + msg = '%s field is required and cannot be empty' % attr + raise exception.ValidationError(message=msg) + + def _require_matching_id(self, value, ref): + """Ensures the value matches the reference's ID, if any.""" + if 'id' in ref and ref['id'] != value: + raise exception.ValidationError('Cannot change ID') + + def _assign_unique_id(self, ref): + """Generates and assigns a unique identifer to a reference.""" + ref = ref.copy() + ref['id'] = uuid.uuid4().hex + return ref + + def _filter_by_attribute(self, context, refs, attr): + """Filters a list of references by query string value.""" + if attr in context['query_string']: + value = context['query_string'][attr] + return [r for r in refs if r[attr] == value] + return refs diff --git a/keystone/exception.py b/keystone/exception.py index f559fec1..8f4ce0db 100644 --- a/keystone/exception.py +++ b/keystone/exception.py @@ -83,6 +83,10 @@ class MetadataNotFound(NotFound): # so this exception should not be exposed +class PolicyNotFound(NotFound): + """Could not find policy: %(policy_id)s""" + + class RoleNotFound(NotFound): """Could not find role: %(role_id)s""" diff --git a/keystone/identity/core.py b/keystone/identity/core.py index c9c496c2..3e3c337a 100644 --- a/keystone/identity/core.py +++ b/keystone/identity/core.py @@ -20,6 +20,7 @@ import urllib import urlparse import uuid +from keystone.common import controller from keystone.common import logging from keystone.common import manager from keystone.common import wsgi @@ -76,15 +77,6 @@ class Driver(object): """ raise exception.NotImplemented() - def get_user(self, user_id): - """Get a user by id. - - :returns: user_ref - :raises: keystone.exception.UserNotFound - - """ - raise exception.NotImplemented() - def get_user_by_name(self, user_name): """Get a user by name. @@ -94,36 +86,6 @@ class Driver(object): """ raise exception.NotImplemented() - def get_role(self, role_id): - """Get a role by id. - - :returns: role_ref - :raises: keystone.exception.RoleNotFound - - """ - raise exception.NotImplemented() - - def list_users(self): - """List all users in the system. - - NOTE(termie): I'd prefer if this listed only the users for a given - tenant. - - :returns: a list of user_refs or an empty list - - """ - raise exception.NotImplemented() - - def list_roles(self): - """List all roles in the system. - - :returns: a list of role_refs or an empty list. - - """ - raise exception.NotImplemented() - - # NOTE(termie): seven calls below should probably be exposed by the api - # more clearly when the api redesign happens def add_user_to_tenant(self, tenant_id, user_id): """Add user to a tenant without an explicit role relationship. @@ -202,7 +164,132 @@ class Driver(object): """ raise exception.NotImplemented() + # tenant crud + def create_tenant(self, tenant_id, tenant): + """Creates a new tenant. + + :raises: keystone.exception.Conflict + + """ + raise exception.NotImplemented() + + def update_tenant(self, tenant_id, tenant): + """Updates an existing tenant. + + :raises: keystone.exception.TenantNotFound, keystone.exception.Conflict + + """ + raise exception.NotImplemented() + + def delete_tenant(self, tenant_id): + """Deletes an existing tenant. + + :raises: keystone.exception.TenantNotFound + + """ + raise exception.NotImplemented() + + # metadata crud + def get_metadata(self, user_id, tenant_id): + raise exception.NotImplemented() + + def create_metadata(self, user_id, tenant_id, metadata): + raise exception.NotImplemented() + + def update_metadata(self, user_id, tenant_id, metadata): + raise exception.NotImplemented() + + def delete_metadata(self, user_id, tenant_id): + raise exception.NotImplemented() + + # domain crud + def create_domain(self, domain_id, domain): + """Creates a new domain. + + :raises: keystone.exception.Conflict + + """ + raise exception.NotImplemented() + + def list_domains(self): + """List all domains in the system. + + :returns: a list of domain_refs or an empty list. + + """ + raise exception.NotImplemented() + + def get_domain(self, domain_id): + """Get a domain by ID. + + :returns: user_ref + :raises: keystone.exception.DomainNotFound + + """ + raise exception.NotImplemented() + + def update_domain(self, domain_id, domain): + """Updates an existing domain. + + :raises: keystone.exception.DomainNotFound, + keystone.exception.Conflict + + """ + raise exception.NotImplemented() + + def delete_domain(self, domain_id): + """Deletes an existing domain. + + :raises: keystone.exception.DomainNotFound + + """ + raise exception.NotImplemented() + + # project crud + def create_project(self, project_id, project): + """Creates a new project. + + :raises: keystone.exception.Conflict + + """ + raise exception.NotImplemented() + + def list_projects(self): + """List all projects in the system. + + :returns: a list of project_refs or an empty list. + + """ + raise exception.NotImplemented() + + def get_project(self): + """Get a project by ID. + + :returns: user_ref + :raises: keystone.exception.ProjectNotFound + + """ + raise exception.NotImplemented() + + def update_project(self, project_id, project): + """Updates an existing project. + + :raises: keystone.exception.ProjectNotFound, + keystone.exception.Conflict + + """ + raise exception.NotImplemented() + + def delete_project(self, project_id): + """Deletes an existing project. + + :raises: keystone.exception.ProjectNotFound + + """ + raise exception.NotImplemented() + # user crud + def create_user(self, user_id, user): """Creates a new user. @@ -211,10 +298,28 @@ class Driver(object): """ raise exception.NotImplemented() + def list_users(self): + """List all users in the system. + + :returns: a list of user_refs or an empty list. + + """ + raise exception.NotImplemented() + + def get_user(self, user_id): + """Get a user by ID. + + :returns: user_ref + :raises: keystone.exception.UserNotFound + + """ + raise exception.NotImplemented() + def update_user(self, user_id, user): """Updates an existing user. - :raises: keystone.exception.UserNotFound, keystone.exception.Conflict + :raises: keystone.exception.UserNotFound, + keystone.exception.Conflict """ raise exception.NotImplemented() @@ -227,45 +332,52 @@ class Driver(object): """ raise exception.NotImplemented() - # tenant crud - def create_tenant(self, tenant_id, tenant): - """Creates a new tenant. + # credential crud + + def create_credential(self, credential_id, credential): + """Creates a new credential. :raises: keystone.exception.Conflict """ raise exception.NotImplemented() - def update_tenant(self, tenant_id, tenant): - """Updates an existing tenant. + def list_credentials(self): + """List all credentials in the system. - :raises: keystone.exception.TenantNotFound, keystone.exception.Conflict + :returns: a list of credential_refs or an empty list. """ raise exception.NotImplemented() - def delete_tenant(self, tenant_id): - """Deletes an existing tenant. + def get_credential(self, credential_id): + """Get a credential by ID. - :raises: keystone.exception.TenantNotFound + :returns: credential_ref + :raises: keystone.exception.CredentialNotFound """ raise exception.NotImplemented() - # metadata crud - def get_metadata(self, user_id, tenant_id): - raise exception.NotImplemented() + def update_credential(self, credential_id, credential): + """Updates an existing credential. - def create_metadata(self, user_id, tenant_id, metadata): - raise exception.NotImplemented() + :raises: keystone.exception.CredentialNotFound, + keystone.exception.Conflict - def update_metadata(self, user_id, tenant_id, metadata): + """ raise exception.NotImplemented() - def delete_metadata(self, user_id, tenant_id): + def delete_credential(self, credential_id): + """Deletes an existing credential. + + :raises: keystone.exception.CredentialNotFound + + """ raise exception.NotImplemented() # role crud + def create_role(self, role_id, role): """Creates a new role. @@ -274,10 +386,28 @@ class Driver(object): """ raise exception.NotImplemented() + def list_roles(self): + """List all roles in the system. + + :returns: a list of role_refs or an empty list. + + """ + raise exception.NotImplemented() + + def get_role(self, role_id): + """Get a role by ID. + + :returns: role_ref + :raises: keystone.exception.RoleNotFound + + """ + raise exception.NotImplemented() + def update_role(self, role_id, role): """Updates an existing role. - :raises: keystone.exception.RoleNotFound, keystone.exception.Conflict + :raises: keystone.exception.RoleNotFound, + keystone.exception.Conflict """ raise exception.NotImplemented() @@ -684,3 +814,223 @@ class RoleController(wsgi.Application): if not roles: self.identity_api.remove_user_from_tenant( context, tenant_id, user_id) + + +class DomainControllerV3(controller.V3Controller): + 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} + + def list_domains(self, context): + self.assert_admin(context) + + refs = self.identity_api.list_domains(context) + return {'domains': self._paginate(context, refs)} + + def get_domain(self, context, domain_id): + self.assert_admin(context) + + ref = self.identity_api.get_domain(context, domain_id) + return {'domain': ref} + + 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} + + def delete_domain(self, context, domain_id): + self.assert_admin(context) + return self.identity_api.delete_domain(context, domain_id) + + +class ProjectControllerV3(controller.V3Controller): + 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} + + def list_projects(self, context): + self.assert_admin(context) + + refs = self.identity_api.list_projects(context) + return {'projects': self._paginate(context, refs)} + + 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)} + + def get_project(self, context, project_id): + self.assert_admin(context) + + ref = self.identity_api.get_project(context, project_id) + return {'project': ref} + + 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} + + def delete_project(self, context, project_id): + self.assert_admin(context) + return self.identity_api.delete_project(context, project_id) + + +class UserControllerV3(controller.V3Controller): + 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} + + def list_users(self, context): + self.assert_admin(context) + + refs = self.identity_api.list_users(context) + return {'users': self._paginate(context, refs)} + + def get_user(self, context, user_id): + self.assert_admin(context) + + ref = self.identity_api.get_user(context, user_id) + return {'user': ref} + + 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} + + def delete_user(self, context, user_id): + self.assert_admin(context) + return self.identity_api.delete_user(context, user_id) + + +class CredentialControllerV3(controller.V3Controller): + 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} + + def list_credentials(self, context): + self.assert_admin(context) + + refs = self.identity_api.list_credentials(context) + return {'credentials': self._paginate(context, refs)} + + def get_credential(self, context, credential_id): + self.assert_admin(context) + + ref = self.identity_api.get_credential(context, credential_id) + return {'credential': ref} + + 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( + context, + credential_id, + credential) + return {'credential': ref} + + def delete_credential(self, context, credential_id): + self.assert_admin(context) + return self.identity_api.delete_credential(context, credential_id) + + +class RoleControllerV3(controller.V3Controller): + 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} + + def list_roles(self, context): + self.assert_admin(context) + + refs = self.identity_api.list_roles(context) + return {'roles': self._paginate(context, refs)} + + def get_role(self, context, role_id): + self.assert_admin(context) + + ref = self.identity_api.get_role(context, role_id) + return {'role': ref} + + 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} + + 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): + if (domain_id and project_id) or (not domain_id and not project_id): + msg = 'Specify a domain or project, not both' + raise exception.ValidationError(msg) + + 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) + + 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) + + 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) + + 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( + context, role_id, user_id, domain_id, project_id) diff --git a/keystone/policy/core.py b/keystone/policy/core.py index 5b60443b..498e027b 100644 --- a/keystone/policy/core.py +++ b/keystone/policy/core.py @@ -16,7 +16,9 @@ """Main entry point into the Policy service.""" + from keystone.common import manager +from keystone.common import controller from keystone import config from keystone import exception @@ -35,6 +37,26 @@ class Manager(manager.Manager): def __init__(self): super(Manager, self).__init__(CONF.policy.driver) + def get_policy(self, context, policy_id): + try: + return self.driver.get_policy(policy_id) + except exception.NotFound: + raise exception.PolicyNotFound(policy_id=policy_id) + + def update_policy(self, context, policy_id, policy): + if 'id' in policy and policy_id != policy['id']: + raise exception.ValidationError('Cannot change policy ID') + try: + return self.driver.update_policy(policy_id, policy) + except exception.NotFound: + raise exception.PolicyNotFound(policy_id=policy_id) + + def delete_policy(self, context, policy_id): + try: + return self.driver.delete_policy(policy_id) + except exception.NotFound: + raise exception.PolicyNotFound(policy_id=policy_id) + class Driver(object): def enforce(context, credentials, action, target): @@ -44,3 +66,81 @@ class Driver(object): `keystone.common.policy.enforce`. """ raise exception.NotImplemented() + + def create_policy(self, policy_id, policy): + """Store a policy blob for a particular endpoint. + + :raises: keystone.exception.EndpointNotFound, + keystone.exception.Conflict + + """ + raise exception.NotImplemented() + + def list_policies(self): + """List all policies.""" + raise exception.NotImplemented() + + def get_policy(self, policy_id): + """Retrieve a specific policy blob. + + :raises: keystone.exception.PolicyNotFound + + """ + raise exception.NotImplemented() + + def update_policy(self, policy_id, policy): + """Update a policy blob. + + :raises: keystone.exception.PolicyNotFound, + keystone.exception.EndpointNotFound + + """ + raise exception.NotImplemented() + + def delete_policy(self, policy_id): + """Remove a policy blob. + + :raises: keystone.exception.PolicyNotFound + + """ + raise exception.NotImplemented() + + +class PolicyControllerV3(controller.V3Controller): + 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') + self._require_attribute(ref, 'endpoint_id') + + self.catalog_api.get_endpoint(context, ref['endpoint_id']) + + ref = self.policy_api.create_policy(context, ref['id'], ref) + return {'policy': ref} + + def list_policies(self, context): + self.assert_admin(context) + refs = self.policy_api.list_policies(context) + refs = self._filter_by_attribute(context, refs, 'endpoint_id') + refs = self._filter_by_attribute(context, refs, 'type') + return {'policies': self._paginate(context, refs)} + + def get_policy(self, context, policy_id): + self.assert_admin(context) + ref = self.policy_api.get_policy(context, policy_id) + return {'policy': ref} + + def update_policy(self, context, policy_id, policy): + self.assert_admin(context) + + if 'endpoint_id' in policy: + self.catalog_api.get_endpoint(context, policy['endpoint_id']) + + ref = self.policy_api.update_policy(context, policy_id, policy) + return {'policy': ref} + + def delete_policy(self, context, policy_id): + self.assert_admin(context) + return self.policy_api.delete_policy(context, policy_id) diff --git a/keystone/service.py b/keystone/service.py index 65e336cd..459e4e88 100644 --- a/keystone/service.py +++ b/keystone/service.py @@ -33,6 +33,176 @@ from keystone import token LOG = logging.getLogger(__name__) +class V3Router(wsgi.ComposingRouter): + def crud_routes(self, mapper, controller, collection_key, key): + collection_path = '/%(collection_key)s' % { + 'collection_key': collection_key} + entity_path = '/%(collection_key)s/{%(key)s_id}' % { + 'collection_key': collection_key, + 'key': key} + + mapper.connect( + collection_path, + controller=controller, + action='create_%s' % key, + conditions=dict(method=['POST'])) + mapper.connect( + collection_path, + controller=controller, + action='list_%s' % collection_key, + conditions=dict(method=['GET'])) + mapper.connect( + entity_path, + controller=controller, + action='get_%s' % key, + conditions=dict(method=['GET'])) + mapper.connect( + entity_path, + controller=controller, + action='update_%s' % key, + conditions=dict(method=['PATCH'])) + mapper.connect( + entity_path, + controller=controller, + action='delete_%s' % key, + conditions=dict(method=['DELETE'])) + + def __init__(self): + mapper = routes.Mapper() + + apis = dict( + catalog_api=catalog.Manager(), + identity_api=identity.Manager(), + policy_api=policy.Manager(), + token_api=token.Manager()) + + # Catalog + + self.crud_routes( + mapper, + catalog.ServiceControllerV3(**apis), + 'services', + 'service') + + self.crud_routes( + mapper, + catalog.EndpointControllerV3(**apis), + 'endpoints', + 'endpoint') + + # Identity + + self.crud_routes( + mapper, + identity.DomainControllerV3(**apis), + 'domains', + 'domain') + + project_controller = identity.ProjectControllerV3(**apis) + self.crud_routes( + mapper, + project_controller, + 'projects', + 'project') + mapper.connect( + '/users/{user_id}/projects', + controller=project_controller, + action='list_user_projects', + conditions=dict(method=['GET'])) + + self.crud_routes( + mapper, + identity.UserControllerV3(**apis), + 'users', + 'user') + + self.crud_routes( + mapper, + identity.CredentialControllerV3(**apis), + 'credentials', + 'credential') + + role_controller = identity.RoleControllerV3(**apis) + self.crud_routes( + mapper, + role_controller, + 'roles', + 'role') + mapper.connect( + '/projects/{project_id}/users/{user_id}/roles/{role_id}', + controller=role_controller, + action='create_grant', + conditions=dict(method=['PUT'])) + mapper.connect( + '/projects/{project_id}/users/{user_id}/roles/{role_id}', + controller=role_controller, + action='check_grant', + conditions=dict(method=['HEAD'])) + mapper.connect( + '/projects/{project_id}/users/{user_id}/roles', + controller=role_controller, + action='list_grants', + conditions=dict(method=['GET'])) + mapper.connect( + '/projects/{project_id}/users/{user_id}/roles/{role_id}', + controller=role_controller, + action='revoke_grant', + conditions=dict(method=['DELETE'])) + mapper.connect( + '/domains/{domain_id}/users/{user_id}/roles/{role_id}', + controller=role_controller, + action='create_grant', + conditions=dict(method=['PUT'])) + mapper.connect( + '/domains/{domain_id}/users/{user_id}/roles/{role_id}', + controller=role_controller, + action='check_grant', + conditions=dict(method=['HEAD'])) + mapper.connect( + '/domains/{domain_id}/users/{user_id}/roles', + controller=role_controller, + action='list_grants', + conditions=dict(method=['GET'])) + mapper.connect( + '/domains/{domain_id}/users/{user_id}/roles/{role_id}', + controller=role_controller, + action='revoke_grant', + conditions=dict(method=['DELETE'])) + + # Policy + + policy_controller = policy.PolicyControllerV3(**apis) + self.crud_routes( + mapper, + policy_controller, + 'policies', + 'policy') + + # Token + + """ + # v2.0 LEGACY + mapper.connect('/tokens/{token_id}', + controller=auth_controller, + action='validate_token', + conditions=dict(method=['GET'])) + mapper.connect('/tokens/{token_id}', + controller=auth_controller, + action='validate_token_head', + conditions=dict(method=['HEAD'])) + mapper.connect('/tokens/{token_id}', + controller=auth_controller, + action='delete_token', + conditions=dict(method=['DELETE'])) + mapper.connect('/tokens/{token_id}/endpoints', + controller=auth_controller, + action='endpoints', + conditions=dict(method=['GET'])) + """ + + super(V3Router, self).__init__(mapper, []) + + class AdminRouter(wsgi.ComposingRouter): def __init__(self): mapper = routes.Mapper() @@ -717,3 +887,10 @@ def admin_version_app_factory(global_conf, **local_conf): conf = global_conf.copy() conf.update(local_conf) return AdminVersionRouter() + + +@logging.fail_gracefully +def v3_app_factory(global_conf, **local_conf): + conf = global_conf.copy() + conf.update(local_conf) + return V3Router() |