Move oauth1 extension into core
Remove oauth1 as an extension and move it to a core resource. For now we leave the database migrations in the extension directory until we have a general policy for merging these into core. DocImpact: update keystone-paste and remove oauth1 from pipeline Change-Id: I0ed1ec44d42c3b379a5c2a40e3e6298842dfc01d Implements: bp move-extensions
This commit is contained in:
parent
9f3abc6983
commit
78e256273a
|
@ -30,9 +30,6 @@ use = egg:keystone#ec2_extension
|
||||||
[filter:ec2_extension_v3]
|
[filter:ec2_extension_v3]
|
||||||
use = egg:keystone#ec2_extension_v3
|
use = egg:keystone#ec2_extension_v3
|
||||||
|
|
||||||
[filter:oauth1_extension]
|
|
||||||
use = egg:keystone#oauth1_extension
|
|
||||||
|
|
||||||
[filter:s3_extension]
|
[filter:s3_extension]
|
||||||
use = egg:keystone#s3_extension
|
use = egg:keystone#s3_extension
|
||||||
|
|
||||||
|
@ -73,7 +70,7 @@ pipeline = sizelimit url_normalize request_id build_auth_context token_auth admi
|
||||||
[pipeline:api_v3]
|
[pipeline:api_v3]
|
||||||
# The last item in this pipeline must be service_v3 or an equivalent
|
# The last item in this pipeline must be service_v3 or an equivalent
|
||||||
# application. It cannot be a filter.
|
# application. It cannot be a filter.
|
||||||
pipeline = sizelimit url_normalize request_id build_auth_context token_auth admin_token_auth json_body ec2_extension_v3 s3_extension simple_cert_extension revoke_extension oauth1_extension endpoint_filter_extension service_v3
|
pipeline = sizelimit url_normalize request_id build_auth_context token_auth admin_token_auth json_body ec2_extension_v3 s3_extension simple_cert_extension revoke_extension endpoint_filter_extension service_v3
|
||||||
|
|
||||||
[app:public_version_service]
|
[app:public_version_service]
|
||||||
use = egg:keystone#public_version_service
|
use = egg:keystone#public_version_service
|
||||||
|
|
|
@ -18,10 +18,10 @@ from oslo_utils import timeutils
|
||||||
from keystone import auth
|
from keystone import auth
|
||||||
from keystone.common import controller
|
from keystone.common import controller
|
||||||
from keystone.common import dependency
|
from keystone.common import dependency
|
||||||
from keystone.contrib.oauth1 import core as oauth
|
|
||||||
from keystone.contrib.oauth1 import validator
|
|
||||||
from keystone import exception
|
from keystone import exception
|
||||||
from keystone.i18n import _
|
from keystone.i18n import _
|
||||||
|
from keystone.oauth1 import core as oauth
|
||||||
|
from keystone.oauth1 import validator
|
||||||
|
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
# 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.
|
|
||||||
|
|
||||||
from keystone.contrib.oauth1.core import * # noqa
|
|
|
@ -12,261 +12,19 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import datetime
|
from oslo_log import versionutils
|
||||||
import random as _random
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
from oslo_serialization import jsonutils
|
from keystone.oauth1.backends import sql
|
||||||
from oslo_utils import timeutils
|
|
||||||
|
|
||||||
from keystone.common import sql
|
|
||||||
from keystone.common import utils
|
|
||||||
from keystone.contrib.oauth1 import core
|
|
||||||
from keystone import exception
|
|
||||||
from keystone.i18n import _
|
|
||||||
|
|
||||||
|
|
||||||
random = _random.SystemRandom()
|
_OLD = "keystone.contrib.oauth1.backends.sql.OAuth1"
|
||||||
|
_NEW = "sql"
|
||||||
|
|
||||||
|
|
||||||
class Consumer(sql.ModelBase, sql.DictBase):
|
class OAuth1(sql.OAuth1):
|
||||||
__tablename__ = 'consumer'
|
|
||||||
attributes = ['id', 'description', 'secret']
|
|
||||||
id = sql.Column(sql.String(64), primary_key=True, nullable=False)
|
|
||||||
description = sql.Column(sql.String(64), nullable=True)
|
|
||||||
secret = sql.Column(sql.String(64), nullable=False)
|
|
||||||
extra = sql.Column(sql.JsonBlob(), nullable=False)
|
|
||||||
|
|
||||||
|
@versionutils.deprecated(versionutils.deprecated.MITAKA,
|
||||||
class RequestToken(sql.ModelBase, sql.DictBase):
|
in_favor_of=_NEW,
|
||||||
__tablename__ = 'request_token'
|
what=_OLD)
|
||||||
attributes = ['id', 'request_secret',
|
def __init__(self, *args, **kwargs):
|
||||||
'verifier', 'authorizing_user_id', 'requested_project_id',
|
super(OAuth1, self).__init__(*args, **kwargs)
|
||||||
'role_ids', 'consumer_id', 'expires_at']
|
|
||||||
id = sql.Column(sql.String(64), primary_key=True, nullable=False)
|
|
||||||
request_secret = sql.Column(sql.String(64), nullable=False)
|
|
||||||
verifier = sql.Column(sql.String(64), nullable=True)
|
|
||||||
authorizing_user_id = sql.Column(sql.String(64), nullable=True)
|
|
||||||
requested_project_id = sql.Column(sql.String(64), nullable=False)
|
|
||||||
role_ids = sql.Column(sql.Text(), nullable=True)
|
|
||||||
consumer_id = sql.Column(sql.String(64), sql.ForeignKey('consumer.id'),
|
|
||||||
nullable=False, index=True)
|
|
||||||
expires_at = sql.Column(sql.String(64), nullable=True)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_dict(cls, user_dict):
|
|
||||||
return cls(**user_dict)
|
|
||||||
|
|
||||||
def to_dict(self):
|
|
||||||
return dict(self.items())
|
|
||||||
|
|
||||||
|
|
||||||
class AccessToken(sql.ModelBase, sql.DictBase):
|
|
||||||
__tablename__ = 'access_token'
|
|
||||||
attributes = ['id', 'access_secret', 'authorizing_user_id',
|
|
||||||
'project_id', 'role_ids', 'consumer_id',
|
|
||||||
'expires_at']
|
|
||||||
id = sql.Column(sql.String(64), primary_key=True, nullable=False)
|
|
||||||
access_secret = sql.Column(sql.String(64), nullable=False)
|
|
||||||
authorizing_user_id = sql.Column(sql.String(64), nullable=False,
|
|
||||||
index=True)
|
|
||||||
project_id = sql.Column(sql.String(64), nullable=False)
|
|
||||||
role_ids = sql.Column(sql.Text(), nullable=False)
|
|
||||||
consumer_id = sql.Column(sql.String(64), sql.ForeignKey('consumer.id'),
|
|
||||||
nullable=False)
|
|
||||||
expires_at = sql.Column(sql.String(64), nullable=True)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_dict(cls, user_dict):
|
|
||||||
return cls(**user_dict)
|
|
||||||
|
|
||||||
def to_dict(self):
|
|
||||||
return dict(self.items())
|
|
||||||
|
|
||||||
|
|
||||||
class OAuth1(object):
|
|
||||||
def _get_consumer(self, session, consumer_id):
|
|
||||||
consumer_ref = session.query(Consumer).get(consumer_id)
|
|
||||||
if consumer_ref is None:
|
|
||||||
raise exception.NotFound(_('Consumer not found'))
|
|
||||||
return consumer_ref
|
|
||||||
|
|
||||||
def get_consumer_with_secret(self, consumer_id):
|
|
||||||
session = sql.get_session()
|
|
||||||
consumer_ref = self._get_consumer(session, consumer_id)
|
|
||||||
return consumer_ref.to_dict()
|
|
||||||
|
|
||||||
def get_consumer(self, consumer_id):
|
|
||||||
return core.filter_consumer(
|
|
||||||
self.get_consumer_with_secret(consumer_id))
|
|
||||||
|
|
||||||
def create_consumer(self, consumer):
|
|
||||||
consumer['secret'] = uuid.uuid4().hex
|
|
||||||
if not consumer.get('description'):
|
|
||||||
consumer['description'] = None
|
|
||||||
session = sql.get_session()
|
|
||||||
with session.begin():
|
|
||||||
consumer_ref = Consumer.from_dict(consumer)
|
|
||||||
session.add(consumer_ref)
|
|
||||||
return consumer_ref.to_dict()
|
|
||||||
|
|
||||||
def _delete_consumer(self, session, consumer_id):
|
|
||||||
consumer_ref = self._get_consumer(session, consumer_id)
|
|
||||||
session.delete(consumer_ref)
|
|
||||||
|
|
||||||
def _delete_request_tokens(self, session, consumer_id):
|
|
||||||
q = session.query(RequestToken)
|
|
||||||
req_tokens = q.filter_by(consumer_id=consumer_id)
|
|
||||||
req_tokens_list = set([x.id for x in req_tokens])
|
|
||||||
for token_id in req_tokens_list:
|
|
||||||
token_ref = self._get_request_token(session, token_id)
|
|
||||||
session.delete(token_ref)
|
|
||||||
|
|
||||||
def _delete_access_tokens(self, session, consumer_id):
|
|
||||||
q = session.query(AccessToken)
|
|
||||||
acc_tokens = q.filter_by(consumer_id=consumer_id)
|
|
||||||
acc_tokens_list = set([x.id for x in acc_tokens])
|
|
||||||
for token_id in acc_tokens_list:
|
|
||||||
token_ref = self._get_access_token(session, token_id)
|
|
||||||
session.delete(token_ref)
|
|
||||||
|
|
||||||
def delete_consumer(self, consumer_id):
|
|
||||||
session = sql.get_session()
|
|
||||||
with session.begin():
|
|
||||||
self._delete_request_tokens(session, consumer_id)
|
|
||||||
self._delete_access_tokens(session, consumer_id)
|
|
||||||
self._delete_consumer(session, consumer_id)
|
|
||||||
|
|
||||||
def list_consumers(self):
|
|
||||||
session = sql.get_session()
|
|
||||||
cons = session.query(Consumer)
|
|
||||||
return [core.filter_consumer(x.to_dict()) for x in cons]
|
|
||||||
|
|
||||||
def update_consumer(self, consumer_id, consumer):
|
|
||||||
session = sql.get_session()
|
|
||||||
with session.begin():
|
|
||||||
consumer_ref = self._get_consumer(session, consumer_id)
|
|
||||||
old_consumer_dict = consumer_ref.to_dict()
|
|
||||||
old_consumer_dict.update(consumer)
|
|
||||||
new_consumer = Consumer.from_dict(old_consumer_dict)
|
|
||||||
consumer_ref.description = new_consumer.description
|
|
||||||
consumer_ref.extra = new_consumer.extra
|
|
||||||
return core.filter_consumer(consumer_ref.to_dict())
|
|
||||||
|
|
||||||
def create_request_token(self, consumer_id, project_id, token_duration,
|
|
||||||
request_token_id=None, request_token_secret=None):
|
|
||||||
if request_token_id is None:
|
|
||||||
request_token_id = uuid.uuid4().hex
|
|
||||||
if request_token_secret is None:
|
|
||||||
request_token_secret = uuid.uuid4().hex
|
|
||||||
expiry_date = None
|
|
||||||
if token_duration:
|
|
||||||
now = timeutils.utcnow()
|
|
||||||
future = now + datetime.timedelta(seconds=token_duration)
|
|
||||||
expiry_date = utils.isotime(future, subsecond=True)
|
|
||||||
|
|
||||||
ref = {}
|
|
||||||
ref['id'] = request_token_id
|
|
||||||
ref['request_secret'] = request_token_secret
|
|
||||||
ref['verifier'] = None
|
|
||||||
ref['authorizing_user_id'] = None
|
|
||||||
ref['requested_project_id'] = project_id
|
|
||||||
ref['role_ids'] = None
|
|
||||||
ref['consumer_id'] = consumer_id
|
|
||||||
ref['expires_at'] = expiry_date
|
|
||||||
session = sql.get_session()
|
|
||||||
with session.begin():
|
|
||||||
token_ref = RequestToken.from_dict(ref)
|
|
||||||
session.add(token_ref)
|
|
||||||
return token_ref.to_dict()
|
|
||||||
|
|
||||||
def _get_request_token(self, session, request_token_id):
|
|
||||||
token_ref = session.query(RequestToken).get(request_token_id)
|
|
||||||
if token_ref is None:
|
|
||||||
raise exception.NotFound(_('Request token not found'))
|
|
||||||
return token_ref
|
|
||||||
|
|
||||||
def get_request_token(self, request_token_id):
|
|
||||||
session = sql.get_session()
|
|
||||||
token_ref = self._get_request_token(session, request_token_id)
|
|
||||||
return token_ref.to_dict()
|
|
||||||
|
|
||||||
def authorize_request_token(self, request_token_id, user_id,
|
|
||||||
role_ids):
|
|
||||||
session = sql.get_session()
|
|
||||||
with session.begin():
|
|
||||||
token_ref = self._get_request_token(session, request_token_id)
|
|
||||||
token_dict = token_ref.to_dict()
|
|
||||||
token_dict['authorizing_user_id'] = user_id
|
|
||||||
token_dict['verifier'] = ''.join(random.sample(core.VERIFIER_CHARS,
|
|
||||||
8))
|
|
||||||
token_dict['role_ids'] = jsonutils.dumps(role_ids)
|
|
||||||
|
|
||||||
new_token = RequestToken.from_dict(token_dict)
|
|
||||||
for attr in RequestToken.attributes:
|
|
||||||
if (attr == 'authorizing_user_id' or attr == 'verifier'
|
|
||||||
or attr == 'role_ids'):
|
|
||||||
setattr(token_ref, attr, getattr(new_token, attr))
|
|
||||||
|
|
||||||
return token_ref.to_dict()
|
|
||||||
|
|
||||||
def create_access_token(self, request_token_id, token_duration,
|
|
||||||
access_token_id=None, access_token_secret=None):
|
|
||||||
if access_token_id is None:
|
|
||||||
access_token_id = uuid.uuid4().hex
|
|
||||||
if access_token_secret is None:
|
|
||||||
access_token_secret = uuid.uuid4().hex
|
|
||||||
session = sql.get_session()
|
|
||||||
with session.begin():
|
|
||||||
req_token_ref = self._get_request_token(session, request_token_id)
|
|
||||||
token_dict = req_token_ref.to_dict()
|
|
||||||
|
|
||||||
expiry_date = None
|
|
||||||
if token_duration:
|
|
||||||
now = timeutils.utcnow()
|
|
||||||
future = now + datetime.timedelta(seconds=token_duration)
|
|
||||||
expiry_date = utils.isotime(future, subsecond=True)
|
|
||||||
|
|
||||||
# add Access Token
|
|
||||||
ref = {}
|
|
||||||
ref['id'] = access_token_id
|
|
||||||
ref['access_secret'] = access_token_secret
|
|
||||||
ref['authorizing_user_id'] = token_dict['authorizing_user_id']
|
|
||||||
ref['project_id'] = token_dict['requested_project_id']
|
|
||||||
ref['role_ids'] = token_dict['role_ids']
|
|
||||||
ref['consumer_id'] = token_dict['consumer_id']
|
|
||||||
ref['expires_at'] = expiry_date
|
|
||||||
token_ref = AccessToken.from_dict(ref)
|
|
||||||
session.add(token_ref)
|
|
||||||
|
|
||||||
# remove request token, it's been used
|
|
||||||
session.delete(req_token_ref)
|
|
||||||
|
|
||||||
return token_ref.to_dict()
|
|
||||||
|
|
||||||
def _get_access_token(self, session, access_token_id):
|
|
||||||
token_ref = session.query(AccessToken).get(access_token_id)
|
|
||||||
if token_ref is None:
|
|
||||||
raise exception.NotFound(_('Access token not found'))
|
|
||||||
return token_ref
|
|
||||||
|
|
||||||
def get_access_token(self, access_token_id):
|
|
||||||
session = sql.get_session()
|
|
||||||
token_ref = self._get_access_token(session, access_token_id)
|
|
||||||
return token_ref.to_dict()
|
|
||||||
|
|
||||||
def list_access_tokens(self, user_id):
|
|
||||||
session = sql.get_session()
|
|
||||||
q = session.query(AccessToken)
|
|
||||||
user_auths = q.filter_by(authorizing_user_id=user_id)
|
|
||||||
return [core.filter_token(x.to_dict()) for x in user_auths]
|
|
||||||
|
|
||||||
def delete_access_token(self, user_id, access_token_id):
|
|
||||||
session = sql.get_session()
|
|
||||||
with session.begin():
|
|
||||||
token_ref = self._get_access_token(session, access_token_id)
|
|
||||||
token_dict = token_ref.to_dict()
|
|
||||||
if token_dict['authorizing_user_id'] != user_id:
|
|
||||||
raise exception.Unauthorized(_('User IDs do not match'))
|
|
||||||
|
|
||||||
session.delete(token_ref)
|
|
||||||
|
|
|
@ -12,143 +12,22 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import functools
|
from oslo_log import log
|
||||||
|
from oslo_log import versionutils
|
||||||
|
|
||||||
from keystone.common import json_home
|
|
||||||
from keystone.common import wsgi
|
from keystone.common import wsgi
|
||||||
from keystone.contrib.oauth1 import controllers
|
from keystone.i18n import _
|
||||||
|
|
||||||
|
|
||||||
build_resource_relation = functools.partial(
|
LOG = log.getLogger(__name__)
|
||||||
json_home.build_v3_extension_resource_relation,
|
|
||||||
extension_name='OS-OAUTH1', extension_version='1.0')
|
|
||||||
|
|
||||||
build_parameter_relation = functools.partial(
|
|
||||||
json_home.build_v3_extension_parameter_relation,
|
|
||||||
extension_name='OS-OAUTH1', extension_version='1.0')
|
|
||||||
|
|
||||||
ACCESS_TOKEN_ID_PARAMETER_RELATION = build_parameter_relation(
|
|
||||||
parameter_name='access_token_id')
|
|
||||||
|
|
||||||
|
|
||||||
class OAuth1Extension(wsgi.V3ExtensionRouter):
|
class OAuth1Extension(wsgi.Middleware):
|
||||||
"""API Endpoints for the OAuth1 extension.
|
|
||||||
|
|
||||||
The goal of this extension is to allow third-party service providers
|
def __init__(self, *args, **kwargs):
|
||||||
to acquire tokens with a limited subset of a user's roles for acting
|
super(OAuth1Extension, self).__init__(*args, **kwargs)
|
||||||
on behalf of that user. This is done using an oauth-similar flow and
|
msg = _("Remove oauth1_extension from the paste pipeline, the "
|
||||||
api.
|
"oauth1 extension is now always available. Update the "
|
||||||
|
"[pipeline:api_v3] section in keystone-paste.ini accordingly, "
|
||||||
The API looks like::
|
"as it will be removed in the O release.")
|
||||||
|
versionutils.report_deprecated_feature(LOG, msg)
|
||||||
# Basic admin-only consumer crud
|
|
||||||
POST /OS-OAUTH1/consumers
|
|
||||||
GET /OS-OAUTH1/consumers
|
|
||||||
PATCH /OS-OAUTH1/consumers/{consumer_id}
|
|
||||||
GET /OS-OAUTH1/consumers/{consumer_id}
|
|
||||||
DELETE /OS-OAUTH1/consumers/{consumer_id}
|
|
||||||
|
|
||||||
# User access token crud
|
|
||||||
GET /users/{user_id}/OS-OAUTH1/access_tokens
|
|
||||||
GET /users/{user_id}/OS-OAUTH1/access_tokens/{access_token_id}
|
|
||||||
GET /users/{user_id}/OS-OAUTH1/access_tokens/{access_token_id}/roles
|
|
||||||
GET /users/{user_id}/OS-OAUTH1/access_tokens
|
|
||||||
/{access_token_id}/roles/{role_id}
|
|
||||||
DELETE /users/{user_id}/OS-OAUTH1/access_tokens/{access_token_id}
|
|
||||||
|
|
||||||
# OAuth interfaces
|
|
||||||
POST /OS-OAUTH1/request_token # create a request token
|
|
||||||
PUT /OS-OAUTH1/authorize # authorize a request token
|
|
||||||
POST /OS-OAUTH1/access_token # create an access token
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
def add_routes(self, mapper):
|
|
||||||
consumer_controller = controllers.ConsumerCrudV3()
|
|
||||||
access_token_controller = controllers.AccessTokenCrudV3()
|
|
||||||
access_token_roles_controller = controllers.AccessTokenRolesV3()
|
|
||||||
oauth_controller = controllers.OAuthControllerV3()
|
|
||||||
|
|
||||||
# basic admin-only consumer crud
|
|
||||||
self._add_resource(
|
|
||||||
mapper, consumer_controller,
|
|
||||||
path='/OS-OAUTH1/consumers',
|
|
||||||
get_action='list_consumers',
|
|
||||||
post_action='create_consumer',
|
|
||||||
rel=build_resource_relation(resource_name='consumers'))
|
|
||||||
self._add_resource(
|
|
||||||
mapper, consumer_controller,
|
|
||||||
path='/OS-OAUTH1/consumers/{consumer_id}',
|
|
||||||
get_action='get_consumer',
|
|
||||||
patch_action='update_consumer',
|
|
||||||
delete_action='delete_consumer',
|
|
||||||
rel=build_resource_relation(resource_name='consumer'),
|
|
||||||
path_vars={
|
|
||||||
'consumer_id':
|
|
||||||
build_parameter_relation(parameter_name='consumer_id'),
|
|
||||||
})
|
|
||||||
|
|
||||||
# user access token crud
|
|
||||||
self._add_resource(
|
|
||||||
mapper, access_token_controller,
|
|
||||||
path='/users/{user_id}/OS-OAUTH1/access_tokens',
|
|
||||||
get_action='list_access_tokens',
|
|
||||||
rel=build_resource_relation(resource_name='user_access_tokens'),
|
|
||||||
path_vars={
|
|
||||||
'user_id': json_home.Parameters.USER_ID,
|
|
||||||
})
|
|
||||||
self._add_resource(
|
|
||||||
mapper, access_token_controller,
|
|
||||||
path='/users/{user_id}/OS-OAUTH1/access_tokens/{access_token_id}',
|
|
||||||
get_action='get_access_token',
|
|
||||||
delete_action='delete_access_token',
|
|
||||||
rel=build_resource_relation(resource_name='user_access_token'),
|
|
||||||
path_vars={
|
|
||||||
'access_token_id': ACCESS_TOKEN_ID_PARAMETER_RELATION,
|
|
||||||
'user_id': json_home.Parameters.USER_ID,
|
|
||||||
})
|
|
||||||
self._add_resource(
|
|
||||||
mapper, access_token_roles_controller,
|
|
||||||
path='/users/{user_id}/OS-OAUTH1/access_tokens/{access_token_id}/'
|
|
||||||
'roles',
|
|
||||||
get_action='list_access_token_roles',
|
|
||||||
rel=build_resource_relation(
|
|
||||||
resource_name='user_access_token_roles'),
|
|
||||||
path_vars={
|
|
||||||
'access_token_id': ACCESS_TOKEN_ID_PARAMETER_RELATION,
|
|
||||||
'user_id': json_home.Parameters.USER_ID,
|
|
||||||
})
|
|
||||||
self._add_resource(
|
|
||||||
mapper, access_token_roles_controller,
|
|
||||||
path='/users/{user_id}/OS-OAUTH1/access_tokens/{access_token_id}/'
|
|
||||||
'roles/{role_id}',
|
|
||||||
get_action='get_access_token_role',
|
|
||||||
rel=build_resource_relation(
|
|
||||||
resource_name='user_access_token_role'),
|
|
||||||
path_vars={
|
|
||||||
'access_token_id': ACCESS_TOKEN_ID_PARAMETER_RELATION,
|
|
||||||
'role_id': json_home.Parameters.ROLE_ID,
|
|
||||||
'user_id': json_home.Parameters.USER_ID,
|
|
||||||
})
|
|
||||||
|
|
||||||
# oauth flow calls
|
|
||||||
self._add_resource(
|
|
||||||
mapper, oauth_controller,
|
|
||||||
path='/OS-OAUTH1/request_token',
|
|
||||||
post_action='create_request_token',
|
|
||||||
rel=build_resource_relation(resource_name='request_tokens'))
|
|
||||||
self._add_resource(
|
|
||||||
mapper, oauth_controller,
|
|
||||||
path='/OS-OAUTH1/access_token',
|
|
||||||
post_action='create_access_token',
|
|
||||||
rel=build_resource_relation(resource_name='access_tokens'))
|
|
||||||
self._add_resource(
|
|
||||||
mapper, oauth_controller,
|
|
||||||
path='/OS-OAUTH1/authorize/{request_token_id}',
|
|
||||||
path_vars={
|
|
||||||
'request_token_id':
|
|
||||||
build_parameter_relation(parameter_name='request_token_id')
|
|
||||||
},
|
|
||||||
put_action='authorize_request_token',
|
|
||||||
rel=build_resource_relation(
|
|
||||||
resource_name='authorize_request_token'))
|
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
from keystone.oauth1.core import * # noqa
|
||||||
|
from keystone.oauth1 import routers # noqa
|
|
@ -0,0 +1,272 @@
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import random as _random
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
from oslo_serialization import jsonutils
|
||||||
|
from oslo_utils import timeutils
|
||||||
|
|
||||||
|
from keystone.common import sql
|
||||||
|
from keystone.common import utils
|
||||||
|
from keystone import exception
|
||||||
|
from keystone.i18n import _
|
||||||
|
from keystone.oauth1 import core
|
||||||
|
|
||||||
|
|
||||||
|
random = _random.SystemRandom()
|
||||||
|
|
||||||
|
|
||||||
|
class Consumer(sql.ModelBase, sql.DictBase):
|
||||||
|
__tablename__ = 'consumer'
|
||||||
|
attributes = ['id', 'description', 'secret']
|
||||||
|
id = sql.Column(sql.String(64), primary_key=True, nullable=False)
|
||||||
|
description = sql.Column(sql.String(64), nullable=True)
|
||||||
|
secret = sql.Column(sql.String(64), nullable=False)
|
||||||
|
extra = sql.Column(sql.JsonBlob(), nullable=False)
|
||||||
|
|
||||||
|
|
||||||
|
class RequestToken(sql.ModelBase, sql.DictBase):
|
||||||
|
__tablename__ = 'request_token'
|
||||||
|
attributes = ['id', 'request_secret',
|
||||||
|
'verifier', 'authorizing_user_id', 'requested_project_id',
|
||||||
|
'role_ids', 'consumer_id', 'expires_at']
|
||||||
|
id = sql.Column(sql.String(64), primary_key=True, nullable=False)
|
||||||
|
request_secret = sql.Column(sql.String(64), nullable=False)
|
||||||
|
verifier = sql.Column(sql.String(64), nullable=True)
|
||||||
|
authorizing_user_id = sql.Column(sql.String(64), nullable=True)
|
||||||
|
requested_project_id = sql.Column(sql.String(64), nullable=False)
|
||||||
|
role_ids = sql.Column(sql.Text(), nullable=True)
|
||||||
|
consumer_id = sql.Column(sql.String(64), sql.ForeignKey('consumer.id'),
|
||||||
|
nullable=False, index=True)
|
||||||
|
expires_at = sql.Column(sql.String(64), nullable=True)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, user_dict):
|
||||||
|
return cls(**user_dict)
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
return dict(self.items())
|
||||||
|
|
||||||
|
|
||||||
|
class AccessToken(sql.ModelBase, sql.DictBase):
|
||||||
|
__tablename__ = 'access_token'
|
||||||
|
attributes = ['id', 'access_secret', 'authorizing_user_id',
|
||||||
|
'project_id', 'role_ids', 'consumer_id',
|
||||||
|
'expires_at']
|
||||||
|
id = sql.Column(sql.String(64), primary_key=True, nullable=False)
|
||||||
|
access_secret = sql.Column(sql.String(64), nullable=False)
|
||||||
|
authorizing_user_id = sql.Column(sql.String(64), nullable=False,
|
||||||
|
index=True)
|
||||||
|
project_id = sql.Column(sql.String(64), nullable=False)
|
||||||
|
role_ids = sql.Column(sql.Text(), nullable=False)
|
||||||
|
consumer_id = sql.Column(sql.String(64), sql.ForeignKey('consumer.id'),
|
||||||
|
nullable=False)
|
||||||
|
expires_at = sql.Column(sql.String(64), nullable=True)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, user_dict):
|
||||||
|
return cls(**user_dict)
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
return dict(self.items())
|
||||||
|
|
||||||
|
|
||||||
|
class OAuth1(object):
|
||||||
|
def _get_consumer(self, session, consumer_id):
|
||||||
|
consumer_ref = session.query(Consumer).get(consumer_id)
|
||||||
|
if consumer_ref is None:
|
||||||
|
raise exception.NotFound(_('Consumer not found'))
|
||||||
|
return consumer_ref
|
||||||
|
|
||||||
|
def get_consumer_with_secret(self, consumer_id):
|
||||||
|
session = sql.get_session()
|
||||||
|
consumer_ref = self._get_consumer(session, consumer_id)
|
||||||
|
return consumer_ref.to_dict()
|
||||||
|
|
||||||
|
def get_consumer(self, consumer_id):
|
||||||
|
return core.filter_consumer(
|
||||||
|
self.get_consumer_with_secret(consumer_id))
|
||||||
|
|
||||||
|
def create_consumer(self, consumer):
|
||||||
|
consumer['secret'] = uuid.uuid4().hex
|
||||||
|
if not consumer.get('description'):
|
||||||
|
consumer['description'] = None
|
||||||
|
session = sql.get_session()
|
||||||
|
with session.begin():
|
||||||
|
consumer_ref = Consumer.from_dict(consumer)
|
||||||
|
session.add(consumer_ref)
|
||||||
|
return consumer_ref.to_dict()
|
||||||
|
|
||||||
|
def _delete_consumer(self, session, consumer_id):
|
||||||
|
consumer_ref = self._get_consumer(session, consumer_id)
|
||||||
|
session.delete(consumer_ref)
|
||||||
|
|
||||||
|
def _delete_request_tokens(self, session, consumer_id):
|
||||||
|
q = session.query(RequestToken)
|
||||||
|
req_tokens = q.filter_by(consumer_id=consumer_id)
|
||||||
|
req_tokens_list = set([x.id for x in req_tokens])
|
||||||
|
for token_id in req_tokens_list:
|
||||||
|
token_ref = self._get_request_token(session, token_id)
|
||||||
|
session.delete(token_ref)
|
||||||
|
|
||||||
|
def _delete_access_tokens(self, session, consumer_id):
|
||||||
|
q = session.query(AccessToken)
|
||||||
|
acc_tokens = q.filter_by(consumer_id=consumer_id)
|
||||||
|
acc_tokens_list = set([x.id for x in acc_tokens])
|
||||||
|
for token_id in acc_tokens_list:
|
||||||
|
token_ref = self._get_access_token(session, token_id)
|
||||||
|
session.delete(token_ref)
|
||||||
|
|
||||||
|
def delete_consumer(self, consumer_id):
|
||||||
|
session = sql.get_session()
|
||||||
|
with session.begin():
|
||||||
|
self._delete_request_tokens(session, consumer_id)
|
||||||
|
self._delete_access_tokens(session, consumer_id)
|
||||||
|
self._delete_consumer(session, consumer_id)
|
||||||
|
|
||||||
|
def list_consumers(self):
|
||||||
|
session = sql.get_session()
|
||||||
|
cons = session.query(Consumer)
|
||||||
|
return [core.filter_consumer(x.to_dict()) for x in cons]
|
||||||
|
|
||||||
|
def update_consumer(self, consumer_id, consumer):
|
||||||
|
session = sql.get_session()
|
||||||
|
with session.begin():
|
||||||
|
consumer_ref = self._get_consumer(session, consumer_id)
|
||||||
|
old_consumer_dict = consumer_ref.to_dict()
|
||||||
|
old_consumer_dict.update(consumer)
|
||||||
|
new_consumer = Consumer.from_dict(old_consumer_dict)
|
||||||
|
consumer_ref.description = new_consumer.description
|
||||||
|
consumer_ref.extra = new_consumer.extra
|
||||||
|
return core.filter_consumer(consumer_ref.to_dict())
|
||||||
|
|
||||||
|
def create_request_token(self, consumer_id, project_id, token_duration,
|
||||||
|
request_token_id=None, request_token_secret=None):
|
||||||
|
if request_token_id is None:
|
||||||
|
request_token_id = uuid.uuid4().hex
|
||||||
|
if request_token_secret is None:
|
||||||
|
request_token_secret = uuid.uuid4().hex
|
||||||
|
expiry_date = None
|
||||||
|
if token_duration:
|
||||||
|
now = timeutils.utcnow()
|
||||||
|
future = now + datetime.timedelta(seconds=token_duration)
|
||||||
|
expiry_date = utils.isotime(future, subsecond=True)
|
||||||
|
|
||||||
|
ref = {}
|
||||||
|
ref['id'] = request_token_id
|
||||||
|
ref['request_secret'] = request_token_secret
|
||||||
|
ref['verifier'] = None
|
||||||
|
ref['authorizing_user_id'] = None
|
||||||
|
ref['requested_project_id'] = project_id
|
||||||
|
ref['role_ids'] = None
|
||||||
|
ref['consumer_id'] = consumer_id
|
||||||
|
ref['expires_at'] = expiry_date
|
||||||
|
session = sql.get_session()
|
||||||
|
with session.begin():
|
||||||
|
token_ref = RequestToken.from_dict(ref)
|
||||||
|
session.add(token_ref)
|
||||||
|
return token_ref.to_dict()
|
||||||
|
|
||||||
|
def _get_request_token(self, session, request_token_id):
|
||||||
|
token_ref = session.query(RequestToken).get(request_token_id)
|
||||||
|
if token_ref is None:
|
||||||
|
raise exception.NotFound(_('Request token not found'))
|
||||||
|
return token_ref
|
||||||
|
|
||||||
|
def get_request_token(self, request_token_id):
|
||||||
|
session = sql.get_session()
|
||||||
|
token_ref = self._get_request_token(session, request_token_id)
|
||||||
|
return token_ref.to_dict()
|
||||||
|
|
||||||
|
def authorize_request_token(self, request_token_id, user_id,
|
||||||
|
role_ids):
|
||||||
|
session = sql.get_session()
|
||||||
|
with session.begin():
|
||||||
|
token_ref = self._get_request_token(session, request_token_id)
|
||||||
|
token_dict = token_ref.to_dict()
|
||||||
|
token_dict['authorizing_user_id'] = user_id
|
||||||
|
token_dict['verifier'] = ''.join(random.sample(core.VERIFIER_CHARS,
|
||||||
|
8))
|
||||||
|
token_dict['role_ids'] = jsonutils.dumps(role_ids)
|
||||||
|
|
||||||
|
new_token = RequestToken.from_dict(token_dict)
|
||||||
|
for attr in RequestToken.attributes:
|
||||||
|
if (attr == 'authorizing_user_id' or attr == 'verifier'
|
||||||
|
or attr == 'role_ids'):
|
||||||
|
setattr(token_ref, attr, getattr(new_token, attr))
|
||||||
|
|
||||||
|
return token_ref.to_dict()
|
||||||
|
|
||||||
|
def create_access_token(self, request_token_id, token_duration,
|
||||||
|
access_token_id=None, access_token_secret=None):
|
||||||
|
if access_token_id is None:
|
||||||
|
access_token_id = uuid.uuid4().hex
|
||||||
|
if access_token_secret is None:
|
||||||
|
access_token_secret = uuid.uuid4().hex
|
||||||
|
session = sql.get_session()
|
||||||
|
with session.begin():
|
||||||
|
req_token_ref = self._get_request_token(session, request_token_id)
|
||||||
|
token_dict = req_token_ref.to_dict()
|
||||||
|
|
||||||
|
expiry_date = None
|
||||||
|
if token_duration:
|
||||||
|
now = timeutils.utcnow()
|
||||||
|
future = now + datetime.timedelta(seconds=token_duration)
|
||||||
|
expiry_date = utils.isotime(future, subsecond=True)
|
||||||
|
|
||||||
|
# add Access Token
|
||||||
|
ref = {}
|
||||||
|
ref['id'] = access_token_id
|
||||||
|
ref['access_secret'] = access_token_secret
|
||||||
|
ref['authorizing_user_id'] = token_dict['authorizing_user_id']
|
||||||
|
ref['project_id'] = token_dict['requested_project_id']
|
||||||
|
ref['role_ids'] = token_dict['role_ids']
|
||||||
|
ref['consumer_id'] = token_dict['consumer_id']
|
||||||
|
ref['expires_at'] = expiry_date
|
||||||
|
token_ref = AccessToken.from_dict(ref)
|
||||||
|
session.add(token_ref)
|
||||||
|
|
||||||
|
# remove request token, it's been used
|
||||||
|
session.delete(req_token_ref)
|
||||||
|
|
||||||
|
return token_ref.to_dict()
|
||||||
|
|
||||||
|
def _get_access_token(self, session, access_token_id):
|
||||||
|
token_ref = session.query(AccessToken).get(access_token_id)
|
||||||
|
if token_ref is None:
|
||||||
|
raise exception.NotFound(_('Access token not found'))
|
||||||
|
return token_ref
|
||||||
|
|
||||||
|
def get_access_token(self, access_token_id):
|
||||||
|
session = sql.get_session()
|
||||||
|
token_ref = self._get_access_token(session, access_token_id)
|
||||||
|
return token_ref.to_dict()
|
||||||
|
|
||||||
|
def list_access_tokens(self, user_id):
|
||||||
|
session = sql.get_session()
|
||||||
|
q = session.query(AccessToken)
|
||||||
|
user_auths = q.filter_by(authorizing_user_id=user_id)
|
||||||
|
return [core.filter_token(x.to_dict()) for x in user_auths]
|
||||||
|
|
||||||
|
def delete_access_token(self, user_id, access_token_id):
|
||||||
|
session = sql.get_session()
|
||||||
|
with session.begin():
|
||||||
|
token_ref = self._get_access_token(session, access_token_id)
|
||||||
|
token_dict = token_ref.to_dict()
|
||||||
|
if token_dict['authorizing_user_id'] != user_id:
|
||||||
|
raise exception.Unauthorized(_('User IDs do not match'))
|
||||||
|
|
||||||
|
session.delete(token_ref)
|
|
@ -22,11 +22,11 @@ from keystone.common import controller
|
||||||
from keystone.common import dependency
|
from keystone.common import dependency
|
||||||
from keystone.common import utils
|
from keystone.common import utils
|
||||||
from keystone.common import wsgi
|
from keystone.common import wsgi
|
||||||
from keystone.contrib.oauth1 import core as oauth1
|
|
||||||
from keystone.contrib.oauth1 import validator
|
|
||||||
from keystone import exception
|
from keystone import exception
|
||||||
from keystone.i18n import _
|
from keystone.i18n import _
|
||||||
from keystone import notifications
|
from keystone import notifications
|
||||||
|
from keystone.oauth1 import core as oauth1
|
||||||
|
from keystone.oauth1 import validator
|
||||||
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
|
@ -0,0 +1,154 @@
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import functools
|
||||||
|
|
||||||
|
from keystone.common import json_home
|
||||||
|
from keystone.common import wsgi
|
||||||
|
from keystone.oauth1 import controllers
|
||||||
|
|
||||||
|
|
||||||
|
build_resource_relation = functools.partial(
|
||||||
|
json_home.build_v3_extension_resource_relation,
|
||||||
|
extension_name='OS-OAUTH1', extension_version='1.0')
|
||||||
|
|
||||||
|
build_parameter_relation = functools.partial(
|
||||||
|
json_home.build_v3_extension_parameter_relation,
|
||||||
|
extension_name='OS-OAUTH1', extension_version='1.0')
|
||||||
|
|
||||||
|
ACCESS_TOKEN_ID_PARAMETER_RELATION = build_parameter_relation(
|
||||||
|
parameter_name='access_token_id')
|
||||||
|
|
||||||
|
|
||||||
|
class Routers(wsgi.RoutersBase):
|
||||||
|
"""API Endpoints for the OAuth1 extension.
|
||||||
|
|
||||||
|
The goal of this extension is to allow third-party service providers
|
||||||
|
to acquire tokens with a limited subset of a user's roles for acting
|
||||||
|
on behalf of that user. This is done using an oauth-similar flow and
|
||||||
|
api.
|
||||||
|
|
||||||
|
The API looks like::
|
||||||
|
|
||||||
|
# Basic admin-only consumer crud
|
||||||
|
POST /OS-OAUTH1/consumers
|
||||||
|
GET /OS-OAUTH1/consumers
|
||||||
|
PATCH /OS-OAUTH1/consumers/{consumer_id}
|
||||||
|
GET /OS-OAUTH1/consumers/{consumer_id}
|
||||||
|
DELETE /OS-OAUTH1/consumers/{consumer_id}
|
||||||
|
|
||||||
|
# User access token crud
|
||||||
|
GET /users/{user_id}/OS-OAUTH1/access_tokens
|
||||||
|
GET /users/{user_id}/OS-OAUTH1/access_tokens/{access_token_id}
|
||||||
|
GET /users/{user_id}/OS-OAUTH1/access_tokens/{access_token_id}/roles
|
||||||
|
GET /users/{user_id}/OS-OAUTH1/access_tokens
|
||||||
|
/{access_token_id}/roles/{role_id}
|
||||||
|
DELETE /users/{user_id}/OS-OAUTH1/access_tokens/{access_token_id}
|
||||||
|
|
||||||
|
# OAuth interfaces
|
||||||
|
POST /OS-OAUTH1/request_token # create a request token
|
||||||
|
PUT /OS-OAUTH1/authorize # authorize a request token
|
||||||
|
POST /OS-OAUTH1/access_token # create an access token
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def append_v3_routers(self, mapper, routers):
|
||||||
|
consumer_controller = controllers.ConsumerCrudV3()
|
||||||
|
access_token_controller = controllers.AccessTokenCrudV3()
|
||||||
|
access_token_roles_controller = controllers.AccessTokenRolesV3()
|
||||||
|
oauth_controller = controllers.OAuthControllerV3()
|
||||||
|
|
||||||
|
# basic admin-only consumer crud
|
||||||
|
self._add_resource(
|
||||||
|
mapper, consumer_controller,
|
||||||
|
path='/OS-OAUTH1/consumers',
|
||||||
|
get_action='list_consumers',
|
||||||
|
post_action='create_consumer',
|
||||||
|
rel=build_resource_relation(resource_name='consumers'))
|
||||||
|
self._add_resource(
|
||||||
|
mapper, consumer_controller,
|
||||||
|
path='/OS-OAUTH1/consumers/{consumer_id}',
|
||||||
|
get_action='get_consumer',
|
||||||
|
patch_action='update_consumer',
|
||||||
|
delete_action='delete_consumer',
|
||||||
|
rel=build_resource_relation(resource_name='consumer'),
|
||||||
|
path_vars={
|
||||||
|
'consumer_id':
|
||||||
|
build_parameter_relation(parameter_name='consumer_id'),
|
||||||
|
})
|
||||||
|
|
||||||
|
# user access token crud
|
||||||
|
self._add_resource(
|
||||||
|
mapper, access_token_controller,
|
||||||
|
path='/users/{user_id}/OS-OAUTH1/access_tokens',
|
||||||
|
get_action='list_access_tokens',
|
||||||
|
rel=build_resource_relation(resource_name='user_access_tokens'),
|
||||||
|
path_vars={
|
||||||
|
'user_id': json_home.Parameters.USER_ID,
|
||||||
|
})
|
||||||
|
self._add_resource(
|
||||||
|
mapper, access_token_controller,
|
||||||
|
path='/users/{user_id}/OS-OAUTH1/access_tokens/{access_token_id}',
|
||||||
|
get_action='get_access_token',
|
||||||
|
delete_action='delete_access_token',
|
||||||
|
rel=build_resource_relation(resource_name='user_access_token'),
|
||||||
|
path_vars={
|
||||||
|
'access_token_id': ACCESS_TOKEN_ID_PARAMETER_RELATION,
|
||||||
|
'user_id': json_home.Parameters.USER_ID,
|
||||||
|
})
|
||||||
|
self._add_resource(
|
||||||
|
mapper, access_token_roles_controller,
|
||||||
|
path='/users/{user_id}/OS-OAUTH1/access_tokens/{access_token_id}/'
|
||||||
|
'roles',
|
||||||
|
get_action='list_access_token_roles',
|
||||||
|
rel=build_resource_relation(
|
||||||
|
resource_name='user_access_token_roles'),
|
||||||
|
path_vars={
|
||||||
|
'access_token_id': ACCESS_TOKEN_ID_PARAMETER_RELATION,
|
||||||
|
'user_id': json_home.Parameters.USER_ID,
|
||||||
|
})
|
||||||
|
self._add_resource(
|
||||||
|
mapper, access_token_roles_controller,
|
||||||
|
path='/users/{user_id}/OS-OAUTH1/access_tokens/{access_token_id}/'
|
||||||
|
'roles/{role_id}',
|
||||||
|
get_action='get_access_token_role',
|
||||||
|
rel=build_resource_relation(
|
||||||
|
resource_name='user_access_token_role'),
|
||||||
|
path_vars={
|
||||||
|
'access_token_id': ACCESS_TOKEN_ID_PARAMETER_RELATION,
|
||||||
|
'role_id': json_home.Parameters.ROLE_ID,
|
||||||
|
'user_id': json_home.Parameters.USER_ID,
|
||||||
|
})
|
||||||
|
|
||||||
|
# oauth flow calls
|
||||||
|
self._add_resource(
|
||||||
|
mapper, oauth_controller,
|
||||||
|
path='/OS-OAUTH1/request_token',
|
||||||
|
post_action='create_request_token',
|
||||||
|
rel=build_resource_relation(resource_name='request_tokens'))
|
||||||
|
self._add_resource(
|
||||||
|
mapper, oauth_controller,
|
||||||
|
path='/OS-OAUTH1/access_token',
|
||||||
|
post_action='create_access_token',
|
||||||
|
rel=build_resource_relation(resource_name='access_tokens'))
|
||||||
|
self._add_resource(
|
||||||
|
mapper, oauth_controller,
|
||||||
|
path='/OS-OAUTH1/authorize/{request_token_id}',
|
||||||
|
path_vars={
|
||||||
|
'request_token_id':
|
||||||
|
build_parameter_relation(parameter_name='request_token_id')
|
||||||
|
},
|
||||||
|
put_action='authorize_request_token',
|
||||||
|
rel=build_resource_relation(
|
||||||
|
resource_name='authorize_request_token'))
|
|
@ -18,8 +18,8 @@ from oslo_log import log
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from keystone.common import dependency
|
from keystone.common import dependency
|
||||||
from keystone.contrib.oauth1 import core as oauth1
|
|
||||||
from keystone import exception
|
from keystone import exception
|
||||||
|
from keystone.oauth1 import core as oauth1
|
||||||
|
|
||||||
|
|
||||||
METHOD_NAME = 'oauth_validator'
|
METHOD_NAME = 'oauth_validator'
|
|
@ -15,12 +15,12 @@ from keystone import auth
|
||||||
from keystone import catalog
|
from keystone import catalog
|
||||||
from keystone.common import cache
|
from keystone.common import cache
|
||||||
from keystone.contrib import endpoint_filter
|
from keystone.contrib import endpoint_filter
|
||||||
from keystone.contrib import oauth1
|
|
||||||
from keystone.contrib import revoke
|
from keystone.contrib import revoke
|
||||||
from keystone import credential
|
from keystone import credential
|
||||||
from keystone import endpoint_policy
|
from keystone import endpoint_policy
|
||||||
from keystone import federation
|
from keystone import federation
|
||||||
from keystone import identity
|
from keystone import identity
|
||||||
|
from keystone import oauth1
|
||||||
from keystone import policy
|
from keystone import policy
|
||||||
from keystone import resource
|
from keystone import resource
|
||||||
from keystone import token
|
from keystone import token
|
||||||
|
|
|
@ -15,16 +15,19 @@
|
||||||
import copy
|
import copy
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
import mock
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
|
from oslo_log import versionutils
|
||||||
from oslo_serialization import jsonutils
|
from oslo_serialization import jsonutils
|
||||||
from pycadf import cadftaxonomy
|
from pycadf import cadftaxonomy
|
||||||
from six.moves import http_client
|
from six.moves import http_client
|
||||||
from six.moves import urllib
|
from six.moves import urllib
|
||||||
|
|
||||||
from keystone.contrib import oauth1
|
from keystone.contrib.oauth1 import routers
|
||||||
from keystone.contrib.oauth1 import controllers
|
|
||||||
from keystone.contrib.oauth1 import core
|
|
||||||
from keystone import exception
|
from keystone import exception
|
||||||
|
from keystone import oauth1
|
||||||
|
from keystone.oauth1 import controllers
|
||||||
|
from keystone.oauth1 import core
|
||||||
from keystone.tests import unit
|
from keystone.tests import unit
|
||||||
from keystone.tests.unit.common import test_notifications
|
from keystone.tests.unit.common import test_notifications
|
||||||
from keystone.tests.unit.ksfixtures import temporaryfile
|
from keystone.tests.unit.ksfixtures import temporaryfile
|
||||||
|
@ -34,10 +37,17 @@ from keystone.tests.unit import test_v3
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
|
|
||||||
|
|
||||||
class OAuth1Tests(test_v3.RestfulTestCase):
|
class OAuth1ContribTests(test_v3.RestfulTestCase):
|
||||||
|
|
||||||
EXTENSION_NAME = 'oauth1'
|
@mock.patch.object(versionutils, 'report_deprecated_feature')
|
||||||
EXTENSION_TO_ADD = 'oauth1_extension'
|
def test_exception_happens(self, mock_deprecator):
|
||||||
|
routers.OAuth1Extension(mock.ANY)
|
||||||
|
mock_deprecator.assert_called_once_with(mock.ANY, mock.ANY)
|
||||||
|
args, _kwargs = mock_deprecator.call_args
|
||||||
|
self.assertIn("Remove oauth1_extension from", args[1])
|
||||||
|
|
||||||
|
|
||||||
|
class OAuth1Tests(test_v3.RestfulTestCase):
|
||||||
|
|
||||||
CONSUMER_URL = '/OS-OAUTH1/consumers'
|
CONSUMER_URL = '/OS-OAUTH1/consumers'
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ from keystone import endpoint_policy
|
||||||
from keystone import federation
|
from keystone import federation
|
||||||
from keystone.i18n import _LW
|
from keystone.i18n import _LW
|
||||||
from keystone import identity
|
from keystone import identity
|
||||||
|
from keystone import oauth1
|
||||||
from keystone import policy
|
from keystone import policy
|
||||||
from keystone import resource
|
from keystone import resource
|
||||||
from keystone import token
|
from keystone import token
|
||||||
|
@ -131,7 +132,8 @@ def v3_app_factory(global_conf, **local_conf):
|
||||||
identity,
|
identity,
|
||||||
policy,
|
policy,
|
||||||
resource,
|
resource,
|
||||||
federation]
|
federation,
|
||||||
|
oauth1]
|
||||||
|
|
||||||
if CONF.trust.enabled:
|
if CONF.trust.enabled:
|
||||||
router_modules.append(trust)
|
router_modules.append(trust)
|
||||||
|
|
|
@ -165,7 +165,7 @@ keystone.federation =
|
||||||
sql = keystone.federation.backends.sql:Federation
|
sql = keystone.federation.backends.sql:Federation
|
||||||
|
|
||||||
keystone.oauth1 =
|
keystone.oauth1 =
|
||||||
sql = keystone.contrib.oauth1.backends.sql:OAuth1
|
sql = keystone.oauth1.backends.sql:OAuth1
|
||||||
|
|
||||||
keystone.revoke =
|
keystone.revoke =
|
||||||
kvs = keystone.contrib.revoke.backends.kvs:Revoke
|
kvs = keystone.contrib.revoke.backends.kvs:Revoke
|
||||||
|
|
Loading…
Reference in New Issue