Move user and admin crud to core
Move the user_crud and admin_crud extensions for V2 to core. Change-Id: If1b6bd354d05f5dafcbcd93b77b515b90522c1e7 bp: extension-to-core
This commit is contained in:
parent
6e4cf708c3
commit
f75f7e872f
@ -18,12 +18,6 @@ use = egg:keystone#admin_token_auth
|
||||
[filter:json_body]
|
||||
use = egg:keystone#json_body
|
||||
|
||||
[filter:user_crud_extension]
|
||||
use = egg:keystone#user_crud_extension
|
||||
|
||||
[filter:crud_extension]
|
||||
use = egg:keystone#crud_extension
|
||||
|
||||
[filter:ec2_extension]
|
||||
use = egg:keystone#ec2_extension
|
||||
|
||||
@ -51,12 +45,12 @@ use = egg:keystone#admin_service
|
||||
[pipeline:public_api]
|
||||
# The last item in this pipeline must be public_service or an equivalent
|
||||
# application. It cannot be a filter.
|
||||
pipeline = sizelimit url_normalize request_id build_auth_context token_auth admin_token_auth json_body ec2_extension user_crud_extension public_service
|
||||
pipeline = sizelimit url_normalize request_id build_auth_context token_auth admin_token_auth json_body ec2_extension public_service
|
||||
|
||||
[pipeline:admin_api]
|
||||
# The last item in this pipeline must be admin_service or an equivalent
|
||||
# application. It cannot be a filter.
|
||||
pipeline = sizelimit url_normalize request_id build_auth_context token_auth admin_token_auth json_body ec2_extension s3_extension crud_extension admin_service
|
||||
pipeline = sizelimit url_normalize request_id build_auth_context token_auth admin_token_auth json_body ec2_extension s3_extension admin_service
|
||||
|
||||
[pipeline:api_v3]
|
||||
# The last item in this pipeline must be service_v3 or an equivalent
|
||||
|
@ -12,229 +12,21 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from keystone import assignment
|
||||
from keystone import catalog
|
||||
from keystone.common import extension
|
||||
from oslo_log import log
|
||||
from oslo_log import versionutils
|
||||
|
||||
from keystone.common import wsgi
|
||||
from keystone import identity
|
||||
from keystone import resource
|
||||
from keystone.i18n import _
|
||||
|
||||
|
||||
extension.register_admin_extension(
|
||||
'OS-KSADM', {
|
||||
'name': 'OpenStack Keystone Admin',
|
||||
'namespace': 'http://docs.openstack.org/identity/api/ext/'
|
||||
'OS-KSADM/v1.0',
|
||||
'alias': 'OS-KSADM',
|
||||
'updated': '2013-07-11T17:14:00-00:00',
|
||||
'description': 'OpenStack extensions to Keystone v2.0 API '
|
||||
'enabling Administrative Operations.',
|
||||
'links': [
|
||||
{
|
||||
'rel': 'describedby',
|
||||
'type': 'text/html',
|
||||
'href': 'http://developer.openstack.org/'
|
||||
'api-ref-identity-v2-ext.html',
|
||||
}
|
||||
]})
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class CrudExtension(wsgi.ExtensionRouter):
|
||||
"""Previously known as the OS-KSADM extension.
|
||||
|
||||
Provides a bunch of CRUD operations for internal data types.
|
||||
|
||||
"""
|
||||
|
||||
def add_routes(self, mapper):
|
||||
tenant_controller = resource.controllers.Tenant()
|
||||
assignment_tenant_controller = (
|
||||
assignment.controllers.TenantAssignment())
|
||||
user_controller = identity.controllers.User()
|
||||
role_controller = assignment.controllers.Role()
|
||||
assignment_role_controller = assignment.controllers.RoleAssignmentV2()
|
||||
service_controller = catalog.controllers.Service()
|
||||
endpoint_controller = catalog.controllers.Endpoint()
|
||||
|
||||
# Tenant Operations
|
||||
mapper.connect(
|
||||
'/tenants',
|
||||
controller=tenant_controller,
|
||||
action='create_project',
|
||||
conditions=dict(method=['POST']))
|
||||
mapper.connect(
|
||||
'/tenants/{tenant_id}',
|
||||
controller=tenant_controller,
|
||||
action='update_project',
|
||||
conditions=dict(method=['PUT', 'POST']))
|
||||
mapper.connect(
|
||||
'/tenants/{tenant_id}',
|
||||
controller=tenant_controller,
|
||||
action='delete_project',
|
||||
conditions=dict(method=['DELETE']))
|
||||
mapper.connect(
|
||||
'/tenants/{tenant_id}/users',
|
||||
controller=assignment_tenant_controller,
|
||||
action='get_project_users',
|
||||
conditions=dict(method=['GET']))
|
||||
|
||||
# User Operations
|
||||
mapper.connect(
|
||||
'/users',
|
||||
controller=user_controller,
|
||||
action='get_users',
|
||||
conditions=dict(method=['GET']))
|
||||
mapper.connect(
|
||||
'/users',
|
||||
controller=user_controller,
|
||||
action='create_user',
|
||||
conditions=dict(method=['POST']))
|
||||
# NOTE(termie): not in diablo
|
||||
mapper.connect(
|
||||
'/users/{user_id}',
|
||||
controller=user_controller,
|
||||
action='update_user',
|
||||
conditions=dict(method=['PUT']))
|
||||
mapper.connect(
|
||||
'/users/{user_id}',
|
||||
controller=user_controller,
|
||||
action='delete_user',
|
||||
conditions=dict(method=['DELETE']))
|
||||
|
||||
# COMPAT(diablo): the copy with no OS-KSADM is from diablo
|
||||
mapper.connect(
|
||||
'/users/{user_id}/password',
|
||||
controller=user_controller,
|
||||
action='set_user_password',
|
||||
conditions=dict(method=['PUT']))
|
||||
mapper.connect(
|
||||
'/users/{user_id}/OS-KSADM/password',
|
||||
controller=user_controller,
|
||||
action='set_user_password',
|
||||
conditions=dict(method=['PUT']))
|
||||
|
||||
# COMPAT(diablo): the copy with no OS-KSADM is from diablo
|
||||
mapper.connect(
|
||||
'/users/{user_id}/tenant',
|
||||
controller=user_controller,
|
||||
action='update_user',
|
||||
conditions=dict(method=['PUT']))
|
||||
mapper.connect(
|
||||
'/users/{user_id}/OS-KSADM/tenant',
|
||||
controller=user_controller,
|
||||
action='update_user',
|
||||
conditions=dict(method=['PUT']))
|
||||
|
||||
# COMPAT(diablo): the copy with no OS-KSADM is from diablo
|
||||
mapper.connect(
|
||||
'/users/{user_id}/enabled',
|
||||
controller=user_controller,
|
||||
action='set_user_enabled',
|
||||
conditions=dict(method=['PUT']))
|
||||
mapper.connect(
|
||||
'/users/{user_id}/OS-KSADM/enabled',
|
||||
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']))
|
||||
|
||||
# Service Operations
|
||||
mapper.connect(
|
||||
'/OS-KSADM/services',
|
||||
controller=service_controller,
|
||||
action='get_services',
|
||||
conditions=dict(method=['GET']))
|
||||
mapper.connect(
|
||||
'/OS-KSADM/services',
|
||||
controller=service_controller,
|
||||
action='create_service',
|
||||
conditions=dict(method=['POST']))
|
||||
mapper.connect(
|
||||
'/OS-KSADM/services/{service_id}',
|
||||
controller=service_controller,
|
||||
action='delete_service',
|
||||
conditions=dict(method=['DELETE']))
|
||||
mapper.connect(
|
||||
'/OS-KSADM/services/{service_id}',
|
||||
controller=service_controller,
|
||||
action='get_service',
|
||||
conditions=dict(method=['GET']))
|
||||
|
||||
# Endpoint Templates
|
||||
mapper.connect(
|
||||
'/endpoints',
|
||||
controller=endpoint_controller,
|
||||
action='get_endpoints',
|
||||
conditions=dict(method=['GET']))
|
||||
mapper.connect(
|
||||
'/endpoints',
|
||||
controller=endpoint_controller,
|
||||
action='create_endpoint',
|
||||
conditions=dict(method=['POST']))
|
||||
mapper.connect(
|
||||
'/endpoints/{endpoint_id}',
|
||||
controller=endpoint_controller,
|
||||
action='delete_endpoint',
|
||||
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']))
|
||||
class CrudExtension(wsgi.Middleware):
|
||||
def __init__(self, application):
|
||||
super(CrudExtension, self).__init__(application)
|
||||
msg = _("Remove admin_crud_extension from the paste pipeline, the "
|
||||
"admin_crud extension is now always available. Update"
|
||||
"the [pipeline:admin_api] section in keystone-paste.ini "
|
||||
"accordingly, as it will be removed in the O release.")
|
||||
versionutils.report_deprecated_feature(LOG, msg)
|
||||
|
@ -12,123 +12,21 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
import uuid
|
||||
|
||||
from oslo_log import log
|
||||
from oslo_log import versionutils
|
||||
|
||||
from keystone.common import dependency
|
||||
from keystone.common import extension
|
||||
from keystone.common import wsgi
|
||||
from keystone import exception
|
||||
from keystone import identity
|
||||
from keystone.models import token_model
|
||||
from keystone.i18n import _
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
extension.register_public_extension(
|
||||
'OS-KSCRUD', {
|
||||
'name': 'OpenStack Keystone User CRUD',
|
||||
'namespace': 'http://docs.openstack.org/identity/api/ext/'
|
||||
'OS-KSCRUD/v1.0',
|
||||
'alias': 'OS-KSCRUD',
|
||||
'updated': '2013-07-07T12:00:0-00:00',
|
||||
'description': 'OpenStack extensions to Keystone v2.0 API '
|
||||
'enabling User Operations.',
|
||||
'links': [
|
||||
{
|
||||
'rel': 'describedby',
|
||||
'type': 'text/html',
|
||||
'href': 'http://developer.openstack.org/'
|
||||
'api-ref-identity-v2-ext.html',
|
||||
}
|
||||
]})
|
||||
|
||||
|
||||
@dependency.requires('catalog_api', 'identity_api', 'resource_api',
|
||||
'token_provider_api')
|
||||
class UserController(identity.controllers.User):
|
||||
def set_user_password(self, context, user_id, user):
|
||||
token_id = context.get('token_id')
|
||||
original_password = user.get('original_password')
|
||||
|
||||
token_data = self.token_provider_api.validate_token(token_id)
|
||||
token_ref = token_model.KeystoneToken(token_id=token_id,
|
||||
token_data=token_data)
|
||||
|
||||
if token_ref.user_id != user_id:
|
||||
raise exception.Forbidden('Token belongs to another user')
|
||||
if original_password is None:
|
||||
raise exception.ValidationError(target='user',
|
||||
attribute='original password')
|
||||
|
||||
try:
|
||||
user_ref = self.identity_api.authenticate(
|
||||
context,
|
||||
user_id=token_ref.user_id,
|
||||
password=original_password)
|
||||
if not user_ref.get('enabled', True):
|
||||
# NOTE(dolph): why can't you set a disabled user's password?
|
||||
raise exception.Unauthorized('User is disabled')
|
||||
except AssertionError:
|
||||
raise exception.Unauthorized()
|
||||
|
||||
update_dict = {'password': user['password'], 'id': user_id}
|
||||
|
||||
admin_context = copy.copy(context)
|
||||
admin_context['is_admin'] = True
|
||||
super(UserController, self).set_user_password(admin_context,
|
||||
user_id,
|
||||
update_dict)
|
||||
|
||||
# Issue a new token based upon the original token data. This will
|
||||
# always be a V2.0 token.
|
||||
|
||||
# TODO(morganfainberg): Add a mechanism to issue a new token directly
|
||||
# from a token model so that this code can go away. This is likely
|
||||
# not the norm as most cases do not need to yank apart a token to
|
||||
# issue a new one.
|
||||
new_token_ref = {}
|
||||
metadata_ref = {}
|
||||
roles_ref = None
|
||||
|
||||
new_token_ref['user'] = user_ref
|
||||
if token_ref.bind:
|
||||
new_token_ref['bind'] = token_ref.bind
|
||||
if token_ref.project_id:
|
||||
new_token_ref['tenant'] = self.resource_api.get_project(
|
||||
token_ref.project_id)
|
||||
if token_ref.role_names:
|
||||
roles_ref = [dict(name=value)
|
||||
for value in token_ref.role_names]
|
||||
if token_ref.role_ids:
|
||||
metadata_ref['roles'] = token_ref.role_ids
|
||||
if token_ref.trust_id:
|
||||
metadata_ref['trust'] = {
|
||||
'id': token_ref.trust_id,
|
||||
'trustee_user_id': token_ref.trustee_user_id}
|
||||
new_token_ref['metadata'] = metadata_ref
|
||||
new_token_ref['id'] = uuid.uuid4().hex
|
||||
|
||||
catalog_ref = self.catalog_api.get_catalog(user_id,
|
||||
token_ref.project_id)
|
||||
|
||||
new_token_id, new_token_data = self.token_provider_api.issue_v2_token(
|
||||
token_ref=new_token_ref, roles_ref=roles_ref,
|
||||
catalog_ref=catalog_ref)
|
||||
LOG.debug('TOKEN_REF %s', new_token_data)
|
||||
return new_token_data
|
||||
|
||||
|
||||
class CrudExtension(wsgi.ExtensionRouter):
|
||||
"""Provides a subset of CRUD operations for internal data types."""
|
||||
|
||||
def add_routes(self, mapper):
|
||||
user_controller = UserController()
|
||||
|
||||
mapper.connect('/OS-KSCRUD/users/{user_id}',
|
||||
controller=user_controller,
|
||||
action='set_user_password',
|
||||
conditions=dict(method=['PATCH']))
|
||||
class CrudExtension(wsgi.Middleware):
|
||||
def __init__(self, application):
|
||||
super(CrudExtension, self).__init__(application)
|
||||
msg = _("Remove user_crud_extension from the paste pipeline, the "
|
||||
"user_crud extension is now always available. Update"
|
||||
"the [pipeline:public_api] section in keystone-paste.ini "
|
||||
"accordingly, as it will be removed in the O release.")
|
||||
versionutils.report_deprecated_feature(LOG, msg)
|
||||
|
0
keystone/v2_crud/__init__.py
Normal file
0
keystone/v2_crud/__init__.py
Normal file
240
keystone/v2_crud/admin_crud.py
Normal file
240
keystone/v2_crud/admin_crud.py
Normal file
@ -0,0 +1,240 @@
|
||||
# Copyright 2012 OpenStack Foundation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from keystone import assignment
|
||||
from keystone import catalog
|
||||
from keystone.common import extension
|
||||
from keystone.common import wsgi
|
||||
from keystone import identity
|
||||
from keystone import resource
|
||||
|
||||
|
||||
extension.register_admin_extension(
|
||||
'OS-KSADM', {
|
||||
'name': 'OpenStack Keystone Admin',
|
||||
'namespace': 'http://docs.openstack.org/identity/api/ext/'
|
||||
'OS-KSADM/v1.0',
|
||||
'alias': 'OS-KSADM',
|
||||
'updated': '2013-07-11T17:14:00-00:00',
|
||||
'description': 'OpenStack extensions to Keystone v2.0 API '
|
||||
'enabling Administrative Operations.',
|
||||
'links': [
|
||||
{
|
||||
'rel': 'describedby',
|
||||
'type': 'text/html',
|
||||
'href': 'http://developer.openstack.org/'
|
||||
'api-ref-identity-v2-ext.html',
|
||||
}
|
||||
]})
|
||||
|
||||
|
||||
class Router(wsgi.ComposableRouter):
|
||||
"""Previously known as the OS-KSADM extension.
|
||||
|
||||
Provides a bunch of CRUD operations for internal data types.
|
||||
|
||||
"""
|
||||
|
||||
def add_routes(self, mapper):
|
||||
tenant_controller = resource.controllers.Tenant()
|
||||
assignment_tenant_controller = (
|
||||
assignment.controllers.TenantAssignment())
|
||||
user_controller = identity.controllers.User()
|
||||
role_controller = assignment.controllers.Role()
|
||||
assignment_role_controller = assignment.controllers.RoleAssignmentV2()
|
||||
service_controller = catalog.controllers.Service()
|
||||
endpoint_controller = catalog.controllers.Endpoint()
|
||||
|
||||
# Tenant Operations
|
||||
mapper.connect(
|
||||
'/tenants',
|
||||
controller=tenant_controller,
|
||||
action='create_project',
|
||||
conditions=dict(method=['POST']))
|
||||
mapper.connect(
|
||||
'/tenants/{tenant_id}',
|
||||
controller=tenant_controller,
|
||||
action='update_project',
|
||||
conditions=dict(method=['PUT', 'POST']))
|
||||
mapper.connect(
|
||||
'/tenants/{tenant_id}',
|
||||
controller=tenant_controller,
|
||||
action='delete_project',
|
||||
conditions=dict(method=['DELETE']))
|
||||
mapper.connect(
|
||||
'/tenants/{tenant_id}/users',
|
||||
controller=assignment_tenant_controller,
|
||||
action='get_project_users',
|
||||
conditions=dict(method=['GET']))
|
||||
|
||||
# User Operations
|
||||
mapper.connect(
|
||||
'/users',
|
||||
controller=user_controller,
|
||||
action='get_users',
|
||||
conditions=dict(method=['GET']))
|
||||
mapper.connect(
|
||||
'/users',
|
||||
controller=user_controller,
|
||||
action='create_user',
|
||||
conditions=dict(method=['POST']))
|
||||
# NOTE(termie): not in diablo
|
||||
mapper.connect(
|
||||
'/users/{user_id}',
|
||||
controller=user_controller,
|
||||
action='update_user',
|
||||
conditions=dict(method=['PUT']))
|
||||
mapper.connect(
|
||||
'/users/{user_id}',
|
||||
controller=user_controller,
|
||||
action='delete_user',
|
||||
conditions=dict(method=['DELETE']))
|
||||
|
||||
# COMPAT(diablo): the copy with no OS-KSADM is from diablo
|
||||
mapper.connect(
|
||||
'/users/{user_id}/password',
|
||||
controller=user_controller,
|
||||
action='set_user_password',
|
||||
conditions=dict(method=['PUT']))
|
||||
mapper.connect(
|
||||
'/users/{user_id}/OS-KSADM/password',
|
||||
controller=user_controller,
|
||||
action='set_user_password',
|
||||
conditions=dict(method=['PUT']))
|
||||
|
||||
# COMPAT(diablo): the copy with no OS-KSADM is from diablo
|
||||
mapper.connect(
|
||||
'/users/{user_id}/tenant',
|
||||
controller=user_controller,
|
||||
action='update_user',
|
||||
conditions=dict(method=['PUT']))
|
||||
mapper.connect(
|
||||
'/users/{user_id}/OS-KSADM/tenant',
|
||||
controller=user_controller,
|
||||
action='update_user',
|
||||
conditions=dict(method=['PUT']))
|
||||
|
||||
# COMPAT(diablo): the copy with no OS-KSADM is from diablo
|
||||
mapper.connect(
|
||||
'/users/{user_id}/enabled',
|
||||
controller=user_controller,
|
||||
action='set_user_enabled',
|
||||
conditions=dict(method=['PUT']))
|
||||
mapper.connect(
|
||||
'/users/{user_id}/OS-KSADM/enabled',
|
||||
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']))
|
||||
|
||||
# Service Operations
|
||||
mapper.connect(
|
||||
'/OS-KSADM/services',
|
||||
controller=service_controller,
|
||||
action='get_services',
|
||||
conditions=dict(method=['GET']))
|
||||
mapper.connect(
|
||||
'/OS-KSADM/services',
|
||||
controller=service_controller,
|
||||
action='create_service',
|
||||
conditions=dict(method=['POST']))
|
||||
mapper.connect(
|
||||
'/OS-KSADM/services/{service_id}',
|
||||
controller=service_controller,
|
||||
action='delete_service',
|
||||
conditions=dict(method=['DELETE']))
|
||||
mapper.connect(
|
||||
'/OS-KSADM/services/{service_id}',
|
||||
controller=service_controller,
|
||||
action='get_service',
|
||||
conditions=dict(method=['GET']))
|
||||
|
||||
# Endpoint Templates
|
||||
mapper.connect(
|
||||
'/endpoints',
|
||||
controller=endpoint_controller,
|
||||
action='get_endpoints',
|
||||
conditions=dict(method=['GET']))
|
||||
mapper.connect(
|
||||
'/endpoints',
|
||||
controller=endpoint_controller,
|
||||
action='create_endpoint',
|
||||
conditions=dict(method=['POST']))
|
||||
mapper.connect(
|
||||
'/endpoints/{endpoint_id}',
|
||||
controller=endpoint_controller,
|
||||
action='delete_endpoint',
|
||||
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']))
|
134
keystone/v2_crud/user_crud.py
Normal file
134
keystone/v2_crud/user_crud.py
Normal file
@ -0,0 +1,134 @@
|
||||
# Copyright 2012 Red Hat, Inc
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
import uuid
|
||||
|
||||
from oslo_log import log
|
||||
|
||||
from keystone.common import dependency
|
||||
from keystone.common import extension
|
||||
from keystone.common import wsgi
|
||||
from keystone import exception
|
||||
from keystone import identity
|
||||
from keystone.models import token_model
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
extension.register_public_extension(
|
||||
'OS-KSCRUD', {
|
||||
'name': 'OpenStack Keystone User CRUD',
|
||||
'namespace': 'http://docs.openstack.org/identity/api/ext/'
|
||||
'OS-KSCRUD/v1.0',
|
||||
'alias': 'OS-KSCRUD',
|
||||
'updated': '2013-07-07T12:00:0-00:00',
|
||||
'description': 'OpenStack extensions to Keystone v2.0 API '
|
||||
'enabling User Operations.',
|
||||
'links': [
|
||||
{
|
||||
'rel': 'describedby',
|
||||
'type': 'text/html',
|
||||
'href': 'http://developer.openstack.org/'
|
||||
'api-ref-identity-v2-ext.html',
|
||||
}
|
||||
]})
|
||||
|
||||
|
||||
@dependency.requires('catalog_api', 'identity_api', 'resource_api',
|
||||
'token_provider_api')
|
||||
class UserController(identity.controllers.User):
|
||||
def set_user_password(self, context, user_id, user):
|
||||
token_id = context.get('token_id')
|
||||
original_password = user.get('original_password')
|
||||
|
||||
token_data = self.token_provider_api.validate_token(token_id)
|
||||
token_ref = token_model.KeystoneToken(token_id=token_id,
|
||||
token_data=token_data)
|
||||
|
||||
if token_ref.user_id != user_id:
|
||||
raise exception.Forbidden('Token belongs to another user')
|
||||
if original_password is None:
|
||||
raise exception.ValidationError(target='user',
|
||||
attribute='original password')
|
||||
|
||||
try:
|
||||
user_ref = self.identity_api.authenticate(
|
||||
context,
|
||||
user_id=token_ref.user_id,
|
||||
password=original_password)
|
||||
if not user_ref.get('enabled', True):
|
||||
# NOTE(dolph): why can't you set a disabled user's password?
|
||||
raise exception.Unauthorized('User is disabled')
|
||||
except AssertionError:
|
||||
raise exception.Unauthorized()
|
||||
|
||||
update_dict = {'password': user['password'], 'id': user_id}
|
||||
|
||||
admin_context = copy.copy(context)
|
||||
admin_context['is_admin'] = True
|
||||
super(UserController, self).set_user_password(admin_context,
|
||||
user_id,
|
||||
update_dict)
|
||||
|
||||
# Issue a new token based upon the original token data. This will
|
||||
# always be a V2.0 token.
|
||||
|
||||
# TODO(morganfainberg): Add a mechanism to issue a new token directly
|
||||
# from a token model so that this code can go away. This is likely
|
||||
# not the norm as most cases do not need to yank apart a token to
|
||||
# issue a new one.
|
||||
new_token_ref = {}
|
||||
metadata_ref = {}
|
||||
roles_ref = None
|
||||
|
||||
new_token_ref['user'] = user_ref
|
||||
if token_ref.bind:
|
||||
new_token_ref['bind'] = token_ref.bind
|
||||
if token_ref.project_id:
|
||||
new_token_ref['tenant'] = self.resource_api.get_project(
|
||||
token_ref.project_id)
|
||||
if token_ref.role_names:
|
||||
roles_ref = [dict(name=value)
|
||||
for value in token_ref.role_names]
|
||||
if token_ref.role_ids:
|
||||
metadata_ref['roles'] = token_ref.role_ids
|
||||
if token_ref.trust_id:
|
||||
metadata_ref['trust'] = {
|
||||
'id': token_ref.trust_id,
|
||||
'trustee_user_id': token_ref.trustee_user_id}
|
||||
new_token_ref['metadata'] = metadata_ref
|
||||
new_token_ref['id'] = uuid.uuid4().hex
|
||||
|
||||
catalog_ref = self.catalog_api.get_catalog(user_id,
|
||||
token_ref.project_id)
|
||||
|
||||
new_token_id, new_token_data = self.token_provider_api.issue_v2_token(
|
||||
token_ref=new_token_ref, roles_ref=roles_ref,
|
||||
catalog_ref=catalog_ref)
|
||||
LOG.debug('TOKEN_REF %s', new_token_data)
|
||||
return new_token_data
|
||||
|
||||
|
||||
class Router(wsgi.ComposableRouter):
|
||||
"""Provides a subset of CRUD operations for internal data types."""
|
||||
|
||||
def add_routes(self, mapper):
|
||||
user_controller = UserController()
|
||||
|
||||
mapper.connect('/OS-KSCRUD/users/{user_id}',
|
||||
controller=user_controller,
|
||||
action='set_user_password',
|
||||
conditions=dict(method=['PATCH']))
|
@ -36,6 +36,8 @@ from keystone.revoke import routers as revoke_routers
|
||||
from keystone.token import _simple_cert as simple_cert_ext
|
||||
from keystone.token import routers as token_routers
|
||||
from keystone.trust import routers as trust_routers
|
||||
from keystone.v2_crud import admin_crud
|
||||
from keystone.v2_crud import user_crud
|
||||
from keystone.version import controllers
|
||||
from keystone.version import routers
|
||||
|
||||
@ -85,6 +87,7 @@ def public_app_factory(global_conf, **local_conf):
|
||||
return wsgi.ComposingRouter(routes.Mapper(),
|
||||
[assignment_routers.Public(),
|
||||
token_routers.Router(),
|
||||
user_crud.Router(),
|
||||
routers.VersionV2('public'),
|
||||
routers.Extension(False)])
|
||||
|
||||
@ -98,6 +101,7 @@ def admin_app_factory(global_conf, **local_conf):
|
||||
assignment_routers.Admin(),
|
||||
token_routers.Router(),
|
||||
resource_routers.Admin(),
|
||||
admin_crud.Router(),
|
||||
routers.VersionV2('admin'),
|
||||
routers.Extension()])
|
||||
|
||||
|
@ -8,6 +8,13 @@ upgrade:
|
||||
and ``[filter:revoke_extension]``. See the sample `keystone-paste.ini
|
||||
<https://git.openstack.org/cgit/openstack/keystone/tree/etc/keystone-paste.ini>`_
|
||||
file for guidance.
|
||||
- >
|
||||
The ``keystone-paste.ini`` file must be updated to remove extension filters,
|
||||
and their use in ``[pipeline:public_api]`` and ``[pipeline:admin_api]`` pipelines.
|
||||
Remove the following filters: ``[filter:user_crud_extension]``,
|
||||
``[filter:crud_extension]``. See the sample `keystone-paste.ini
|
||||
<https://git.openstack.org/cgit/openstack/keystone/tree/etc/keystone-paste.ini>`_
|
||||
file for guidance.
|
||||
other:
|
||||
- >
|
||||
[`blueprint move-extensions <https://blueprints.launchpad.net/keystone/+spec/move-extensions>`_]
|
||||
|
Loading…
Reference in New Issue
Block a user