¡@

Home 

OpenStack Study: controllers.py

OpenStack Index

**** CubicPower OpenStack Study ****

# Copyright 2013 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.

"""Extensions supporting OAuth1."""

from keystone.common import controller

from keystone.common import dependency

from keystone.common import wsgi

from keystone import config

from keystone.contrib.oauth1 import core as oauth1

from keystone.contrib.oauth1 import validator

from keystone import exception

from keystone.openstack.common.gettextutils import _

from keystone.openstack.common import jsonutils

from keystone.openstack.common import timeutils

CONF = config.CONF

@dependency.requires('oauth_api', 'token_api')

**** CubicPower OpenStack Study ****

class ConsumerCrudV3(controller.V3Controller):

collection_name = 'consumers'

member_name = 'consumer'

@classmethod

**** CubicPower OpenStack Study ****

    def base_url(cls, context, path=None):

        """Construct a path and pass it to V3Controller.base_url method."""

        # NOTE(stevemar): Overriding path to /OS-OAUTH1/consumers so that

        # V3Controller.base_url handles setting the self link correctly.

        path = '/OS-OAUTH1/' + cls.collection_name

        return controller.V3Controller.base_url(context, path=path)

    @controller.protected()

**** CubicPower OpenStack Study ****

    def create_consumer(self, context, consumer):

        ref = self._assign_unique_id(self._normalize_dict(consumer))

        consumer_ref = self.oauth_api.create_consumer(ref)

        return ConsumerCrudV3.wrap_member(context, consumer_ref)

    @controller.protected()

**** CubicPower OpenStack Study ****

    def update_consumer(self, context, consumer_id, consumer):

        self._require_matching_id(consumer_id, consumer)

        ref = self._normalize_dict(consumer)

        self._validate_consumer_ref(ref)

        ref = self.oauth_api.update_consumer(consumer_id, ref)

        return ConsumerCrudV3.wrap_member(context, ref)

    @controller.protected()

**** CubicPower OpenStack Study ****

    def list_consumers(self, context):

        ref = self.oauth_api.list_consumers()

        return ConsumerCrudV3.wrap_collection(context, ref)

    @controller.protected()

**** CubicPower OpenStack Study ****

    def get_consumer(self, context, consumer_id):

        ref = self.oauth_api.get_consumer(consumer_id)

        return ConsumerCrudV3.wrap_member(context, ref)

    @controller.protected()

**** CubicPower OpenStack Study ****

    def delete_consumer(self, context, consumer_id):

        user_token_ref = self.token_api.get_token(context['token_id'])

        user_id = user_token_ref['user'].get('id')

        self.token_api.delete_tokens(user_id, consumer_id=consumer_id)

        self.oauth_api.delete_consumer(consumer_id)

**** CubicPower OpenStack Study ****

    def _validate_consumer_ref(self, consumer):

        if 'secret' in consumer:

            msg = _('Cannot change consumer secret')

            raise exception.ValidationError(message=msg)

@dependency.requires('oauth_api', 'token_api')

**** CubicPower OpenStack Study ****

class AccessTokenCrudV3(controller.V3Controller):

collection_name = 'access_tokens'

member_name = 'access_token'

@controller.protected()

**** CubicPower OpenStack Study ****

    def get_access_token(self, context, user_id, access_token_id):

        access_token = self.oauth_api.get_access_token(access_token_id)

        if access_token['authorizing_user_id'] != user_id:

            raise exception.NotFound()

        access_token = self._format_token_entity(context, access_token)

        return AccessTokenCrudV3.wrap_member(context, access_token)

    @controller.protected()

**** CubicPower OpenStack Study ****

    def list_access_tokens(self, context, user_id):

        refs = self.oauth_api.list_access_tokens(user_id)

        formatted_refs = ([self._format_token_entity(context, x)

                           for x in refs])

        return AccessTokenCrudV3.wrap_collection(context, formatted_refs)

    @controller.protected()

**** CubicPower OpenStack Study ****

    def delete_access_token(self, context, user_id, access_token_id):

        access_token = self.oauth_api.get_access_token(access_token_id)

        consumer_id = access_token['consumer_id']

        self.token_api.delete_tokens(user_id, consumer_id=consumer_id)

        return self.oauth_api.delete_access_token(

            user_id, access_token_id)

**** CubicPower OpenStack Study ****

    def _format_token_entity(self, context, entity):

        formatted_entity = entity.copy()

        access_token_id = formatted_entity['id']

        user_id = ""

        if 'role_ids' in entity:

            formatted_entity.pop('role_ids')

        if 'access_secret' in entity:

            formatted_entity.pop('access_secret')

        if 'authorizing_user_id' in entity:

            user_id = formatted_entity['authorizing_user_id']

        url = ('/users/%(user_id)s/OS-OAUTH1/access_tokens/%(access_token_id)s'

               '/roles' % {'user_id': user_id,

                           'access_token_id': access_token_id})

        formatted_entity.setdefault('links', {})

        formatted_entity['links']['roles'] = (self.base_url(context, url))

        return formatted_entity

@dependency.requires('assignment_api', 'oauth_api')

**** CubicPower OpenStack Study ****

class AccessTokenRolesV3(controller.V3Controller):

collection_name = 'roles'

member_name = 'role'

@controller.protected()

**** CubicPower OpenStack Study ****

    def list_access_token_roles(self, context, user_id, access_token_id):

        access_token = self.oauth_api.get_access_token(access_token_id)

        if access_token['authorizing_user_id'] != user_id:

            raise exception.NotFound()

        authed_role_ids = access_token['role_ids']

        authed_role_ids = jsonutils.loads(authed_role_ids)

        refs = ([self._format_role_entity(x) for x in authed_role_ids])

        return AccessTokenRolesV3.wrap_collection(context, refs)

    @controller.protected()

**** CubicPower OpenStack Study ****

    def get_access_token_role(self, context, user_id,

                              access_token_id, role_id):

        access_token = self.oauth_api.get_access_token(access_token_id)

        if access_token['authorizing_user_id'] != user_id:

            raise exception.Unauthorized(_('User IDs do not match'))

        authed_role_ids = access_token['role_ids']

        authed_role_ids = jsonutils.loads(authed_role_ids)

        for authed_role_id in authed_role_ids:

            if authed_role_id == role_id:

                role = self._format_role_entity(role_id)

                return AccessTokenRolesV3.wrap_member(context, role)

        raise exception.RoleNotFound(_('Could not find role'))

**** CubicPower OpenStack Study ****

    def _format_role_entity(self, role_id):

        role = self.assignment_api.get_role(role_id)

        formatted_entity = role.copy()

        if 'description' in role:

            formatted_entity.pop('description')

        if 'enabled' in role:

            formatted_entity.pop('enabled')

        return formatted_entity

@dependency.requires('assignment_api', 'oauth_api', 'token_api')

**** CubicPower OpenStack Study ****

class OAuthControllerV3(controller.V3Controller):

collection_name = 'not_used'

member_name = 'not_used'

**** CubicPower OpenStack Study ****

    def create_request_token(self, context):

        headers = context['headers']

        oauth_headers = oauth1.get_oauth_headers(headers)

        consumer_id = oauth_headers.get('oauth_consumer_key')

        requested_project_id = headers.get('Requested-Project-Id')

        if not consumer_id:

            raise exception.ValidationError(

                attribute='oauth_consumer_key', target='request')

        if not requested_project_id:

            raise exception.ValidationError(

                attribute='requested_project_id', target='request')

        url = self.base_url(context, context['path'])

        req_headers = {'Requested-Project-Id': requested_project_id}

        req_headers.update(headers)

        request_verifier = oauth1.RequestTokenEndpoint(

            request_validator=validator.OAuthValidator(),

            token_generator=oauth1.token_generator)

        h, b, s = request_verifier.create_request_token_response(

            url,

            http_method='POST',

            body=context['query_string'],

            headers=req_headers)

        if (not b) or int(s) > 399:

            msg = _('Invalid signature')

            raise exception.Unauthorized(message=msg)

        request_token_duration = CONF.oauth1.request_token_duration

        token_ref = self.oauth_api.create_request_token(consumer_id,

                                                        requested_project_id,

                                                        request_token_duration)

        result = ('oauth_token=%(key)s&oauth_token_secret=%(secret)s'

                  % {'key': token_ref['id'],

                     'secret': token_ref['request_secret']})

        if CONF.oauth1.request_token_duration:

            expiry_bit = '&oauth_expires_at=%s' % token_ref['expires_at']

            result += expiry_bit

        headers = [('Content-Type', 'application/x-www-urlformencoded')]

        response = wsgi.render_response(result,

                                        status=(201, 'Created'),

                                        headers=headers)

        return response

**** CubicPower OpenStack Study ****

    def create_access_token(self, context):

        headers = context['headers']

        oauth_headers = oauth1.get_oauth_headers(headers)

        consumer_id = oauth_headers.get('oauth_consumer_key')

        request_token_id = oauth_headers.get('oauth_token')

        oauth_verifier = oauth_headers.get('oauth_verifier')

        if not consumer_id:

            raise exception.ValidationError(

                attribute='oauth_consumer_key', target='request')

        if not request_token_id:

            raise exception.ValidationError(

                attribute='oauth_token', target='request')

        if not oauth_verifier:

            raise exception.ValidationError(

                attribute='oauth_verifier', target='request')

        req_token = self.oauth_api.get_request_token(

            request_token_id)

        expires_at = req_token['expires_at']

        if expires_at:

            now = timeutils.utcnow()

            expires = timeutils.normalize_time(

                timeutils.parse_isotime(expires_at))

            if now > expires:

                raise exception.Unauthorized(_('Request token is expired'))

        url = self.base_url(context, context['path'])

        access_verifier = oauth1.AccessTokenEndpoint(

            request_validator=validator.OAuthValidator(),

            token_generator=oauth1.token_generator)

        h, b, s = access_verifier.create_access_token_response(

            url,

            http_method='POST',

            body=context['query_string'],

            headers=headers)

        params = oauth1.extract_non_oauth_params(b)

        if len(params) != 0:

            msg = _('There should not be any non-oauth parameters')

            raise exception.Unauthorized(message=msg)

        if req_token['consumer_id'] != consumer_id:

            msg = _('provided consumer key does not match stored consumer key')

            raise exception.Unauthorized(message=msg)

        if req_token['verifier'] != oauth_verifier:

            msg = _('provided verifier does not match stored verifier')

            raise exception.Unauthorized(message=msg)

        if req_token['id'] != request_token_id:

            msg = _('provided request key does not match stored request key')

            raise exception.Unauthorized(message=msg)

        if not req_token.get('authorizing_user_id'):

            msg = _('Request Token does not have an authorizing user id')

            raise exception.Unauthorized(message=msg)

        access_token_duration = CONF.oauth1.access_token_duration

        token_ref = self.oauth_api.create_access_token(request_token_id,

                                                       access_token_duration)

        result = ('oauth_token=%(key)s&oauth_token_secret=%(secret)s'

                  % {'key': token_ref['id'],

                     'secret': token_ref['access_secret']})

        if CONF.oauth1.access_token_duration:

            expiry_bit = '&oauth_expires_at=%s' % (token_ref['expires_at'])

            result += expiry_bit

        headers = [('Content-Type', 'application/x-www-urlformencoded')]

        response = wsgi.render_response(result,

                                        status=(201, 'Created'),

                                        headers=headers)

        return response

    @controller.protected()

**** CubicPower OpenStack Study ****

    def authorize_request_token(self, context, request_token_id, roles):

        """An authenticated user is going to authorize a request token.

        As a security precaution, the requested roles must match those in

        the request token. Because this is in a CLI-only world at the moment,

        there is not another easy way to make sure the user knows which roles

        are being requested before authorizing.

        """

        req_token = self.oauth_api.get_request_token(request_token_id)

        expires_at = req_token['expires_at']

        if expires_at:

            now = timeutils.utcnow()

            expires = timeutils.normalize_time(

                timeutils.parse_isotime(expires_at))

            if now > expires:

                raise exception.Unauthorized(_('Request token is expired'))

        # put the roles in a set for easy comparison

        authed_roles = set()

        for role in roles:

            authed_roles.add(role['id'])

        # verify the authorizing user has the roles

        user_token = self.token_api.get_token(context['token_id'])

        user_id = user_token['user'].get('id')

        project_id = req_token['requested_project_id']

        user_roles = self.assignment_api.get_roles_for_user_and_project(

            user_id, project_id)

        cred_set = set(user_roles)

        if not cred_set.issuperset(authed_roles):

            msg = _('authorizing user does not have role required')

            raise exception.Unauthorized(message=msg)

        # create list of just the id's for the backend

        role_list = list(authed_roles)

        # verify the user has the project too

        req_project_id = req_token['requested_project_id']

        user_projects = self.assignment_api.list_projects_for_user(user_id)

        found = False

        for user_project in user_projects:

            if user_project['id'] == req_project_id:

                found = True

                break

        if not found:

            msg = _("User is not a member of the requested project")

            raise exception.Unauthorized(message=msg)

        # finally authorize the token

        authed_token = self.oauth_api.authorize_request_token(

            request_token_id, user_id, role_list)

        to_return = {'token': {'oauth_verifier': authed_token['verifier']}}

        return to_return