Move federation extension into keystone core

Remove federation 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.

Some instances of federation constants were removed because
they were causing a circular dependency, these can be refactored in
a later patch.

DocImpact: You should no longer run the migrations for this extension
Implements: bp move-extensions

Co-Authored-By: Nithya Renganathan <narengan@us.ibm.com>

Change-Id: If5857a6ee4c7c527929069b25beab40f4c5d87e2
This commit is contained in:
Steve Martinelli 2015-10-11 03:01:05 -04:00 committed by Dave Chen
parent baf76a387c
commit cbefe7c7b8
32 changed files with 714 additions and 655 deletions

View File

@ -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:federation_extension]
use = egg:keystone#federation_extension
[filter:oauth1_extension] [filter:oauth1_extension]
use = egg:keystone#oauth1_extension use = egg:keystone#oauth1_extension
@ -76,7 +73,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 federation_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 oauth1_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

View File

@ -28,7 +28,6 @@ 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 import config from keystone import config
from keystone.contrib.federation import constants as federation_constants
from keystone import exception from keystone import exception
from keystone.i18n import _, _LI, _LW from keystone.i18n import _, _LI, _LW
from keystone.resource import controllers as resource_controllers from keystone.resource import controllers as resource_controllers
@ -423,7 +422,8 @@ class Auth(controller.V3Controller):
return return
# Skip scoping when unscoped federated token is being issued # Skip scoping when unscoped federated token is being issued
if federation_constants.IDENTITY_PROVIDER in auth_context: # FIXME(stevemar): Use constants from keystone.federation.constants
if 'OS-FEDERATION:identity_provider' in auth_context:
return return
# Do not scope if request is for explicitly unscoped token # Do not scope if request is for explicitly unscoped token

View File

@ -19,9 +19,9 @@ from six.moves.urllib import parse
from keystone import auth from keystone import auth
from keystone.auth import plugins as auth_plugins from keystone.auth import plugins as auth_plugins
from keystone.common import dependency from keystone.common import dependency
from keystone.contrib.federation import constants as federation_constants
from keystone.contrib.federation import utils
from keystone import exception from keystone import exception
from keystone.federation import constants as federation_constants
from keystone.federation import utils
from keystone.i18n import _ from keystone.i18n import _
from keystone.models import token_model from keystone.models import token_model
from keystone import notifications from keystone import notifications

View File

@ -29,6 +29,8 @@ from keystone.common.sql import migration_helpers
from keystone.common import utils from keystone.common import utils
from keystone import config from keystone import config
from keystone import exception from keystone import exception
from keystone.federation import idp
from keystone.federation import utils as mapping_engine
from keystone.i18n import _, _LW from keystone.i18n import _, _LW
from keystone.server import backends from keystone.server import backends
from keystone import token from keystone import token
@ -538,9 +540,6 @@ class SamlIdentityProviderMetadata(BaseApp):
@staticmethod @staticmethod
def main(): def main():
# NOTE(marek-denis): Since federation is currently an extension import
# corresponding modules only when they are really going to be used.
from keystone.contrib.federation import idp
metadata = idp.MetadataGenerator().generate_metadata() metadata = idp.MetadataGenerator().generate_metadata()
print(metadata.to_string()) print(metadata.to_string())
@ -598,7 +597,6 @@ class MappingEngineTester(BaseApp):
@classmethod @classmethod
def main(cls): def main(cls):
from keystone.contrib.federation import utils as mapping_engine
if not CONF.command.engine_debug: if not CONF.command.engine_debug:
mapping_engine.LOG.logger.setLevel('WARN') mapping_engine.LOG.logger.setLevel('WARN')

View File

@ -20,9 +20,9 @@ from oslo_log import log
from keystone.auth import controllers from keystone.auth import controllers
from keystone.common import dependency from keystone.common import dependency
from keystone.contrib.federation import constants as federation_constants
from keystone.contrib.federation import utils
from keystone import exception from keystone import exception
from keystone.federation import constants as federation_constants
from keystone.federation import utils
from keystone.i18n import _ from keystone.i18n import _

View File

@ -1,15 +0,0 @@
# Copyright 2014 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.federation.core import * # noqa

View File

@ -12,355 +12,18 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from oslo_serialization import jsonutils from oslo_log import versionutils
from keystone.common import sql from keystone.federation.backends import sql
from keystone.contrib.federation import core
from keystone import exception _OLD = "keystone.contrib.federation.backends.sql.Federation"
from sqlalchemy import orm _NEW = "sql"
class FederationProtocolModel(sql.ModelBase, sql.DictBase): class Federation(sql.Federation):
__tablename__ = 'federation_protocol'
attributes = ['id', 'idp_id', 'mapping_id']
mutable_attributes = frozenset(['mapping_id'])
id = sql.Column(sql.String(64), primary_key=True) @versionutils.deprecated(versionutils.deprecated.MITAKA,
idp_id = sql.Column(sql.String(64), sql.ForeignKey('identity_provider.id', in_favor_of=_NEW,
ondelete='CASCADE'), primary_key=True) what=_OLD)
mapping_id = sql.Column(sql.String(64), nullable=False) def __init__(self, *args, **kwargs):
super(Federation, self).__init__(*args, **kwargs)
@classmethod
def from_dict(cls, dictionary):
new_dictionary = dictionary.copy()
return cls(**new_dictionary)
def to_dict(self):
"""Return a dictionary with model's attributes."""
d = dict()
for attr in self.__class__.attributes:
d[attr] = getattr(self, attr)
return d
class IdentityProviderModel(sql.ModelBase, sql.DictBase):
__tablename__ = 'identity_provider'
attributes = ['id', 'enabled', 'description', 'remote_ids']
mutable_attributes = frozenset(['description', 'enabled', 'remote_ids'])
id = sql.Column(sql.String(64), primary_key=True)
enabled = sql.Column(sql.Boolean, nullable=False)
description = sql.Column(sql.Text(), nullable=True)
remote_ids = orm.relationship('IdPRemoteIdsModel',
order_by='IdPRemoteIdsModel.remote_id',
cascade='all, delete-orphan')
@classmethod
def from_dict(cls, dictionary):
new_dictionary = dictionary.copy()
remote_ids_list = new_dictionary.pop('remote_ids', None)
if not remote_ids_list:
remote_ids_list = []
identity_provider = cls(**new_dictionary)
remote_ids = []
# NOTE(fmarco76): the remote_ids_list contains only remote ids
# associated with the IdP because of the "relationship" established in
# sqlalchemy and corresponding to the FK in the idp_remote_ids table
for remote in remote_ids_list:
remote_ids.append(IdPRemoteIdsModel(remote_id=remote))
identity_provider.remote_ids = remote_ids
return identity_provider
def to_dict(self):
"""Return a dictionary with model's attributes."""
d = dict()
for attr in self.__class__.attributes:
d[attr] = getattr(self, attr)
d['remote_ids'] = []
for remote in self.remote_ids:
d['remote_ids'].append(remote.remote_id)
return d
class IdPRemoteIdsModel(sql.ModelBase, sql.DictBase):
__tablename__ = 'idp_remote_ids'
attributes = ['idp_id', 'remote_id']
mutable_attributes = frozenset(['idp_id', 'remote_id'])
idp_id = sql.Column(sql.String(64),
sql.ForeignKey('identity_provider.id',
ondelete='CASCADE'))
remote_id = sql.Column(sql.String(255),
primary_key=True)
@classmethod
def from_dict(cls, dictionary):
new_dictionary = dictionary.copy()
return cls(**new_dictionary)
def to_dict(self):
"""Return a dictionary with model's attributes."""
d = dict()
for attr in self.__class__.attributes:
d[attr] = getattr(self, attr)
return d
class MappingModel(sql.ModelBase, sql.DictBase):
__tablename__ = 'mapping'
attributes = ['id', 'rules']
id = sql.Column(sql.String(64), primary_key=True)
rules = sql.Column(sql.JsonBlob(), nullable=False)
@classmethod
def from_dict(cls, dictionary):
new_dictionary = dictionary.copy()
new_dictionary['rules'] = jsonutils.dumps(new_dictionary['rules'])
return cls(**new_dictionary)
def to_dict(self):
"""Return a dictionary with model's attributes."""
d = dict()
for attr in self.__class__.attributes:
d[attr] = getattr(self, attr)
d['rules'] = jsonutils.loads(d['rules'])
return d
class ServiceProviderModel(sql.ModelBase, sql.DictBase):
__tablename__ = 'service_provider'
attributes = ['auth_url', 'id', 'enabled', 'description',
'relay_state_prefix', 'sp_url']
mutable_attributes = frozenset(['auth_url', 'description', 'enabled',
'relay_state_prefix', 'sp_url'])
id = sql.Column(sql.String(64), primary_key=True)
enabled = sql.Column(sql.Boolean, nullable=False)
description = sql.Column(sql.Text(), nullable=True)
auth_url = sql.Column(sql.String(256), nullable=False)
sp_url = sql.Column(sql.String(256), nullable=False)
relay_state_prefix = sql.Column(sql.String(256), nullable=False)
@classmethod
def from_dict(cls, dictionary):
new_dictionary = dictionary.copy()
return cls(**new_dictionary)
def to_dict(self):
"""Return a dictionary with model's attributes."""
d = dict()
for attr in self.__class__.attributes:
d[attr] = getattr(self, attr)
return d
class Federation(core.FederationDriverV8):
# Identity Provider CRUD
@sql.handle_conflicts(conflict_type='identity_provider')
def create_idp(self, idp_id, idp):
idp['id'] = idp_id
with sql.transaction() as session:
idp_ref = IdentityProviderModel.from_dict(idp)
session.add(idp_ref)
return idp_ref.to_dict()
def delete_idp(self, idp_id):
with sql.transaction() as session:
self._delete_assigned_protocols(session, idp_id)
idp_ref = self._get_idp(session, idp_id)
session.delete(idp_ref)
def _get_idp(self, session, idp_id):
idp_ref = session.query(IdentityProviderModel).get(idp_id)
if not idp_ref:
raise exception.IdentityProviderNotFound(idp_id=idp_id)
return idp_ref
def _get_idp_from_remote_id(self, session, remote_id):
q = session.query(IdPRemoteIdsModel)
q = q.filter_by(remote_id=remote_id)
try:
return q.one()
except sql.NotFound:
raise exception.IdentityProviderNotFound(idp_id=remote_id)
def list_idps(self):
with sql.transaction() as session:
idps = session.query(IdentityProviderModel)
idps_list = [idp.to_dict() for idp in idps]
return idps_list
def get_idp(self, idp_id):
with sql.transaction() as session:
idp_ref = self._get_idp(session, idp_id)
return idp_ref.to_dict()
def get_idp_from_remote_id(self, remote_id):
with sql.transaction() as session:
ref = self._get_idp_from_remote_id(session, remote_id)
return ref.to_dict()
def update_idp(self, idp_id, idp):
with sql.transaction() as session:
idp_ref = self._get_idp(session, idp_id)
old_idp = idp_ref.to_dict()
old_idp.update(idp)
new_idp = IdentityProviderModel.from_dict(old_idp)
for attr in IdentityProviderModel.mutable_attributes:
setattr(idp_ref, attr, getattr(new_idp, attr))
return idp_ref.to_dict()
# Protocol CRUD
def _get_protocol(self, session, idp_id, protocol_id):
q = session.query(FederationProtocolModel)
q = q.filter_by(id=protocol_id, idp_id=idp_id)
try:
return q.one()
except sql.NotFound:
kwargs = {'protocol_id': protocol_id,
'idp_id': idp_id}
raise exception.FederatedProtocolNotFound(**kwargs)
@sql.handle_conflicts(conflict_type='federation_protocol')
def create_protocol(self, idp_id, protocol_id, protocol):
protocol['id'] = protocol_id
protocol['idp_id'] = idp_id
with sql.transaction() as session:
self._get_idp(session, idp_id)
protocol_ref = FederationProtocolModel.from_dict(protocol)
session.add(protocol_ref)
return protocol_ref.to_dict()
def update_protocol(self, idp_id, protocol_id, protocol):
with sql.transaction() as session:
proto_ref = self._get_protocol(session, idp_id, protocol_id)
old_proto = proto_ref.to_dict()
old_proto.update(protocol)
new_proto = FederationProtocolModel.from_dict(old_proto)
for attr in FederationProtocolModel.mutable_attributes:
setattr(proto_ref, attr, getattr(new_proto, attr))
return proto_ref.to_dict()
def get_protocol(self, idp_id, protocol_id):
with sql.transaction() as session:
protocol_ref = self._get_protocol(session, idp_id, protocol_id)
return protocol_ref.to_dict()
def list_protocols(self, idp_id):
with sql.transaction() as session:
q = session.query(FederationProtocolModel)
q = q.filter_by(idp_id=idp_id)
protocols = [protocol.to_dict() for protocol in q]
return protocols
def delete_protocol(self, idp_id, protocol_id):
with sql.transaction() as session:
key_ref = self._get_protocol(session, idp_id, protocol_id)
session.delete(key_ref)
def _delete_assigned_protocols(self, session, idp_id):
query = session.query(FederationProtocolModel)
query = query.filter_by(idp_id=idp_id)
query.delete()
# Mapping CRUD
def _get_mapping(self, session, mapping_id):
mapping_ref = session.query(MappingModel).get(mapping_id)
if not mapping_ref:
raise exception.MappingNotFound(mapping_id=mapping_id)
return mapping_ref
@sql.handle_conflicts(conflict_type='mapping')
def create_mapping(self, mapping_id, mapping):
ref = {}
ref['id'] = mapping_id
ref['rules'] = mapping.get('rules')
with sql.transaction() as session:
mapping_ref = MappingModel.from_dict(ref)
session.add(mapping_ref)
return mapping_ref.to_dict()
def delete_mapping(self, mapping_id):
with sql.transaction() as session:
mapping_ref = self._get_mapping(session, mapping_id)
session.delete(mapping_ref)
def list_mappings(self):
with sql.transaction() as session:
mappings = session.query(MappingModel)
return [x.to_dict() for x in mappings]
def get_mapping(self, mapping_id):
with sql.transaction() as session:
mapping_ref = self._get_mapping(session, mapping_id)
return mapping_ref.to_dict()
@sql.handle_conflicts(conflict_type='mapping')
def update_mapping(self, mapping_id, mapping):
ref = {}
ref['id'] = mapping_id
ref['rules'] = mapping.get('rules')
with sql.transaction() as session:
mapping_ref = self._get_mapping(session, mapping_id)
old_mapping = mapping_ref.to_dict()
old_mapping.update(ref)
new_mapping = MappingModel.from_dict(old_mapping)
for attr in MappingModel.attributes:
setattr(mapping_ref, attr, getattr(new_mapping, attr))
return mapping_ref.to_dict()
def get_mapping_from_idp_and_protocol(self, idp_id, protocol_id):
with sql.transaction() as session:
protocol_ref = self._get_protocol(session, idp_id, protocol_id)
mapping_id = protocol_ref.mapping_id
mapping_ref = self._get_mapping(session, mapping_id)
return mapping_ref.to_dict()
# Service Provider CRUD
@sql.handle_conflicts(conflict_type='service_provider')
def create_sp(self, sp_id, sp):
sp['id'] = sp_id
with sql.transaction() as session:
sp_ref = ServiceProviderModel.from_dict(sp)
session.add(sp_ref)
return sp_ref.to_dict()
def delete_sp(self, sp_id):
with sql.transaction() as session:
sp_ref = self._get_sp(session, sp_id)
session.delete(sp_ref)
def _get_sp(self, session, sp_id):
sp_ref = session.query(ServiceProviderModel).get(sp_id)
if not sp_ref:
raise exception.ServiceProviderNotFound(sp_id=sp_id)
return sp_ref
def list_sps(self):
with sql.transaction() as session:
sps = session.query(ServiceProviderModel)
sps_list = [sp.to_dict() for sp in sps]
return sps_list
def get_sp(self, sp_id):
with sql.transaction() as session:
sp_ref = self._get_sp(session, sp_id)
return sp_ref.to_dict()
def update_sp(self, sp_id, sp):
with sql.transaction() as session:
sp_ref = self._get_sp(session, sp_id)
old_sp = sp_ref.to_dict()
old_sp.update(sp)
new_sp = ServiceProviderModel.from_dict(old_sp)
for attr in ServiceProviderModel.mutable_attributes:
setattr(sp_ref, attr, getattr(new_sp, attr))
return sp_ref.to_dict()
def get_enabled_service_providers(self):
with sql.transaction() as session:
service_providers = session.query(ServiceProviderModel)
service_providers = service_providers.filter_by(enabled=True)
return service_providers

View File

@ -10,243 +10,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.federation 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-FEDERATION', extension_version='1.0')
build_parameter_relation = functools.partial(
json_home.build_v3_extension_parameter_relation,
extension_name='OS-FEDERATION', extension_version='1.0')
IDP_ID_PARAMETER_RELATION = build_parameter_relation(parameter_name='idp_id')
PROTOCOL_ID_PARAMETER_RELATION = build_parameter_relation(
parameter_name='protocol_id')
SP_ID_PARAMETER_RELATION = build_parameter_relation(parameter_name='sp_id')
class FederationExtension(wsgi.V3ExtensionRouter): class FederationExtension(wsgi.Middleware):
"""API Endpoints for the Federation extension.
The API looks like:: def __init__(self, *args, **kwargs):
super(FederationExtension, self).__init__(*args, **kwargs)
PUT /OS-FEDERATION/identity_providers/{idp_id} msg = _("Remove federation_extension from the paste pipeline, the "
GET /OS-FEDERATION/identity_providers "federation extension is now always available. Update the "
GET /OS-FEDERATION/identity_providers/{idp_id} "[pipeline:api_v3] section in keystone-paste.ini accordingly, "
DELETE /OS-FEDERATION/identity_providers/{idp_id} "as it will be removed in the O release.")
PATCH /OS-FEDERATION/identity_providers/{idp_id} versionutils.report_deprecated_feature(LOG, msg)
PUT /OS-FEDERATION/identity_providers/
{idp_id}/protocols/{protocol_id}
GET /OS-FEDERATION/identity_providers/
{idp_id}/protocols
GET /OS-FEDERATION/identity_providers/
{idp_id}/protocols/{protocol_id}
PATCH /OS-FEDERATION/identity_providers/
{idp_id}/protocols/{protocol_id}
DELETE /OS-FEDERATION/identity_providers/
{idp_id}/protocols/{protocol_id}
PUT /OS-FEDERATION/mappings
GET /OS-FEDERATION/mappings
PATCH /OS-FEDERATION/mappings/{mapping_id}
GET /OS-FEDERATION/mappings/{mapping_id}
DELETE /OS-FEDERATION/mappings/{mapping_id}
GET /OS-FEDERATION/projects
GET /OS-FEDERATION/domains
PUT /OS-FEDERATION/service_providers/{sp_id}
GET /OS-FEDERATION/service_providers
GET /OS-FEDERATION/service_providers/{sp_id}
DELETE /OS-FEDERATION/service_providers/{sp_id}
PATCH /OS-FEDERATION/service_providers/{sp_id}
GET /OS-FEDERATION/identity_providers/{identity_provider}/
protocols/{protocol}/auth
POST /OS-FEDERATION/identity_providers/{identity_provider}/
protocols/{protocol}/auth
GET /auth/OS-FEDERATION/identity_providers/
{idp_id}/protocols/{protocol_id}/websso
?origin=https%3A//horizon.example.com
POST /auth/OS-FEDERATION/identity_providers/
{idp_id}/protocols/{protocol_id}/websso
?origin=https%3A//horizon.example.com
POST /auth/OS-FEDERATION/saml2
POST /auth/OS-FEDERATION/saml2/ecp
GET /OS-FEDERATION/saml2/metadata
GET /auth/OS-FEDERATION/websso/{protocol_id}
?origin=https%3A//horizon.example.com
POST /auth/OS-FEDERATION/websso/{protocol_id}
?origin=https%3A//horizon.example.com
"""
def _construct_url(self, suffix):
return "/OS-FEDERATION/%s" % suffix
def add_routes(self, mapper):
auth_controller = controllers.Auth()
idp_controller = controllers.IdentityProvider()
protocol_controller = controllers.FederationProtocol()
mapping_controller = controllers.MappingController()
project_controller = controllers.ProjectAssignmentV3()
domain_controller = controllers.DomainV3()
saml_metadata_controller = controllers.SAMLMetadataV3()
sp_controller = controllers.ServiceProvider()
# Identity Provider CRUD operations
self._add_resource(
mapper, idp_controller,
path=self._construct_url('identity_providers/{idp_id}'),
get_action='get_identity_provider',
put_action='create_identity_provider',
patch_action='update_identity_provider',
delete_action='delete_identity_provider',
rel=build_resource_relation(resource_name='identity_provider'),
path_vars={
'idp_id': IDP_ID_PARAMETER_RELATION,
})
self._add_resource(
mapper, idp_controller,
path=self._construct_url('identity_providers'),
get_action='list_identity_providers',
rel=build_resource_relation(resource_name='identity_providers'))
# Protocol CRUD operations
self._add_resource(
mapper, protocol_controller,
path=self._construct_url('identity_providers/{idp_id}/protocols/'
'{protocol_id}'),
get_action='get_protocol',
put_action='create_protocol',
patch_action='update_protocol',
delete_action='delete_protocol',
rel=build_resource_relation(
resource_name='identity_provider_protocol'),
path_vars={
'idp_id': IDP_ID_PARAMETER_RELATION,
'protocol_id': PROTOCOL_ID_PARAMETER_RELATION,
})
self._add_resource(
mapper, protocol_controller,
path=self._construct_url('identity_providers/{idp_id}/protocols'),
get_action='list_protocols',
rel=build_resource_relation(
resource_name='identity_provider_protocols'),
path_vars={
'idp_id': IDP_ID_PARAMETER_RELATION,
})
# Mapping CRUD operations
self._add_resource(
mapper, mapping_controller,
path=self._construct_url('mappings/{mapping_id}'),
get_action='get_mapping',
put_action='create_mapping',
patch_action='update_mapping',
delete_action='delete_mapping',
rel=build_resource_relation(resource_name='mapping'),
path_vars={
'mapping_id': build_parameter_relation(
parameter_name='mapping_id'),
})
self._add_resource(
mapper, mapping_controller,
path=self._construct_url('mappings'),
get_action='list_mappings',
rel=build_resource_relation(resource_name='mappings'))
# Service Providers CRUD operations
self._add_resource(
mapper, sp_controller,
path=self._construct_url('service_providers/{sp_id}'),
get_action='get_service_provider',
put_action='create_service_provider',
patch_action='update_service_provider',
delete_action='delete_service_provider',
rel=build_resource_relation(resource_name='service_provider'),
path_vars={
'sp_id': SP_ID_PARAMETER_RELATION,
})
self._add_resource(
mapper, sp_controller,
path=self._construct_url('service_providers'),
get_action='list_service_providers',
rel=build_resource_relation(resource_name='service_providers'))
self._add_resource(
mapper, domain_controller,
path=self._construct_url('domains'),
new_path='/auth/domains',
get_action='list_domains_for_groups',
rel=build_resource_relation(resource_name='domains'))
self._add_resource(
mapper, project_controller,
path=self._construct_url('projects'),
new_path='/auth/projects',
get_action='list_projects_for_groups',
rel=build_resource_relation(resource_name='projects'))
# Auth operations
self._add_resource(
mapper, auth_controller,
path=self._construct_url('identity_providers/{identity_provider}/'
'protocols/{protocol}/auth'),
get_post_action='federated_authentication',
rel=build_resource_relation(
resource_name='identity_provider_protocol_auth'),
path_vars={
'identity_provider': IDP_ID_PARAMETER_RELATION,
'protocol': PROTOCOL_ID_PARAMETER_RELATION,
})
self._add_resource(
mapper, auth_controller,
path='/auth' + self._construct_url('saml2'),
post_action='create_saml_assertion',
rel=build_resource_relation(resource_name='saml2'))
self._add_resource(
mapper, auth_controller,
path='/auth' + self._construct_url('saml2/ecp'),
post_action='create_ecp_assertion',
rel=build_resource_relation(resource_name='ecp'))
self._add_resource(
mapper, auth_controller,
path='/auth' + self._construct_url('websso/{protocol_id}'),
get_post_action='federated_sso_auth',
rel=build_resource_relation(resource_name='websso'),
path_vars={
'protocol_id': PROTOCOL_ID_PARAMETER_RELATION,
})
self._add_resource(
mapper, auth_controller,
path='/auth' + self._construct_url(
'identity_providers/{idp_id}/protocols/{protocol_id}/websso'),
get_post_action='federated_idp_specific_sso_auth',
rel=build_resource_relation(resource_name='identity_providers'),
path_vars={
'idp_id': IDP_ID_PARAMETER_RELATION,
'protocol_id': PROTOCOL_ID_PARAMETER_RELATION,
})
# Keystone-Identity-Provider metadata endpoint
self._add_resource(
mapper, saml_metadata_controller,
path=self._construct_url('saml2/metadata'),
get_action='get_metadata',
rel=build_resource_relation(resource_name='metadata'))

View File

@ -0,0 +1,16 @@
# Copyright 2014 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.federation.core import * # noqa
from keystone.federation import routers # noqa

View File

View File

@ -0,0 +1,366 @@
# Copyright 2014 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 oslo_serialization import jsonutils
from sqlalchemy import orm
from keystone.common import sql
from keystone import exception
from keystone.federation import core
class FederationProtocolModel(sql.ModelBase, sql.DictBase):
__tablename__ = 'federation_protocol'
attributes = ['id', 'idp_id', 'mapping_id']
mutable_attributes = frozenset(['mapping_id'])
id = sql.Column(sql.String(64), primary_key=True)
idp_id = sql.Column(sql.String(64), sql.ForeignKey('identity_provider.id',
ondelete='CASCADE'), primary_key=True)
mapping_id = sql.Column(sql.String(64), nullable=False)
@classmethod
def from_dict(cls, dictionary):
new_dictionary = dictionary.copy()
return cls(**new_dictionary)
def to_dict(self):
"""Return a dictionary with model's attributes."""
d = dict()
for attr in self.__class__.attributes:
d[attr] = getattr(self, attr)
return d
class IdentityProviderModel(sql.ModelBase, sql.DictBase):
__tablename__ = 'identity_provider'
attributes = ['id', 'enabled', 'description', 'remote_ids']
mutable_attributes = frozenset(['description', 'enabled', 'remote_ids'])
id = sql.Column(sql.String(64), primary_key=True)
enabled = sql.Column(sql.Boolean, nullable=False)
description = sql.Column(sql.Text(), nullable=True)
remote_ids = orm.relationship('IdPRemoteIdsModel',
order_by='IdPRemoteIdsModel.remote_id',
cascade='all, delete-orphan')
@classmethod
def from_dict(cls, dictionary):
new_dictionary = dictionary.copy()
remote_ids_list = new_dictionary.pop('remote_ids', None)
if not remote_ids_list:
remote_ids_list = []
identity_provider = cls(**new_dictionary)
remote_ids = []
# NOTE(fmarco76): the remote_ids_list contains only remote ids
# associated with the IdP because of the "relationship" established in
# sqlalchemy and corresponding to the FK in the idp_remote_ids table
for remote in remote_ids_list:
remote_ids.append(IdPRemoteIdsModel(remote_id=remote))
identity_provider.remote_ids = remote_ids
return identity_provider
def to_dict(self):
"""Return a dictionary with model's attributes."""
d = dict()
for attr in self.__class__.attributes:
d[attr] = getattr(self, attr)
d['remote_ids'] = []
for remote in self.remote_ids:
d['remote_ids'].append(remote.remote_id)
return d
class IdPRemoteIdsModel(sql.ModelBase, sql.DictBase):
__tablename__ = 'idp_remote_ids'
attributes = ['idp_id', 'remote_id']
mutable_attributes = frozenset(['idp_id', 'remote_id'])
idp_id = sql.Column(sql.String(64),
sql.ForeignKey('identity_provider.id',
ondelete='CASCADE'))
remote_id = sql.Column(sql.String(255),
primary_key=True)
@classmethod
def from_dict(cls, dictionary):
new_dictionary = dictionary.copy()
return cls(**new_dictionary)
def to_dict(self):
"""Return a dictionary with model's attributes."""
d = dict()
for attr in self.__class__.attributes:
d[attr] = getattr(self, attr)
return d
class MappingModel(sql.ModelBase, sql.DictBase):
__tablename__ = 'mapping'
attributes = ['id', 'rules']
id = sql.Column(sql.String(64), primary_key=True)
rules = sql.Column(sql.JsonBlob(), nullable=False)
@classmethod
def from_dict(cls, dictionary):
new_dictionary = dictionary.copy()
new_dictionary['rules'] = jsonutils.dumps(new_dictionary['rules'])
return cls(**new_dictionary)
def to_dict(self):
"""Return a dictionary with model's attributes."""
d = dict()
for attr in self.__class__.attributes:
d[attr] = getattr(self, attr)
d['rules'] = jsonutils.loads(d['rules'])
return d
class ServiceProviderModel(sql.ModelBase, sql.DictBase):
__tablename__ = 'service_provider'
attributes = ['auth_url', 'id', 'enabled', 'description',
'relay_state_prefix', 'sp_url']
mutable_attributes = frozenset(['auth_url', 'description', 'enabled',
'relay_state_prefix', 'sp_url'])
id = sql.Column(sql.String(64), primary_key=True)
enabled = sql.Column(sql.Boolean, nullable=False)
description = sql.Column(sql.Text(), nullable=True)
auth_url = sql.Column(sql.String(256), nullable=False)
sp_url = sql.Column(sql.String(256), nullable=False)
relay_state_prefix = sql.Column(sql.String(256), nullable=False)
@classmethod
def from_dict(cls, dictionary):
new_dictionary = dictionary.copy()
return cls(**new_dictionary)
def to_dict(self):
"""Return a dictionary with model's attributes."""
d = dict()
for attr in self.__class__.attributes:
d[attr] = getattr(self, attr)
return d
class Federation(core.FederationDriverV8):
# Identity Provider CRUD
@sql.handle_conflicts(conflict_type='identity_provider')
def create_idp(self, idp_id, idp):
idp['id'] = idp_id
with sql.transaction() as session:
idp_ref = IdentityProviderModel.from_dict(idp)
session.add(idp_ref)
return idp_ref.to_dict()
def delete_idp(self, idp_id):
with sql.transaction() as session:
self._delete_assigned_protocols(session, idp_id)
idp_ref = self._get_idp(session, idp_id)
session.delete(idp_ref)
def _get_idp(self, session, idp_id):
idp_ref = session.query(IdentityProviderModel).get(idp_id)
if not idp_ref:
raise exception.IdentityProviderNotFound(idp_id=idp_id)
return idp_ref
def _get_idp_from_remote_id(self, session, remote_id):
q = session.query(IdPRemoteIdsModel)
q = q.filter_by(remote_id=remote_id)
try:
return q.one()
except sql.NotFound:
raise exception.IdentityProviderNotFound(idp_id=remote_id)
def list_idps(self):
with sql.transaction() as session:
idps = session.query(IdentityProviderModel)
idps_list = [idp.to_dict() for idp in idps]
return idps_list
def get_idp(self, idp_id):
with sql.transaction() as session:
idp_ref = self._get_idp(session, idp_id)
return idp_ref.to_dict()
def get_idp_from_remote_id(self, remote_id):
with sql.transaction() as session:
ref = self._get_idp_from_remote_id(session, remote_id)
return ref.to_dict()
def update_idp(self, idp_id, idp):
with sql.transaction() as session:
idp_ref = self._get_idp(session, idp_id)
old_idp = idp_ref.to_dict()
old_idp.update(idp)
new_idp = IdentityProviderModel.from_dict(old_idp)
for attr in IdentityProviderModel.mutable_attributes:
setattr(idp_ref, attr, getattr(new_idp, attr))
return idp_ref.to_dict()
# Protocol CRUD
def _get_protocol(self, session, idp_id, protocol_id):
q = session.query(FederationProtocolModel)
q = q.filter_by(id=protocol_id, idp_id=idp_id)
try:
return q.one()
except sql.NotFound:
kwargs = {'protocol_id': protocol_id,
'idp_id': idp_id}
raise exception.FederatedProtocolNotFound(**kwargs)
@sql.handle_conflicts(conflict_type='federation_protocol')
def create_protocol(self, idp_id, protocol_id, protocol):
protocol['id'] = protocol_id
protocol['idp_id'] = idp_id
with sql.transaction() as session:
self._get_idp(session, idp_id)
protocol_ref = FederationProtocolModel.from_dict(protocol)
session.add(protocol_ref)
return protocol_ref.to_dict()
def update_protocol(self, idp_id, protocol_id, protocol):
with sql.transaction() as session:
proto_ref = self._get_protocol(session, idp_id, protocol_id)
old_proto = proto_ref.to_dict()
old_proto.update(protocol)
new_proto = FederationProtocolModel.from_dict(old_proto)
for attr in FederationProtocolModel.mutable_attributes:
setattr(proto_ref, attr, getattr(new_proto, attr))
return proto_ref.to_dict()
def get_protocol(self, idp_id, protocol_id):
with sql.transaction() as session:
protocol_ref = self._get_protocol(session, idp_id, protocol_id)
return protocol_ref.to_dict()
def list_protocols(self, idp_id):
with sql.transaction() as session:
q = session.query(FederationProtocolModel)
q = q.filter_by(idp_id=idp_id)
protocols = [protocol.to_dict() for protocol in q]
return protocols
def delete_protocol(self, idp_id, protocol_id):
with sql.transaction() as session:
key_ref = self._get_protocol(session, idp_id, protocol_id)
session.delete(key_ref)
def _delete_assigned_protocols(self, session, idp_id):
query = session.query(FederationProtocolModel)
query = query.filter_by(idp_id=idp_id)
query.delete()
# Mapping CRUD
def _get_mapping(self, session, mapping_id):
mapping_ref = session.query(MappingModel).get(mapping_id)
if not mapping_ref:
raise exception.MappingNotFound(mapping_id=mapping_id)
return mapping_ref
@sql.handle_conflicts(conflict_type='mapping')
def create_mapping(self, mapping_id, mapping):
ref = {}
ref['id'] = mapping_id
ref['rules'] = mapping.get('rules')
with sql.transaction() as session:
mapping_ref = MappingModel.from_dict(ref)
session.add(mapping_ref)
return mapping_ref.to_dict()
def delete_mapping(self, mapping_id):
with sql.transaction() as session:
mapping_ref = self._get_mapping(session, mapping_id)
session.delete(mapping_ref)
def list_mappings(self):
with sql.transaction() as session:
mappings = session.query(MappingModel)
return [x.to_dict() for x in mappings]
def get_mapping(self, mapping_id):
with sql.transaction() as session:
mapping_ref = self._get_mapping(session, mapping_id)
return mapping_ref.to_dict()
@sql.handle_conflicts(conflict_type='mapping')
def update_mapping(self, mapping_id, mapping):
ref = {}
ref['id'] = mapping_id
ref['rules'] = mapping.get('rules')
with sql.transaction() as session:
mapping_ref = self._get_mapping(session, mapping_id)
old_mapping = mapping_ref.to_dict()
old_mapping.update(ref)
new_mapping = MappingModel.from_dict(old_mapping)
for attr in MappingModel.attributes:
setattr(mapping_ref, attr, getattr(new_mapping, attr))
return mapping_ref.to_dict()
def get_mapping_from_idp_and_protocol(self, idp_id, protocol_id):
with sql.transaction() as session:
protocol_ref = self._get_protocol(session, idp_id, protocol_id)
mapping_id = protocol_ref.mapping_id
mapping_ref = self._get_mapping(session, mapping_id)
return mapping_ref.to_dict()
# Service Provider CRUD
@sql.handle_conflicts(conflict_type='service_provider')
def create_sp(self, sp_id, sp):
sp['id'] = sp_id
with sql.transaction() as session:
sp_ref = ServiceProviderModel.from_dict(sp)
session.add(sp_ref)
return sp_ref.to_dict()
def delete_sp(self, sp_id):
with sql.transaction() as session:
sp_ref = self._get_sp(session, sp_id)
session.delete(sp_ref)
def _get_sp(self, session, sp_id):
sp_ref = session.query(ServiceProviderModel).get(sp_id)
if not sp_ref:
raise exception.ServiceProviderNotFound(sp_id=sp_id)
return sp_ref
def list_sps(self):
with sql.transaction() as session:
sps = session.query(ServiceProviderModel)
sps_list = [sp.to_dict() for sp in sps]
return sps_list
def get_sp(self, sp_id):
with sql.transaction() as session:
sp_ref = self._get_sp(session, sp_id)
return sp_ref.to_dict()
def update_sp(self, sp_id, sp):
with sql.transaction() as session:
sp_ref = self._get_sp(session, sp_id)
old_sp = sp_ref.to_dict()
old_sp.update(sp)
new_sp = ServiceProviderModel.from_dict(old_sp)
for attr in ServiceProviderModel.mutable_attributes:
setattr(sp_ref, attr, getattr(new_sp, attr))
return sp_ref.to_dict()
def get_enabled_service_providers(self):
with sql.transaction() as session:
service_providers = session.query(ServiceProviderModel)
service_providers = service_providers.filter_by(enabled=True)
return service_providers

View File

@ -26,10 +26,10 @@ from keystone.common import controller
from keystone.common import dependency from keystone.common import dependency
from keystone.common import validation from keystone.common import validation
from keystone.common import wsgi from keystone.common import wsgi
from keystone.contrib.federation import idp as keystone_idp
from keystone.contrib.federation import schema
from keystone.contrib.federation import utils
from keystone import exception from keystone import exception
from keystone.federation import idp as keystone_idp
from keystone.federation import schema
from keystone.federation import utils
from keystone.i18n import _ from keystone.i18n import _
from keystone.models import token_model from keystone.models import token_model

View File

@ -21,8 +21,8 @@ import six
from keystone.common import dependency from keystone.common import dependency
from keystone.common import extension from keystone.common import extension
from keystone.common import manager from keystone.common import manager
from keystone.contrib.federation import utils
from keystone import exception from keystone import exception
from keystone.federation import utils
CONF = cfg.CONF CONF = cfg.CONF

View File

@ -0,0 +1,252 @@
# 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.federation import controllers
build_resource_relation = functools.partial(
json_home.build_v3_extension_resource_relation,
extension_name='OS-FEDERATION', extension_version='1.0')
build_parameter_relation = functools.partial(
json_home.build_v3_extension_parameter_relation,
extension_name='OS-FEDERATION', extension_version='1.0')
IDP_ID_PARAMETER_RELATION = build_parameter_relation(parameter_name='idp_id')
PROTOCOL_ID_PARAMETER_RELATION = build_parameter_relation(
parameter_name='protocol_id')
SP_ID_PARAMETER_RELATION = build_parameter_relation(parameter_name='sp_id')
class Routers(wsgi.RoutersBase):
"""API Endpoints for the Federation extension.
The API looks like::
PUT /OS-FEDERATION/identity_providers/{idp_id}
GET /OS-FEDERATION/identity_providers
GET /OS-FEDERATION/identity_providers/{idp_id}
DELETE /OS-FEDERATION/identity_providers/{idp_id}
PATCH /OS-FEDERATION/identity_providers/{idp_id}
PUT /OS-FEDERATION/identity_providers/
{idp_id}/protocols/{protocol_id}
GET /OS-FEDERATION/identity_providers/
{idp_id}/protocols
GET /OS-FEDERATION/identity_providers/
{idp_id}/protocols/{protocol_id}
PATCH /OS-FEDERATION/identity_providers/
{idp_id}/protocols/{protocol_id}
DELETE /OS-FEDERATION/identity_providers/
{idp_id}/protocols/{protocol_id}
PUT /OS-FEDERATION/mappings
GET /OS-FEDERATION/mappings
PATCH /OS-FEDERATION/mappings/{mapping_id}
GET /OS-FEDERATION/mappings/{mapping_id}
DELETE /OS-FEDERATION/mappings/{mapping_id}
GET /OS-FEDERATION/projects
GET /OS-FEDERATION/domains
PUT /OS-FEDERATION/service_providers/{sp_id}
GET /OS-FEDERATION/service_providers
GET /OS-FEDERATION/service_providers/{sp_id}
DELETE /OS-FEDERATION/service_providers/{sp_id}
PATCH /OS-FEDERATION/service_providers/{sp_id}
GET /OS-FEDERATION/identity_providers/{identity_provider}/
protocols/{protocol}/auth
POST /OS-FEDERATION/identity_providers/{identity_provider}/
protocols/{protocol}/auth
GET /auth/OS-FEDERATION/identity_providers/
{idp_id}/protocols/{protocol_id}/websso
?origin=https%3A//horizon.example.com
POST /auth/OS-FEDERATION/identity_providers/
{idp_id}/protocols/{protocol_id}/websso
?origin=https%3A//horizon.example.com
POST /auth/OS-FEDERATION/saml2
POST /auth/OS-FEDERATION/saml2/ecp
GET /OS-FEDERATION/saml2/metadata
GET /auth/OS-FEDERATION/websso/{protocol_id}
?origin=https%3A//horizon.example.com
POST /auth/OS-FEDERATION/websso/{protocol_id}
?origin=https%3A//horizon.example.com
"""
def _construct_url(self, suffix):
return "/OS-FEDERATION/%s" % suffix
def append_v3_routers(self, mapper, routers):
auth_controller = controllers.Auth()
idp_controller = controllers.IdentityProvider()
protocol_controller = controllers.FederationProtocol()
mapping_controller = controllers.MappingController()
project_controller = controllers.ProjectAssignmentV3()
domain_controller = controllers.DomainV3()
saml_metadata_controller = controllers.SAMLMetadataV3()
sp_controller = controllers.ServiceProvider()
# Identity Provider CRUD operations
self._add_resource(
mapper, idp_controller,
path=self._construct_url('identity_providers/{idp_id}'),
get_action='get_identity_provider',
put_action='create_identity_provider',
patch_action='update_identity_provider',
delete_action='delete_identity_provider',
rel=build_resource_relation(resource_name='identity_provider'),
path_vars={
'idp_id': IDP_ID_PARAMETER_RELATION,
})
self._add_resource(
mapper, idp_controller,
path=self._construct_url('identity_providers'),
get_action='list_identity_providers',
rel=build_resource_relation(resource_name='identity_providers'))
# Protocol CRUD operations
self._add_resource(
mapper, protocol_controller,
path=self._construct_url('identity_providers/{idp_id}/protocols/'
'{protocol_id}'),
get_action='get_protocol',
put_action='create_protocol',
patch_action='update_protocol',
delete_action='delete_protocol',
rel=build_resource_relation(
resource_name='identity_provider_protocol'),
path_vars={
'idp_id': IDP_ID_PARAMETER_RELATION,
'protocol_id': PROTOCOL_ID_PARAMETER_RELATION,
})
self._add_resource(
mapper, protocol_controller,
path=self._construct_url('identity_providers/{idp_id}/protocols'),
get_action='list_protocols',
rel=build_resource_relation(
resource_name='identity_provider_protocols'),
path_vars={
'idp_id': IDP_ID_PARAMETER_RELATION,
})
# Mapping CRUD operations
self._add_resource(
mapper, mapping_controller,
path=self._construct_url('mappings/{mapping_id}'),
get_action='get_mapping',
put_action='create_mapping',
patch_action='update_mapping',
delete_action='delete_mapping',
rel=build_resource_relation(resource_name='mapping'),
path_vars={
'mapping_id': build_parameter_relation(
parameter_name='mapping_id'),
})
self._add_resource(
mapper, mapping_controller,
path=self._construct_url('mappings'),
get_action='list_mappings',
rel=build_resource_relation(resource_name='mappings'))
# Service Providers CRUD operations
self._add_resource(
mapper, sp_controller,
path=self._construct_url('service_providers/{sp_id}'),
get_action='get_service_provider',
put_action='create_service_provider',
patch_action='update_service_provider',
delete_action='delete_service_provider',
rel=build_resource_relation(resource_name='service_provider'),
path_vars={
'sp_id': SP_ID_PARAMETER_RELATION,
})
self._add_resource(
mapper, sp_controller,
path=self._construct_url('service_providers'),
get_action='list_service_providers',
rel=build_resource_relation(resource_name='service_providers'))
self._add_resource(
mapper, domain_controller,
path=self._construct_url('domains'),
new_path='/auth/domains',
get_action='list_domains_for_groups',
rel=build_resource_relation(resource_name='domains'))
self._add_resource(
mapper, project_controller,
path=self._construct_url('projects'),
new_path='/auth/projects',
get_action='list_projects_for_groups',
rel=build_resource_relation(resource_name='projects'))
# Auth operations
self._add_resource(
mapper, auth_controller,
path=self._construct_url('identity_providers/{identity_provider}/'
'protocols/{protocol}/auth'),
get_post_action='federated_authentication',
rel=build_resource_relation(
resource_name='identity_provider_protocol_auth'),
path_vars={
'identity_provider': IDP_ID_PARAMETER_RELATION,
'protocol': PROTOCOL_ID_PARAMETER_RELATION,
})
self._add_resource(
mapper, auth_controller,
path='/auth' + self._construct_url('saml2'),
post_action='create_saml_assertion',
rel=build_resource_relation(resource_name='saml2'))
self._add_resource(
mapper, auth_controller,
path='/auth' + self._construct_url('saml2/ecp'),
post_action='create_ecp_assertion',
rel=build_resource_relation(resource_name='ecp'))
self._add_resource(
mapper, auth_controller,
path='/auth' + self._construct_url('websso/{protocol_id}'),
get_post_action='federated_sso_auth',
rel=build_resource_relation(resource_name='websso'),
path_vars={
'protocol_id': PROTOCOL_ID_PARAMETER_RELATION,
})
self._add_resource(
mapper, auth_controller,
path='/auth' + self._construct_url(
'identity_providers/{idp_id}/protocols/{protocol_id}/websso'),
get_post_action='federated_idp_specific_sso_auth',
rel=build_resource_relation(resource_name='identity_providers'),
path_vars={
'idp_id': IDP_ID_PARAMETER_RELATION,
'protocol_id': PROTOCOL_ID_PARAMETER_RELATION,
})
# Keystone-Identity-Provider metadata endpoint
self._add_resource(
mapper, saml_metadata_controller,
path=self._construct_url('saml2/metadata'),
get_action='get_metadata',
rel=build_resource_relation(resource_name='metadata'))

View File

@ -581,7 +581,7 @@ class RuleProcessor(object):
:param local: local mapping reference that needs to be updated :param local: local mapping reference that needs to be updated
:type local: dict :type local: dict
:param direct_maps: identity values used to update local :param direct_maps: identity values used to update local
:type direct_maps: keystone.contrib.federation.utils.DirectMaps :type direct_maps: keystone.federation.utils.DirectMaps
Example local:: Example local::
@ -659,7 +659,7 @@ class RuleProcessor(object):
} }
:returns: identity values used to update local :returns: identity values used to update local
:rtype: keystone.contrib.federation.utils.DirectMaps or None :rtype: keystone.federation.utils.DirectMaps or None
""" """
direct_maps = DirectMaps() direct_maps = DirectMaps()

View File

@ -17,9 +17,9 @@ from oslo_log import log
from keystone.common import authorization from keystone.common import authorization
from keystone.common import tokenless_auth from keystone.common import tokenless_auth
from keystone.common import wsgi from keystone.common import wsgi
from keystone.contrib.federation import constants as federation_constants
from keystone.contrib.federation import utils
from keystone import exception from keystone import exception
from keystone.federation import constants as federation_constants
from keystone.federation import utils
from keystone.i18n import _, _LI, _LW from keystone.i18n import _, _LI, _LW
from keystone.middleware import core from keystone.middleware import core
from keystone.models import token_model from keystone.models import token_model

View File

@ -17,11 +17,11 @@ from oslo_config import cfg
from oslo_utils import timeutils from oslo_utils import timeutils
import six import six
from keystone.contrib.federation import constants as federation_constants
from keystone import exception from keystone import exception
from keystone.i18n import _ from keystone.i18n import _
# FIXME(stevemar): Use constants from keystone.federation.constants
OS_FEDERATION = 'OS-FEDERATION'
CONF = cfg.CONF CONF = cfg.CONF
# supported token versions # supported token versions
V2 = 'v2.0' V2 = 'v2.0'
@ -297,8 +297,7 @@ class KeystoneToken(dict):
@property @property
def is_federated_user(self): def is_federated_user(self):
try: try:
return (self.version is V3 and return (self.version is V3 and OS_FEDERATION in self['user'])
federation_constants.FEDERATION in self['user'])
except KeyError: except KeyError:
raise exception.UnexpectedError() raise exception.UnexpectedError()
@ -307,8 +306,7 @@ class KeystoneToken(dict):
if self.is_federated_user: if self.is_federated_user:
if self.version is V3: if self.version is V3:
try: try:
groups = self['user'][federation_constants.FEDERATION].get( groups = self['user'][OS_FEDERATION].get('groups', [])
'groups', [])
return [g['id'] for g in groups] return [g['id'] for g in groups]
except KeyError: except KeyError:
raise exception.UnexpectedError() raise exception.UnexpectedError()
@ -318,15 +316,12 @@ class KeystoneToken(dict):
def federation_idp_id(self): def federation_idp_id(self):
if self.version is not V3 or not self.is_federated_user: if self.version is not V3 or not self.is_federated_user:
return None return None
return ( return self['user'][OS_FEDERATION]['identity_provider']['id']
self['user'][federation_constants.FEDERATION]
['identity_provider']['id'])
@property @property
def federation_protocol_id(self): def federation_protocol_id(self):
if self.version is V3 and self.is_federated_user: if self.version is V3 and self.is_federated_user:
return (self['user'][federation_constants.FEDERATION]['protocol'] return self['user'][OS_FEDERATION]['protocol']['id']
['id'])
return None return None
@property @property

View File

@ -15,11 +15,11 @@ 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 federation
from keystone.contrib import oauth1 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 identity from keystone import identity
from keystone import policy from keystone import policy
from keystone import resource from keystone import resource

View File

@ -17,8 +17,8 @@ import copy
import uuid import uuid
from keystone.common import authorization from keystone.common import authorization
from keystone.contrib.federation import constants as federation_constants
from keystone import exception from keystone import exception
from keystone.federation import constants as federation_constants
from keystone.models import token_model from keystone.models import token_model
from keystone.tests import unit from keystone.tests import unit
from keystone.tests.unit import test_token_provider from keystone.tests.unit import test_token_provider

View File

@ -13,8 +13,8 @@
import uuid import uuid
from keystone.auth.plugins import mapped from keystone.auth.plugins import mapped
from keystone.contrib.federation import utils as mapping_utils
from keystone import exception from keystone import exception
from keystone.federation import utils as mapping_utils
from keystone.tests import unit from keystone.tests import unit
from keystone.tests.unit import mapping_fixtures from keystone.tests.unit import mapping_fixtures

View File

@ -22,8 +22,8 @@ import webob
from keystone.common import authorization from keystone.common import authorization
from keystone.common import tokenless_auth from keystone.common import tokenless_auth
from keystone.contrib.federation import constants as federation_constants
from keystone import exception from keystone import exception
from keystone.federation import constants as federation_constants
from keystone import middleware from keystone import middleware
from keystone.tests import unit from keystone.tests import unit
from keystone.tests.unit import mapping_fixtures from keystone.tests.unit import mapping_fixtures

View File

@ -20,6 +20,7 @@ from lxml import etree
import mock import mock
from oslo_config import cfg from oslo_config import cfg
from oslo_log import log from oslo_log import log
from oslo_log import versionutils
from oslo_utils import importutils from oslo_utils import importutils
from oslotest import mockpatch from oslotest import mockpatch
import saml2 import saml2
@ -33,9 +34,10 @@ if not xmldsig:
from keystone.auth import controllers as auth_controllers from keystone.auth import controllers as auth_controllers
from keystone.common import environment from keystone.common import environment
from keystone.contrib.federation import controllers as federation_controllers from keystone.contrib.federation import routers
from keystone.contrib.federation import idp as keystone_idp
from keystone import exception from keystone import exception
from keystone.federation import controllers as federation_controllers
from keystone.federation import idp as keystone_idp
from keystone import notifications from keystone import notifications
from keystone.tests import unit from keystone.tests import unit
from keystone.tests.unit import core from keystone.tests.unit import core
@ -60,8 +62,12 @@ def dummy_validator(*args, **kwargs):
class FederationTests(test_v3.RestfulTestCase): class FederationTests(test_v3.RestfulTestCase):
EXTENSION_NAME = 'federation' @mock.patch.object(versionutils, 'report_deprecated_feature')
EXTENSION_TO_ADD = 'federation_extension' def test_exception_happens(self, mock_deprecator):
routers.FederationExtension(mock.ANY)
mock_deprecator.assert_called_once_with(mock.ANY, mock.ANY)
args, _kwargs = mock_deprecator.call_args
self.assertIn("Remove federation_extension from", args[1])
class FederatedSetupMixin(object): class FederatedSetupMixin(object):
@ -770,7 +776,7 @@ class FederatedSetupMixin(object):
self.domainC['id']) self.domainC['id'])
class FederatedIdentityProviderTests(FederationTests): class FederatedIdentityProviderTests(test_v3.RestfulTestCase):
"""A test class for Identity Providers.""" """A test class for Identity Providers."""
idp_keys = ['description', 'enabled'] idp_keys = ['description', 'enabled']
@ -1298,7 +1304,7 @@ class FederatedIdentityProviderTests(FederationTests):
self.get(url, expected_status=http_client.NOT_FOUND) self.get(url, expected_status=http_client.NOT_FOUND)
class MappingCRUDTests(FederationTests): class MappingCRUDTests(test_v3.RestfulTestCase):
"""A class for testing CRUD operations for Mappings.""" """A class for testing CRUD operations for Mappings."""
MAPPING_URL = '/OS-FEDERATION/mappings/' MAPPING_URL = '/OS-FEDERATION/mappings/'
@ -1465,7 +1471,7 @@ class MappingCRUDTests(FederationTests):
body={'mapping': mapping}) body={'mapping': mapping})
class FederatedTokenTests(FederationTests, FederatedSetupMixin): class FederatedTokenTests(test_v3.RestfulTestCase, FederatedSetupMixin):
def auth_plugin_config_override(self): def auth_plugin_config_override(self):
methods = ['saml2'] methods = ['saml2']
@ -1502,7 +1508,7 @@ class FederatedTokenTests(FederationTests, FederatedSetupMixin):
self.assertTrue(note['send_notification_called']) self.assertTrue(note['send_notification_called'])
def load_fixtures(self, fixtures): def load_fixtures(self, fixtures):
super(FederationTests, self).load_fixtures(fixtures) super(FederatedTokenTests, self).load_fixtures(fixtures)
self.load_federation_sample_data() self.load_federation_sample_data()
def test_issue_unscoped_token_notify(self): def test_issue_unscoped_token_notify(self):
@ -2367,7 +2373,7 @@ class FederatedTokenTests(FederationTests, FederatedSetupMixin):
assertion='ANOTHER_LOCAL_USER_ASSERTION') assertion='ANOTHER_LOCAL_USER_ASSERTION')
class FernetFederatedTokenTests(FederationTests, FederatedSetupMixin): class FernetFederatedTokenTests(test_v3.RestfulTestCase, FederatedSetupMixin):
AUTH_METHOD = 'token' AUTH_METHOD = 'token'
def load_fixtures(self, fixtures): def load_fixtures(self, fixtures):
@ -2441,7 +2447,7 @@ class FederatedTokenTestsMethodToken(FederatedTokenTests):
self).auth_plugin_config_override(methods) self).auth_plugin_config_override(methods)
class JsonHomeTests(FederationTests, test_v3.JsonHomeTestMixin): class JsonHomeTests(test_v3.RestfulTestCase, test_v3.JsonHomeTestMixin):
JSON_HOME_DATA = { JSON_HOME_DATA = {
'http://docs.openstack.org/api/openstack-identity/3/ext/OS-FEDERATION/' 'http://docs.openstack.org/api/openstack-identity/3/ext/OS-FEDERATION/'
'1.0/rel/identity_provider': { '1.0/rel/identity_provider': {
@ -2469,7 +2475,7 @@ def _load_xml(filename):
return xml.read() return xml.read()
class SAMLGenerationTests(FederationTests): class SAMLGenerationTests(test_v3.RestfulTestCase):
SP_AUTH_URL = ('http://beta.com:5000/v3/OS-FEDERATION/identity_providers' SP_AUTH_URL = ('http://beta.com:5000/v3/OS-FEDERATION/identity_providers'
'/BETA/protocols/saml2/auth') '/BETA/protocols/saml2/auth')
@ -2936,7 +2942,7 @@ class SAMLGenerationTests(FederationTests):
self.assertEqual(expected_log, logger_fixture.output) self.assertEqual(expected_log, logger_fixture.output)
class IdPMetadataGenerationTests(FederationTests): class IdPMetadataGenerationTests(test_v3.RestfulTestCase):
"""A class for testing Identity Provider Metadata generation.""" """A class for testing Identity Provider Metadata generation."""
METADATA_URL = '/OS-FEDERATION/saml2/metadata' METADATA_URL = '/OS-FEDERATION/saml2/metadata'
@ -3066,7 +3072,7 @@ class IdPMetadataGenerationTests(FederationTests):
self.assertEqual(reference_file, r.result) self.assertEqual(reference_file, r.result)
class ServiceProviderTests(FederationTests): class ServiceProviderTests(test_v3.RestfulTestCase):
"""A test class for Service Providers.""" """A test class for Service Providers."""
MEMBER_NAME = 'service_provider' MEMBER_NAME = 'service_provider'
@ -3076,7 +3082,7 @@ class ServiceProviderTests(FederationTests):
'relay_state_prefix', 'sp_url'] 'relay_state_prefix', 'sp_url']
def setUp(self): def setUp(self):
super(FederationTests, self).setUp() super(ServiceProviderTests, self).setUp()
# Add a Service Provider # Add a Service Provider
url = self.base_url(suffix=self.SERVICE_PROVIDER_ID) url = self.base_url(suffix=self.SERVICE_PROVIDER_ID)
self.SP_REF = self.sp_ref() self.SP_REF = self.sp_ref()
@ -3359,7 +3365,7 @@ class WebSSOTests(FederatedTokenTests):
self.assertIn(self.TRUSTED_DASHBOARD, resp.body) self.assertIn(self.TRUSTED_DASHBOARD, resp.body)
class K2KServiceCatalogTests(FederationTests): class K2KServiceCatalogTests(test_v3.RestfulTestCase):
SP1 = 'SP1' SP1 = 'SP1'
SP2 = 'SP2' SP2 = 'SP2'
SP3 = 'SP3' SP3 = 'SP3'

View File

@ -22,9 +22,9 @@ from keystone.common import validation
from keystone.common.validation import parameter_types from keystone.common.validation import parameter_types
from keystone.common.validation import validators from keystone.common.validation import validators
from keystone.contrib.endpoint_filter import schema as endpoint_filter_schema from keystone.contrib.endpoint_filter import schema as endpoint_filter_schema
from keystone.contrib.federation import schema as federation_schema
from keystone.credential import schema as credential_schema from keystone.credential import schema as credential_schema
from keystone import exception from keystone import exception
from keystone.federation import schema as federation_schema
from keystone.identity import schema as identity_schema from keystone.identity import schema as identity_schema
from keystone.policy import schema as policy_schema from keystone.policy import schema as policy_schema
from keystone.resource import schema as resource_schema from keystone.resource import schema as resource_schema

View File

@ -22,8 +22,8 @@ from six.moves import urllib
from keystone.common import config from keystone.common import config
from keystone.common import utils from keystone.common import utils
from keystone.contrib.federation import constants as federation_constants
from keystone import exception from keystone import exception
from keystone.federation import constants as federation_constants
from keystone.tests import unit from keystone.tests import unit
from keystone.tests.unit import ksfixtures from keystone.tests.unit import ksfixtures
from keystone.tests.unit.ksfixtures import database from keystone.tests.unit.ksfixtures import database

View File

@ -17,8 +17,8 @@ from oslo_config import cfg
from oslo_utils import timeutils from oslo_utils import timeutils
from six.moves import range from six.moves import range
from keystone.contrib.federation import constants as federation_constants
from keystone import exception from keystone import exception
from keystone.federation import constants as federation_constants
from keystone.models import token_model from keystone.models import token_model
from keystone.tests.unit import core from keystone.tests.unit import core
from keystone.tests.unit import test_token_provider from keystone.tests.unit import test_token_provider

View File

@ -22,8 +22,8 @@ from six.moves.urllib import parse
from keystone.common import controller as common_controller from keystone.common import controller as common_controller
from keystone.common import dependency from keystone.common import dependency
from keystone.common import utils from keystone.common import utils
from keystone.contrib.federation import constants as federation_constants
from keystone import exception from keystone import exception
from keystone.federation import constants as federation_constants
from keystone.i18n import _, _LE from keystone.i18n import _, _LE
from keystone import token from keystone import token
from keystone.token import provider from keystone.token import provider

View File

@ -15,8 +15,8 @@ from oslo_log import log
from keystone.common import dependency from keystone.common import dependency
from keystone.common import utils as ks_utils from keystone.common import utils as ks_utils
from keystone.contrib.federation import constants as federation_constants
from keystone import exception from keystone import exception
from keystone.federation import constants as federation_constants
from keystone.i18n import _ from keystone.i18n import _
from keystone.token import provider from keystone.token import provider
from keystone.token.providers import common from keystone.token.providers import common

View File

@ -26,6 +26,7 @@ from keystone import catalog
from keystone.common import wsgi from keystone.common import wsgi
from keystone import credential from keystone import credential
from keystone import endpoint_policy from keystone import endpoint_policy
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 policy from keystone import policy
@ -129,7 +130,8 @@ def v3_app_factory(global_conf, **local_conf):
credential, credential,
identity, identity,
policy, policy,
resource] resource,
federation]
if CONF.trust.enabled: if CONF.trust.enabled:
router_modules.append(trust) router_modules.append(trust)

View File

@ -162,7 +162,7 @@ keystone.endpoint_policy =
sql = keystone.endpoint_policy.backends.sql:EndpointPolicy sql = keystone.endpoint_policy.backends.sql:EndpointPolicy
keystone.federation = keystone.federation =
sql = keystone.contrib.federation.backends.sql:Federation sql = keystone.federation.backends.sql:Federation
keystone.oauth1 = keystone.oauth1 =
sql = keystone.contrib.oauth1.backends.sql:OAuth1 sql = keystone.contrib.oauth1.backends.sql:OAuth1