aboutsummaryrefslogtreecommitdiffstats
path: root/keystone-moon/keystone/contrib
diff options
context:
space:
mode:
Diffstat (limited to 'keystone-moon/keystone/contrib')
-rw-r--r--keystone-moon/keystone/contrib/ec2/controllers.py72
-rw-r--r--keystone-moon/keystone/contrib/endpoint_filter/backends/catalog_sql.py6
-rw-r--r--keystone-moon/keystone/contrib/endpoint_filter/backends/sql.py5
-rw-r--r--keystone-moon/keystone/contrib/endpoint_filter/controllers.py2
-rw-r--r--keystone-moon/keystone/contrib/endpoint_filter/core.py4
-rw-r--r--keystone-moon/keystone/contrib/endpoint_filter/migrate_repo/versions/001_add_endpoint_filtering_table.py9
-rw-r--r--keystone-moon/keystone/contrib/endpoint_filter/migrate_repo/versions/002_add_endpoint_groups.py10
-rw-r--r--keystone-moon/keystone/contrib/endpoint_filter/routers.py51
-rw-r--r--keystone-moon/keystone/contrib/endpoint_policy/__init__.py15
-rw-r--r--keystone-moon/keystone/contrib/endpoint_policy/backends/sql.py134
-rw-r--r--keystone-moon/keystone/contrib/endpoint_policy/migrate_repo/versions/001_add_endpoint_policy_table.py8
-rw-r--r--keystone-moon/keystone/contrib/endpoint_policy/routers.py79
-rw-r--r--keystone-moon/keystone/contrib/example/core.py11
-rw-r--r--keystone-moon/keystone/contrib/example/migrate_repo/versions/001_example_table.py11
-rw-r--r--keystone-moon/keystone/contrib/federation/backends/sql.py71
-rw-r--r--keystone-moon/keystone/contrib/federation/constants.py15
-rw-r--r--keystone-moon/keystone/contrib/federation/controllers.py142
-rw-r--r--keystone-moon/keystone/contrib/federation/core.py18
-rw-r--r--keystone-moon/keystone/contrib/federation/idp.py128
-rw-r--r--keystone-moon/keystone/contrib/federation/migrate_repo/versions/001_add_identity_provider_table.py9
-rw-r--r--keystone-moon/keystone/contrib/federation/migrate_repo/versions/002_add_mapping_tables.py10
-rw-r--r--keystone-moon/keystone/contrib/federation/migrate_repo/versions/003_mapping_id_nullable_false.py6
-rw-r--r--keystone-moon/keystone/contrib/federation/migrate_repo/versions/004_add_remote_id_column.py7
-rw-r--r--keystone-moon/keystone/contrib/federation/migrate_repo/versions/005_add_service_provider_table.py7
-rw-r--r--keystone-moon/keystone/contrib/federation/migrate_repo/versions/006_fixup_service_provider_attributes.py8
-rw-r--r--keystone-moon/keystone/contrib/federation/migrate_repo/versions/007_add_remote_id_table.py41
-rw-r--r--keystone-moon/keystone/contrib/federation/migrate_repo/versions/008_add_relay_state_to_sp.py39
-rw-r--r--keystone-moon/keystone/contrib/federation/routers.py50
-rw-r--r--keystone-moon/keystone/contrib/federation/schema.py3
-rw-r--r--keystone-moon/keystone/contrib/federation/utils.py66
-rw-r--r--keystone-moon/keystone/contrib/moon/algorithms.py34
-rw-r--r--keystone-moon/keystone/contrib/moon/backends/memory.py17
-rw-r--r--keystone-moon/keystone/contrib/moon/backends/sql.py23
-rw-r--r--keystone-moon/keystone/contrib/moon/core.py609
-rw-r--r--keystone-moon/keystone/contrib/moon/extension.py740
-rw-r--r--keystone-moon/keystone/contrib/moon/migrate_repo/versions/001_moon.py12
-rw-r--r--keystone-moon/keystone/contrib/moon/routers.py2
-rw-r--r--keystone-moon/keystone/contrib/oauth1/backends/sql.py10
-rw-r--r--keystone-moon/keystone/contrib/oauth1/controllers.py12
-rw-r--r--keystone-moon/keystone/contrib/oauth1/core.py5
-rw-r--r--keystone-moon/keystone/contrib/oauth1/migrate_repo/versions/001_add_oauth_tables.py10
-rw-r--r--keystone-moon/keystone/contrib/oauth1/migrate_repo/versions/002_fix_oauth_tables_fk.py17
-rw-r--r--keystone-moon/keystone/contrib/oauth1/migrate_repo/versions/003_consumer_description_nullalbe.py7
-rw-r--r--keystone-moon/keystone/contrib/oauth1/migrate_repo/versions/004_request_token_roles_nullable.py10
-rw-r--r--keystone-moon/keystone/contrib/oauth1/migrate_repo/versions/005_consumer_id_index.py11
-rw-r--r--keystone-moon/keystone/contrib/oauth1/routers.py12
-rw-r--r--keystone-moon/keystone/contrib/revoke/backends/kvs.py33
-rw-r--r--keystone-moon/keystone/contrib/revoke/backends/sql.py4
-rw-r--r--keystone-moon/keystone/contrib/revoke/core.py25
-rw-r--r--keystone-moon/keystone/contrib/revoke/migrate_repo/versions/001_revoke_table.py11
-rw-r--r--keystone-moon/keystone/contrib/revoke/migrate_repo/versions/002_add_audit_id_and_chain_to_revoke_table.py9
-rw-r--r--keystone-moon/keystone/contrib/revoke/model.py120
-rw-r--r--keystone-moon/keystone/contrib/s3/core.py15
53 files changed, 1091 insertions, 1694 deletions
diff --git a/keystone-moon/keystone/contrib/ec2/controllers.py b/keystone-moon/keystone/contrib/ec2/controllers.py
index 6e6d3268..78172ec9 100644
--- a/keystone-moon/keystone/contrib/ec2/controllers.py
+++ b/keystone-moon/keystone/contrib/ec2/controllers.py
@@ -46,7 +46,6 @@ from keystone.common import utils
from keystone.common import wsgi
from keystone import exception
from keystone.i18n import _
-from keystone.models import token_model
@dependency.requires('assignment_api', 'catalog_api', 'credential_api',
@@ -57,16 +56,30 @@ class Ec2ControllerCommon(object):
def check_signature(self, creds_ref, credentials):
signer = ec2_utils.Ec2Signer(creds_ref['secret'])
signature = signer.generate(credentials)
- if utils.auth_str_equal(credentials['signature'], signature):
- return
- # NOTE(vish): Some libraries don't use the port when signing
- # requests, so try again without port.
- elif ':' in credentials['signature']:
- hostname, _port = credentials['host'].split(':')
- credentials['host'] = hostname
- signature = signer.generate(credentials)
- if not utils.auth_str_equal(credentials.signature, signature):
- raise exception.Unauthorized(message='Invalid EC2 signature.')
+ # NOTE(davechen): credentials.get('signature') is not guaranteed to
+ # exist, we need check it explicitly.
+ if credentials.get('signature'):
+ if utils.auth_str_equal(credentials['signature'], signature):
+ return True
+ # NOTE(vish): Some client libraries don't use the port when signing
+ # requests, so try again without port.
+ elif ':' in credentials['host']:
+ hostname, _port = credentials['host'].split(':')
+ credentials['host'] = hostname
+ # NOTE(davechen): we need reinitialize 'signer' to avoid
+ # contaminated status of signature, this is similar with
+ # other programming language libraries, JAVA for example.
+ signer = ec2_utils.Ec2Signer(creds_ref['secret'])
+ signature = signer.generate(credentials)
+ if utils.auth_str_equal(credentials['signature'],
+ signature):
+ return True
+ raise exception.Unauthorized(
+ message='Invalid EC2 signature.')
+ else:
+ raise exception.Unauthorized(
+ message='EC2 signature not supplied.')
+ # Raise the exception when credentials.get('signature') is None
else:
raise exception.Unauthorized(message='EC2 signature not supplied.')
@@ -305,14 +318,7 @@ class Ec2Controller(Ec2ControllerCommon, controller.V2Controller):
:raises exception.Forbidden: when token is invalid
"""
- try:
- token_data = self.token_provider_api.validate_token(
- context['token_id'])
- except exception.TokenNotFound as e:
- raise exception.Unauthorized(e)
-
- token_ref = token_model.KeystoneToken(token_id=context['token_id'],
- token_data=token_data)
+ token_ref = utils.get_token_ref(context)
if token_ref.user_id != user_id:
raise exception.Forbidden(_('Token belongs to another user'))
@@ -329,7 +335,7 @@ class Ec2Controller(Ec2ControllerCommon, controller.V2Controller):
# to properly perform policy enforcement.
self.assert_admin(context)
return True
- except exception.Forbidden:
+ except (exception.Forbidden, exception.Unauthorized):
return False
def _assert_owner(self, user_id, credential_id):
@@ -349,11 +355,11 @@ class Ec2Controller(Ec2ControllerCommon, controller.V2Controller):
@dependency.requires('policy_api', 'token_provider_api')
class Ec2ControllerV3(Ec2ControllerCommon, controller.V3Controller):
- member_name = 'project'
+ collection_name = 'credentials'
+ member_name = 'credential'
def __init__(self):
super(Ec2ControllerV3, self).__init__()
- self.get_member_from_driver = self.credential_api.get_credential
def _check_credential_owner_and_user_id_match(self, context, prep_info,
user_id, credential_id):
@@ -385,23 +391,35 @@ class Ec2ControllerV3(Ec2ControllerCommon, controller.V3Controller):
@controller.protected(callback=_check_credential_owner_and_user_id_match)
def ec2_get_credential(self, context, user_id, credential_id):
- return super(Ec2ControllerV3, self).get_credential(user_id,
- credential_id)
+ ref = super(Ec2ControllerV3, self).get_credential(user_id,
+ credential_id)
+ return Ec2ControllerV3.wrap_member(context, ref['credential'])
@controller.protected()
def ec2_list_credentials(self, context, user_id):
- return super(Ec2ControllerV3, self).get_credentials(user_id)
+ refs = super(Ec2ControllerV3, self).get_credentials(user_id)
+ return Ec2ControllerV3.wrap_collection(context, refs['credentials'])
@controller.protected()
def ec2_create_credential(self, context, user_id, tenant_id):
- return super(Ec2ControllerV3, self).create_credential(context, user_id,
- tenant_id)
+ ref = super(Ec2ControllerV3, self).create_credential(context, user_id,
+ tenant_id)
+ return Ec2ControllerV3.wrap_member(context, ref['credential'])
@controller.protected(callback=_check_credential_owner_and_user_id_match)
def ec2_delete_credential(self, context, user_id, credential_id):
return super(Ec2ControllerV3, self).delete_credential(user_id,
credential_id)
+ @classmethod
+ def _add_self_referential_link(cls, context, ref):
+ path = '/users/%(user_id)s/credentials/OS-EC2/%(credential_id)s'
+ url = cls.base_url(context, path) % {
+ 'user_id': ref['user_id'],
+ 'credential_id': ref['access']}
+ ref.setdefault('links', {})
+ ref['links']['self'] = url
+
def render_token_data_response(token_id, token_data):
"""Render token data HTTP response.
diff --git a/keystone-moon/keystone/contrib/endpoint_filter/backends/catalog_sql.py b/keystone-moon/keystone/contrib/endpoint_filter/backends/catalog_sql.py
index 6ac3c1ca..22d5796a 100644
--- a/keystone-moon/keystone/contrib/endpoint_filter/backends/catalog_sql.py
+++ b/keystone-moon/keystone/contrib/endpoint_filter/backends/catalog_sql.py
@@ -13,20 +13,20 @@
# under the License.
from oslo_config import cfg
-import six
from keystone.catalog.backends import sql
from keystone.catalog import core as catalog_core
from keystone.common import dependency
from keystone import exception
+
CONF = cfg.CONF
@dependency.requires('endpoint_filter_api')
class EndpointFilterCatalog(sql.Catalog):
def get_v3_catalog(self, user_id, project_id):
- substitutions = dict(six.iteritems(CONF))
+ substitutions = dict(CONF.items())
substitutions.update({'tenant_id': project_id, 'user_id': user_id})
services = {}
@@ -66,7 +66,7 @@ class EndpointFilterCatalog(sql.Catalog):
# format catalog
catalog = []
- for service_id, service in six.iteritems(services):
+ for service_id, service in services.items():
formatted_service = {}
formatted_service['id'] = service['id']
formatted_service['type'] = service['type']
diff --git a/keystone-moon/keystone/contrib/endpoint_filter/backends/sql.py b/keystone-moon/keystone/contrib/endpoint_filter/backends/sql.py
index a998423f..53d511e5 100644
--- a/keystone-moon/keystone/contrib/endpoint_filter/backends/sql.py
+++ b/keystone-moon/keystone/contrib/endpoint_filter/backends/sql.py
@@ -13,6 +13,7 @@
# under the License.
from keystone.common import sql
+from keystone.contrib import endpoint_filter
from keystone import exception
from keystone.i18n import _
@@ -52,7 +53,7 @@ class ProjectEndpointGroupMembership(sql.ModelBase, sql.ModelDictMixin):
'project_id'), {})
-class EndpointFilter(object):
+class EndpointFilter(endpoint_filter.Driver):
@sql.handle_conflicts(conflict_type='project_endpoint')
def add_endpoint_to_project(self, endpoint_id, project_id):
@@ -150,9 +151,9 @@ class EndpointFilter(object):
endpoint_group_ref = self._get_endpoint_group(session,
endpoint_group_id)
with session.begin():
- session.delete(endpoint_group_ref)
self._delete_endpoint_group_association_by_endpoint_group(
session, endpoint_group_id)
+ session.delete(endpoint_group_ref)
def get_endpoint_group_in_project(self, endpoint_group_id, project_id):
session = sql.get_session()
diff --git a/keystone-moon/keystone/contrib/endpoint_filter/controllers.py b/keystone-moon/keystone/contrib/endpoint_filter/controllers.py
index dc4ef7a3..eb627c6b 100644
--- a/keystone-moon/keystone/contrib/endpoint_filter/controllers.py
+++ b/keystone-moon/keystone/contrib/endpoint_filter/controllers.py
@@ -49,7 +49,7 @@ class _ControllerBase(controller.V3Controller):
for endpoint in endpoints:
is_candidate = True
- for key, value in six.iteritems(filters):
+ for key, value in filters.items():
if endpoint[key] != value:
is_candidate = False
break
diff --git a/keystone-moon/keystone/contrib/endpoint_filter/core.py b/keystone-moon/keystone/contrib/endpoint_filter/core.py
index 972b65dd..1cb35b1f 100644
--- a/keystone-moon/keystone/contrib/endpoint_filter/core.py
+++ b/keystone-moon/keystone/contrib/endpoint_filter/core.py
@@ -12,6 +12,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+"""Main entry point into the Endpoint Filter service."""
+
import abc
from oslo_config import cfg
@@ -56,6 +58,8 @@ class Manager(manager.Manager):
"""
+ driver_namespace = 'keystone.endpoint_filter'
+
def __init__(self):
super(Manager, self).__init__(CONF.endpoint_filter.driver)
diff --git a/keystone-moon/keystone/contrib/endpoint_filter/migrate_repo/versions/001_add_endpoint_filtering_table.py b/keystone-moon/keystone/contrib/endpoint_filter/migrate_repo/versions/001_add_endpoint_filtering_table.py
index 090e7f47..2aa93a86 100644
--- a/keystone-moon/keystone/contrib/endpoint_filter/migrate_repo/versions/001_add_endpoint_filtering_table.py
+++ b/keystone-moon/keystone/contrib/endpoint_filter/migrate_repo/versions/001_add_endpoint_filtering_table.py
@@ -36,12 +36,3 @@ def upgrade(migrate_engine):
nullable=False))
endpoint_filtering_table.create(migrate_engine, checkfirst=True)
-
-
-def downgrade(migrate_engine):
- meta = sql.MetaData()
- meta.bind = migrate_engine
- # Operations to reverse the above upgrade go here.
- for table_name in ['project_endpoint']:
- table = sql.Table(table_name, meta, autoload=True)
- table.drop()
diff --git a/keystone-moon/keystone/contrib/endpoint_filter/migrate_repo/versions/002_add_endpoint_groups.py b/keystone-moon/keystone/contrib/endpoint_filter/migrate_repo/versions/002_add_endpoint_groups.py
index 5f80160a..2c218b0d 100644
--- a/keystone-moon/keystone/contrib/endpoint_filter/migrate_repo/versions/002_add_endpoint_groups.py
+++ b/keystone-moon/keystone/contrib/endpoint_filter/migrate_repo/versions/002_add_endpoint_groups.py
@@ -39,13 +39,3 @@ def upgrade(migrate_engine):
sql.PrimaryKeyConstraint('endpoint_group_id',
'project_id'))
project_endpoint_group_table.create(migrate_engine, checkfirst=True)
-
-
-def downgrade(migrate_engine):
- meta = sql.MetaData()
- meta.bind = migrate_engine
- # Operations to reverse the above upgrade go here.
- for table_name in ['project_endpoint_group',
- 'endpoint_group']:
- table = sql.Table(table_name, meta, autoload=True)
- table.drop()
diff --git a/keystone-moon/keystone/contrib/endpoint_filter/routers.py b/keystone-moon/keystone/contrib/endpoint_filter/routers.py
index 00c8cd72..285b9df2 100644
--- a/keystone-moon/keystone/contrib/endpoint_filter/routers.py
+++ b/keystone-moon/keystone/contrib/endpoint_filter/routers.py
@@ -36,28 +36,32 @@ class EndpointFilterExtension(wsgi.V3ExtensionRouter):
The API looks like::
- PUT /OS-EP-FILTER/projects/$project_id/endpoints/$endpoint_id
- GET /OS-EP-FILTER/projects/$project_id/endpoints/$endpoint_id
- HEAD /OS-EP-FILTER/projects/$project_id/endpoints/$endpoint_id
- DELETE /OS-EP-FILTER/projects/$project_id/endpoints/$endpoint_id
- GET /OS-EP-FILTER/endpoints/$endpoint_id/projects
- GET /OS-EP-FILTER/projects/$project_id/endpoints
+ PUT /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id}
+ GET /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id}
+ HEAD /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id}
+ DELETE /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id}
+ GET /OS-EP-FILTER/endpoints/{endpoint_id}/projects
+ GET /OS-EP-FILTER/projects/{project_id}/endpoints
+ GET /OS-EP-FILTER/projects/{project_id}/endpoint_groups
GET /OS-EP-FILTER/endpoint_groups
POST /OS-EP-FILTER/endpoint_groups
- GET /OS-EP-FILTER/endpoint_groups/$endpoint_group_id
- HEAD /OS-EP-FILTER/endpoint_groups/$endpoint_group_id
- PATCH /OS-EP-FILTER/endpoint_groups/$endpoint_group_id
- DELETE /OS-EP-FILTER/endpoint_groups/$endpoint_group_id
-
- GET /OS-EP-FILTER/endpoint_groups/$endpoint_group_id/projects
- GET /OS-EP-FILTER/endpoint_groups/$endpoint_group_id/endpoints
-
- PUT /OS-EP-FILTER/endpoint_groups/$endpoint_group/projects/$project_id
- GET /OS-EP-FILTER/endpoint_groups/$endpoint_group/projects/$project_id
- HEAD /OS-EP-FILTER/endpoint_groups/$endpoint_group/projects/$project_id
- DELETE /OS-EP-FILTER/endpoint_groups/$endpoint_group/projects/
- $project_id
+ GET /OS-EP-FILTER/endpoint_groups/{endpoint_group_id}
+ HEAD /OS-EP-FILTER/endpoint_groups/{endpoint_group_id}
+ PATCH /OS-EP-FILTER/endpoint_groups/{endpoint_group_id}
+ DELETE /OS-EP-FILTER/endpoint_groups/{endpoint_group_id}
+
+ GET /OS-EP-FILTER/endpoint_groups/{endpoint_group_id}/projects
+ GET /OS-EP-FILTER/endpoint_groups/{endpoint_group_id}/endpoints
+
+ PUT /OS-EP-FILTER/endpoint_groups/{endpoint_group}/projects/
+ {project_id}
+ GET /OS-EP-FILTER/endpoint_groups/{endpoint_group}/projects/
+ {project_id}
+ HEAD /OS-EP-FILTER/endpoint_groups/{endpoint_group}/projects/
+ {project_id}
+ DELETE /OS-EP-FILTER/endpoint_groups/{endpoint_group}/projects/
+ {project_id}
"""
PATH_PREFIX = '/OS-EP-FILTER'
@@ -101,6 +105,15 @@ class EndpointFilterExtension(wsgi.V3ExtensionRouter):
})
self._add_resource(
mapper, endpoint_group_controller,
+ path=self.PATH_PREFIX + '/projects/{project_id}/endpoint_groups',
+ get_action='list_endpoint_groups_for_project',
+ rel=build_resource_relation(
+ resource_name='project_endpoint_groups'),
+ path_vars={
+ 'project_id': json_home.Parameters.PROJECT_ID,
+ })
+ self._add_resource(
+ mapper, endpoint_group_controller,
path=self.PATH_PREFIX + '/endpoint_groups',
get_action='list_endpoint_groups',
post_action='create_endpoint_group',
diff --git a/keystone-moon/keystone/contrib/endpoint_policy/__init__.py b/keystone-moon/keystone/contrib/endpoint_policy/__init__.py
index 12722dc5..e69de29b 100644
--- a/keystone-moon/keystone/contrib/endpoint_policy/__init__.py
+++ b/keystone-moon/keystone/contrib/endpoint_policy/__init__.py
@@ -1,15 +0,0 @@
-# Copyright 2014 IBM Corp.
-#
-# 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.endpoint_policy.core import * # noqa
diff --git a/keystone-moon/keystone/contrib/endpoint_policy/backends/sql.py b/keystone-moon/keystone/contrib/endpoint_policy/backends/sql.py
index 484444f1..54792f30 100644
--- a/keystone-moon/keystone/contrib/endpoint_policy/backends/sql.py
+++ b/keystone-moon/keystone/contrib/endpoint_policy/backends/sql.py
@@ -1,5 +1,3 @@
-# Copyright 2014 IBM Corp.
-#
# 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
@@ -12,129 +10,23 @@
# License for the specific language governing permissions and limitations
# under the License.
-import uuid
-
-import sqlalchemy
-
-from keystone.common import sql
-from keystone import exception
-
-
-class PolicyAssociation(sql.ModelBase, sql.ModelDictMixin):
- __tablename__ = 'policy_association'
- attributes = ['policy_id', 'endpoint_id', 'region_id', 'service_id']
- # The id column is never exposed outside this module. It only exists to
- # provide a primary key, given that the real columns we would like to use
- # (endpoint_id, service_id, region_id) can be null
- id = sql.Column(sql.String(64), primary_key=True)
- policy_id = sql.Column(sql.String(64), nullable=False)
- endpoint_id = sql.Column(sql.String(64), nullable=True)
- service_id = sql.Column(sql.String(64), nullable=True)
- region_id = sql.Column(sql.String(64), nullable=True)
- __table_args__ = (sql.UniqueConstraint('endpoint_id', 'service_id',
- 'region_id'), {})
-
- def to_dict(self):
- """Returns the model's attributes as a dictionary.
-
- We override the standard method in order to hide the id column,
- since this only exists to provide the table with a primary key.
-
- """
- d = {}
- for attr in self.__class__.attributes:
- d[attr] = getattr(self, attr)
- return d
-
-
-class EndpointPolicy(object):
-
- def create_policy_association(self, policy_id, endpoint_id=None,
- service_id=None, region_id=None):
- with sql.transaction() as session:
- try:
- # See if there is already a row for this association, and if
- # so, update it with the new policy_id
- query = session.query(PolicyAssociation)
- query = query.filter_by(endpoint_id=endpoint_id)
- query = query.filter_by(service_id=service_id)
- query = query.filter_by(region_id=region_id)
- association = query.one()
- association.policy_id = policy_id
- except sql.NotFound:
- association = PolicyAssociation(id=uuid.uuid4().hex,
- policy_id=policy_id,
- endpoint_id=endpoint_id,
- service_id=service_id,
- region_id=region_id)
- session.add(association)
-
- def check_policy_association(self, policy_id, endpoint_id=None,
- service_id=None, region_id=None):
- sql_constraints = sqlalchemy.and_(
- PolicyAssociation.policy_id == policy_id,
- PolicyAssociation.endpoint_id == endpoint_id,
- PolicyAssociation.service_id == service_id,
- PolicyAssociation.region_id == region_id)
-
- # NOTE(henry-nash): Getting a single value to save object
- # management overhead.
- with sql.transaction() as session:
- if session.query(PolicyAssociation.id).filter(
- sql_constraints).distinct().count() == 0:
- raise exception.PolicyAssociationNotFound()
-
- def delete_policy_association(self, policy_id, endpoint_id=None,
- service_id=None, region_id=None):
- with sql.transaction() as session:
- query = session.query(PolicyAssociation)
- query = query.filter_by(policy_id=policy_id)
- query = query.filter_by(endpoint_id=endpoint_id)
- query = query.filter_by(service_id=service_id)
- query = query.filter_by(region_id=region_id)
- query.delete()
+import logging
- def get_policy_association(self, endpoint_id=None,
- service_id=None, region_id=None):
- sql_constraints = sqlalchemy.and_(
- PolicyAssociation.endpoint_id == endpoint_id,
- PolicyAssociation.service_id == service_id,
- PolicyAssociation.region_id == region_id)
+from oslo_log import versionutils
- try:
- with sql.transaction() as session:
- policy_id = session.query(PolicyAssociation.policy_id).filter(
- sql_constraints).distinct().one()
- return {'policy_id': policy_id}
- except sql.NotFound:
- raise exception.PolicyAssociationNotFound()
+from keystone.endpoint_policy.backends import sql
- def list_associations_for_policy(self, policy_id):
- with sql.transaction() as session:
- query = session.query(PolicyAssociation)
- query = query.filter_by(policy_id=policy_id)
- return [ref.to_dict() for ref in query.all()]
+LOG = logging.getLogger(__name__)
- def delete_association_by_endpoint(self, endpoint_id):
- with sql.transaction() as session:
- query = session.query(PolicyAssociation)
- query = query.filter_by(endpoint_id=endpoint_id)
- query.delete()
+_OLD = 'keystone.contrib.endpoint_policy.backends.sql.EndpointPolicy'
+_NEW = 'keystone.endpoint_policy.backends.sql.EndpointPolicy'
- def delete_association_by_service(self, service_id):
- with sql.transaction() as session:
- query = session.query(PolicyAssociation)
- query = query.filter_by(service_id=service_id)
- query.delete()
- def delete_association_by_region(self, region_id):
- with sql.transaction() as session:
- query = session.query(PolicyAssociation)
- query = query.filter_by(region_id=region_id)
- query.delete()
+class EndpointPolicy(sql.EndpointPolicy):
- def delete_association_by_policy(self, policy_id):
- with sql.transaction() as session:
- query = session.query(PolicyAssociation)
- query = query.filter_by(policy_id=policy_id)
- query.delete()
+ @versionutils.deprecated(versionutils.deprecated.LIBERTY,
+ in_favor_of=_NEW,
+ remove_in=1,
+ what=_OLD)
+ def __init__(self, *args, **kwargs):
+ super(EndpointPolicy, self).__init__(*args, **kwargs)
diff --git a/keystone-moon/keystone/contrib/endpoint_policy/migrate_repo/versions/001_add_endpoint_policy_table.py b/keystone-moon/keystone/contrib/endpoint_policy/migrate_repo/versions/001_add_endpoint_policy_table.py
index c77e4380..5c22f169 100644
--- a/keystone-moon/keystone/contrib/endpoint_policy/migrate_repo/versions/001_add_endpoint_policy_table.py
+++ b/keystone-moon/keystone/contrib/endpoint_policy/migrate_repo/versions/001_add_endpoint_policy_table.py
@@ -38,11 +38,3 @@ def upgrade(migrate_engine):
mysql_charset='utf8')
endpoint_policy_table.create(migrate_engine, checkfirst=True)
-
-
-def downgrade(migrate_engine):
- meta = sql.MetaData()
- meta.bind = migrate_engine
- # Operations to reverse the above upgrade go here.
- table = sql.Table('policy_association', meta, autoload=True)
- table.drop()
diff --git a/keystone-moon/keystone/contrib/endpoint_policy/routers.py b/keystone-moon/keystone/contrib/endpoint_policy/routers.py
index 999d1eed..714d1663 100644
--- a/keystone-moon/keystone/contrib/endpoint_policy/routers.py
+++ b/keystone-moon/keystone/contrib/endpoint_policy/routers.py
@@ -1,5 +1,3 @@
-# Copyright 2014 IBM Corp.
-#
# 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
@@ -12,74 +10,23 @@
# 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.contrib.endpoint_policy import controllers
+import logging
+from oslo_log import versionutils
-build_resource_relation = functools.partial(
- json_home.build_v3_extension_resource_relation,
- extension_name='OS-ENDPOINT-POLICY', extension_version='1.0')
+from keystone.common import wsgi
+LOG = logging.getLogger(__name__)
-class EndpointPolicyExtension(wsgi.V3ExtensionRouter):
+_OLD = 'keystone.contrib.endpoint_policy.routers.EndpointPolicyExtension'
+_NEW = 'keystone.endpoint_policy.routers.Routers'
- PATH_PREFIX = '/OS-ENDPOINT-POLICY'
- def add_routes(self, mapper):
- endpoint_policy_controller = controllers.EndpointPolicyV3Controller()
+class EndpointPolicyExtension(wsgi.Middleware):
- self._add_resource(
- mapper, endpoint_policy_controller,
- path='/endpoints/{endpoint_id}' + self.PATH_PREFIX + '/policy',
- get_head_action='get_policy_for_endpoint',
- rel=build_resource_relation(resource_name='endpoint_policy'),
- path_vars={'endpoint_id': json_home.Parameters.ENDPOINT_ID})
- self._add_resource(
- mapper, endpoint_policy_controller,
- path='/policies/{policy_id}' + self.PATH_PREFIX + '/endpoints',
- get_action='list_endpoints_for_policy',
- rel=build_resource_relation(resource_name='policy_endpoints'),
- path_vars={'policy_id': json_home.Parameters.POLICY_ID})
- self._add_resource(
- mapper, endpoint_policy_controller,
- path=('/policies/{policy_id}' + self.PATH_PREFIX +
- '/endpoints/{endpoint_id}'),
- get_head_action='check_policy_association_for_endpoint',
- put_action='create_policy_association_for_endpoint',
- delete_action='delete_policy_association_for_endpoint',
- rel=build_resource_relation(
- resource_name='endpoint_policy_association'),
- path_vars={
- 'policy_id': json_home.Parameters.POLICY_ID,
- 'endpoint_id': json_home.Parameters.ENDPOINT_ID,
- })
- self._add_resource(
- mapper, endpoint_policy_controller,
- path=('/policies/{policy_id}' + self.PATH_PREFIX +
- '/services/{service_id}'),
- get_head_action='check_policy_association_for_service',
- put_action='create_policy_association_for_service',
- delete_action='delete_policy_association_for_service',
- rel=build_resource_relation(
- resource_name='service_policy_association'),
- path_vars={
- 'policy_id': json_home.Parameters.POLICY_ID,
- 'service_id': json_home.Parameters.SERVICE_ID,
- })
- self._add_resource(
- mapper, endpoint_policy_controller,
- path=('/policies/{policy_id}' + self.PATH_PREFIX +
- '/services/{service_id}/regions/{region_id}'),
- get_head_action='check_policy_association_for_region_and_service',
- put_action='create_policy_association_for_region_and_service',
- delete_action='delete_policy_association_for_region_and_service',
- rel=build_resource_relation(
- resource_name='region_and_service_policy_association'),
- path_vars={
- 'policy_id': json_home.Parameters.POLICY_ID,
- 'service_id': json_home.Parameters.SERVICE_ID,
- 'region_id': json_home.Parameters.REGION_ID,
- })
+ @versionutils.deprecated(versionutils.deprecated.LIBERTY,
+ in_favor_of=_NEW,
+ remove_in=1,
+ what=_OLD)
+ def __init__(self, *args, **kwargs):
+ super(EndpointPolicyExtension, self).__init__(*args, **kwargs)
diff --git a/keystone-moon/keystone/contrib/example/core.py b/keystone-moon/keystone/contrib/example/core.py
index 6e85c7f7..e369dc4d 100644
--- a/keystone-moon/keystone/contrib/example/core.py
+++ b/keystone-moon/keystone/contrib/example/core.py
@@ -12,6 +12,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+"""Main entry point into this Example service."""
+
from oslo_log import log
from keystone.common import dependency
@@ -24,15 +26,18 @@ from keystone import notifications
LOG = log.getLogger(__name__)
+@notifications.listener # NOTE(dstanek): only needed if using event_callbacks
@dependency.provider('example_api')
class ExampleManager(manager.Manager):
- """Example Manager.
+ """Default pivot point for this Example backend.
See :mod:`keystone.common.manager.Manager` for more details on
how this dynamically calls the backend.
"""
+ driver_namespace = 'keystone.example'
+
def __init__(self):
# The following is an example of event callbacks. In this setup,
# ExampleManager's data model is depended on project's data model.
@@ -45,8 +50,8 @@ class ExampleManager(manager.Manager):
# project_created_callback will be invoked whenever a new project is
# created.
- # This information is used when the @dependency.provider decorator acts
- # on the class.
+ # This information is used when the @notifications.listener decorator
+ # acts on the class.
self.event_callbacks = {
notifications.ACTIONS.deleted: {
'project': [self.project_deleted_callback],
diff --git a/keystone-moon/keystone/contrib/example/migrate_repo/versions/001_example_table.py b/keystone-moon/keystone/contrib/example/migrate_repo/versions/001_example_table.py
index 10b7ccc7..35061780 100644
--- a/keystone-moon/keystone/contrib/example/migrate_repo/versions/001_example_table.py
+++ b/keystone-moon/keystone/contrib/example/migrate_repo/versions/001_example_table.py
@@ -30,14 +30,3 @@ def upgrade(migrate_engine):
sql.Column('type', sql.String(255)),
sql.Column('extra', sql.Text()))
service_table.create(migrate_engine, checkfirst=True)
-
-
-def downgrade(migrate_engine):
- # Operations to reverse the above upgrade go here.
- meta = sql.MetaData()
- meta.bind = migrate_engine
-
- tables = ['example']
- for t in tables:
- table = sql.Table(t, meta, autoload=True)
- table.drop(migrate_engine, checkfirst=True)
diff --git a/keystone-moon/keystone/contrib/federation/backends/sql.py b/keystone-moon/keystone/contrib/federation/backends/sql.py
index f2c124d0..ed07c08f 100644
--- a/keystone-moon/keystone/contrib/federation/backends/sql.py
+++ b/keystone-moon/keystone/contrib/federation/backends/sql.py
@@ -17,6 +17,7 @@ from oslo_serialization import jsonutils
from keystone.common import sql
from keystone.contrib.federation import core
from keystone import exception
+from sqlalchemy import orm
class FederationProtocolModel(sql.ModelBase, sql.DictBase):
@@ -44,13 +45,53 @@ class FederationProtocolModel(sql.ModelBase, sql.DictBase):
class IdentityProviderModel(sql.ModelBase, sql.DictBase):
__tablename__ = 'identity_provider'
- attributes = ['id', 'remote_id', 'enabled', 'description']
- mutable_attributes = frozenset(['description', 'enabled', 'remote_id'])
+ attributes = ['id', 'enabled', 'description', 'remote_ids']
+ mutable_attributes = frozenset(['description', 'enabled', 'remote_ids'])
id = sql.Column(sql.String(64), primary_key=True)
- remote_id = sql.Column(sql.String(256), nullable=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):
@@ -75,6 +116,7 @@ class MappingModel(sql.ModelBase, sql.DictBase):
@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):
@@ -82,20 +124,23 @@ class MappingModel(sql.ModelBase, sql.DictBase):
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', 'sp_url']
+ attributes = ['auth_url', 'id', 'enabled', 'description',
+ 'relay_state_prefix', 'sp_url']
mutable_attributes = frozenset(['auth_url', 'description', 'enabled',
- 'sp_url'])
+ '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):
@@ -123,6 +168,7 @@ class Federation(core.Driver):
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)
@@ -133,7 +179,7 @@ class Federation(core.Driver):
return idp_ref
def _get_idp_from_remote_id(self, session, remote_id):
- q = session.query(IdentityProviderModel)
+ q = session.query(IdPRemoteIdsModel)
q = q.filter_by(remote_id=remote_id)
try:
return q.one()
@@ -153,8 +199,8 @@ class Federation(core.Driver):
def get_idp_from_remote_id(self, remote_id):
with sql.transaction() as session:
- idp_ref = self._get_idp_from_remote_id(session, remote_id)
- return idp_ref.to_dict()
+ 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:
@@ -214,6 +260,11 @@ class Federation(core.Driver):
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)
@@ -225,7 +276,7 @@ class Federation(core.Driver):
def create_mapping(self, mapping_id, mapping):
ref = {}
ref['id'] = mapping_id
- ref['rules'] = jsonutils.dumps(mapping.get('rules'))
+ ref['rules'] = mapping.get('rules')
with sql.transaction() as session:
mapping_ref = MappingModel.from_dict(ref)
session.add(mapping_ref)
@@ -250,7 +301,7 @@ class Federation(core.Driver):
def update_mapping(self, mapping_id, mapping):
ref = {}
ref['id'] = mapping_id
- ref['rules'] = jsonutils.dumps(mapping.get('rules'))
+ ref['rules'] = mapping.get('rules')
with sql.transaction() as session:
mapping_ref = self._get_mapping(session, mapping_id)
old_mapping = mapping_ref.to_dict()
diff --git a/keystone-moon/keystone/contrib/federation/constants.py b/keystone-moon/keystone/contrib/federation/constants.py
new file mode 100644
index 00000000..afb38494
--- /dev/null
+++ b/keystone-moon/keystone/contrib/federation/constants.py
@@ -0,0 +1,15 @@
+# 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.
+
+FEDERATION = 'OS-FEDERATION'
+IDENTITY_PROVIDER = 'OS-FEDERATION:identity_provider'
+PROTOCOL = 'OS-FEDERATION:protocol'
diff --git a/keystone-moon/keystone/contrib/federation/controllers.py b/keystone-moon/keystone/contrib/federation/controllers.py
index 6066a33f..912d45d5 100644
--- a/keystone-moon/keystone/contrib/federation/controllers.py
+++ b/keystone-moon/keystone/contrib/federation/controllers.py
@@ -10,7 +10,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-"""Extensions supporting Federation."""
+"""Workflow logic for the Federation service."""
import string
@@ -55,9 +55,9 @@ class IdentityProvider(_ControllerBase):
collection_name = 'identity_providers'
member_name = 'identity_provider'
- _mutable_parameters = frozenset(['description', 'enabled', 'remote_id'])
+ _mutable_parameters = frozenset(['description', 'enabled', 'remote_ids'])
_public_parameters = frozenset(['id', 'enabled', 'description',
- 'remote_id', 'links'
+ 'remote_ids', 'links'
])
@classmethod
@@ -247,6 +247,36 @@ class MappingController(_ControllerBase):
@dependency.requires('federation_api')
class Auth(auth_controllers.Auth):
+ def _get_sso_origin_host(self, context):
+ """Validate and return originating dashboard URL.
+
+ Make sure the parameter is specified in the request's URL as well its
+ value belongs to a list of trusted dashboards.
+
+ :param context: request's context
+ :raises: exception.ValidationError: ``origin`` query parameter was not
+ specified. The URL is deemed invalid.
+ :raises: exception.Unauthorized: URL specified in origin query
+ parameter does not exist in list of websso trusted dashboards.
+ :returns: URL with the originating dashboard
+
+ """
+ if 'origin' in context['query_string']:
+ origin = context['query_string'].get('origin')
+ host = urllib.parse.unquote_plus(origin)
+ else:
+ msg = _('Request must have an origin query parameter')
+ LOG.error(msg)
+ raise exception.ValidationError(msg)
+
+ if host not in CONF.federation.trusted_dashboard:
+ msg = _('%(host)s is not a trusted dashboard host')
+ msg = msg % {'host': host}
+ LOG.error(msg)
+ raise exception.Unauthorized(msg)
+
+ return host
+
def federated_authentication(self, context, identity_provider, protocol):
"""Authenticate from dedicated url endpoint.
@@ -268,33 +298,23 @@ class Auth(auth_controllers.Auth):
def federated_sso_auth(self, context, protocol_id):
try:
- remote_id_name = CONF.federation.remote_id_attribute
+ remote_id_name = utils.get_remote_id_parameter(protocol_id)
remote_id = context['environment'][remote_id_name]
except KeyError:
msg = _('Missing entity ID from environment')
LOG.error(msg)
raise exception.Unauthorized(msg)
- if 'origin' in context['query_string']:
- origin = context['query_string'].get('origin')
- host = urllib.parse.unquote_plus(origin)
- else:
- msg = _('Request must have an origin query parameter')
- LOG.error(msg)
- raise exception.ValidationError(msg)
+ host = self._get_sso_origin_host(context)
- if host in CONF.federation.trusted_dashboard:
- ref = self.federation_api.get_idp_from_remote_id(remote_id)
- identity_provider = ref['id']
- res = self.federated_authentication(context, identity_provider,
- protocol_id)
- token_id = res.headers['X-Subject-Token']
- return self.render_html_response(host, token_id)
- else:
- msg = _('%(host)s is not a trusted dashboard host')
- msg = msg % {'host': host}
- LOG.error(msg)
- raise exception.Unauthorized(msg)
+ ref = self.federation_api.get_idp_from_remote_id(remote_id)
+ # NOTE(stevemar): the returned object is a simple dict that
+ # contains the idp_id and remote_id.
+ identity_provider = ref['idp_id']
+ res = self.federated_authentication(context, identity_provider,
+ protocol_id)
+ token_id = res.headers['X-Subject-Token']
+ return self.render_html_response(host, token_id)
def render_html_response(self, host, token_id):
"""Forms an HTML Form from a template with autosubmit."""
@@ -309,45 +329,77 @@ class Auth(auth_controllers.Auth):
return webob.Response(body=body, status='200',
headerlist=headers)
- @validation.validated(schema.saml_create, 'auth')
- def create_saml_assertion(self, context, auth):
- """Exchange a scoped token for a SAML assertion.
-
- :param auth: Dictionary that contains a token and service provider id
- :returns: SAML Assertion based on properties from the token
- """
-
+ def _create_base_saml_assertion(self, context, auth):
issuer = CONF.saml.idp_entity_id
sp_id = auth['scope']['service_provider']['id']
service_provider = self.federation_api.get_sp(sp_id)
utils.assert_enabled_service_provider_object(service_provider)
-
sp_url = service_provider.get('sp_url')
- auth_url = service_provider.get('auth_url')
token_id = auth['identity']['token']['id']
token_data = self.token_provider_api.validate_token(token_id)
token_ref = token_model.KeystoneToken(token_id, token_data)
- subject = token_ref.user_name
- roles = token_ref.role_names
if not token_ref.project_scoped:
action = _('Use a project scoped token when attempting to create '
'a SAML assertion')
raise exception.ForbiddenAction(action=action)
+ subject = token_ref.user_name
+ roles = token_ref.role_names
project = token_ref.project_name
+ # NOTE(rodrigods): the domain name is necessary in order to distinguish
+ # between projects and users with the same name in different domains.
+ project_domain_name = token_ref.project_domain_name
+ subject_domain_name = token_ref.user_domain_name
+
generator = keystone_idp.SAMLGenerator()
- response = generator.samlize_token(issuer, sp_url, subject, roles,
- project)
+ response = generator.samlize_token(
+ issuer, sp_url, subject, subject_domain_name,
+ roles, project, project_domain_name)
+ return (response, service_provider)
+
+ def _build_response_headers(self, service_provider):
+ return [('Content-Type', 'text/xml'),
+ ('X-sp-url', six.binary_type(service_provider['sp_url'])),
+ ('X-auth-url', six.binary_type(service_provider['auth_url']))]
+
+ @validation.validated(schema.saml_create, 'auth')
+ def create_saml_assertion(self, context, auth):
+ """Exchange a scoped token for a SAML assertion.
+
+ :param auth: Dictionary that contains a token and service provider ID
+ :returns: SAML Assertion based on properties from the token
+ """
+ t = self._create_base_saml_assertion(context, auth)
+ (response, service_provider) = t
+
+ headers = self._build_response_headers(service_provider)
return wsgi.render_response(body=response.to_string(),
status=('200', 'OK'),
- headers=[('Content-Type', 'text/xml'),
- ('X-sp-url',
- six.binary_type(sp_url)),
- ('X-auth-url',
- six.binary_type(auth_url))])
+ headers=headers)
+
+ @validation.validated(schema.saml_create, 'auth')
+ def create_ecp_assertion(self, context, auth):
+ """Exchange a scoped token for an ECP assertion.
+
+ :param auth: Dictionary that contains a token and service provider ID
+ :returns: ECP Assertion based on properties from the token
+ """
+
+ t = self._create_base_saml_assertion(context, auth)
+ (saml_assertion, service_provider) = t
+ relay_state_prefix = service_provider.get('relay_state_prefix')
+
+ generator = keystone_idp.ECPGenerator()
+ ecp_assertion = generator.generate_ecp(saml_assertion,
+ relay_state_prefix)
+
+ headers = self._build_response_headers(service_provider)
+ return wsgi.render_response(body=ecp_assertion.to_string(),
+ status=('200', 'OK'),
+ headers=headers)
@dependency.requires('assignment_api', 'resource_api')
@@ -404,15 +456,17 @@ class ServiceProvider(_ControllerBase):
member_name = 'service_provider'
_mutable_parameters = frozenset(['auth_url', 'description', 'enabled',
- 'sp_url'])
+ 'relay_state_prefix', 'sp_url'])
_public_parameters = frozenset(['auth_url', 'id', 'enabled', 'description',
- 'links', 'sp_url'])
+ 'links', 'relay_state_prefix', 'sp_url'])
@controller.protected()
@validation.validated(schema.service_provider_create, 'service_provider')
def create_service_provider(self, context, sp_id, service_provider):
service_provider = self._normalize_dict(service_provider)
service_provider.setdefault('enabled', False)
+ service_provider.setdefault('relay_state_prefix',
+ CONF.saml.relay_state_prefix)
ServiceProvider.check_immutable_params(service_provider)
sp_ref = self.federation_api.create_sp(sp_id, service_provider)
response = ServiceProvider.wrap_member(context, sp_ref)
diff --git a/keystone-moon/keystone/contrib/federation/core.py b/keystone-moon/keystone/contrib/federation/core.py
index b596cff7..2ab75ecb 100644
--- a/keystone-moon/keystone/contrib/federation/core.py
+++ b/keystone-moon/keystone/contrib/federation/core.py
@@ -10,7 +10,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-"""Extension supporting Federation."""
+"""Main entry point into the Federation service."""
import abc
@@ -21,6 +21,7 @@ import six
from keystone.common import dependency
from keystone.common import extension
from keystone.common import manager
+from keystone.contrib.federation import utils
from keystone import exception
@@ -41,11 +42,6 @@ EXTENSION_DATA = {
extension.register_admin_extension(EXTENSION_DATA['alias'], EXTENSION_DATA)
extension.register_public_extension(EXTENSION_DATA['alias'], EXTENSION_DATA)
-FEDERATION = 'OS-FEDERATION'
-IDENTITY_PROVIDER = 'OS-FEDERATION:identity_provider'
-PROTOCOL = 'OS-FEDERATION:protocol'
-FEDERATED_DOMAIN_KEYWORD = 'Federated'
-
@dependency.provider('federation_api')
class Manager(manager.Manager):
@@ -55,6 +51,9 @@ class Manager(manager.Manager):
dynamically calls the backend.
"""
+
+ driver_namespace = 'keystone.federation'
+
def __init__(self):
super(Manager, self).__init__(CONF.federation.driver)
@@ -84,6 +83,13 @@ class Manager(manager.Manager):
service_providers = self.driver.get_enabled_service_providers()
return [normalize(sp) for sp in service_providers]
+ def evaluate(self, idp_id, protocol_id, assertion_data):
+ mapping = self.get_mapping_from_idp_and_protocol(idp_id, protocol_id)
+ rules = mapping['rules']
+ rule_processor = utils.RuleProcessor(rules)
+ mapped_properties = rule_processor.process(assertion_data)
+ return mapped_properties, mapping['id']
+
@six.add_metaclass(abc.ABCMeta)
class Driver(object):
diff --git a/keystone-moon/keystone/contrib/federation/idp.py b/keystone-moon/keystone/contrib/federation/idp.py
index bf400135..739fc01a 100644
--- a/keystone-moon/keystone/contrib/federation/idp.py
+++ b/keystone-moon/keystone/contrib/federation/idp.py
@@ -17,17 +17,24 @@ import uuid
from oslo_config import cfg
from oslo_log import log
+from oslo_utils import fileutils
+from oslo_utils import importutils
from oslo_utils import timeutils
import saml2
+from saml2 import client_base
from saml2 import md
+from saml2.profile import ecp
from saml2 import saml
from saml2 import samlp
+from saml2.schema import soapenv
from saml2 import sigver
-import xmldsig
+xmldsig = importutils.try_import("saml2.xmldsig")
+if not xmldsig:
+ xmldsig = importutils.try_import("xmldsig")
+from keystone.common import utils
from keystone import exception
from keystone.i18n import _, _LE
-from keystone.openstack.common import fileutils
LOG = log.getLogger(__name__)
@@ -40,8 +47,8 @@ class SAMLGenerator(object):
def __init__(self):
self.assertion_id = uuid.uuid4().hex
- def samlize_token(self, issuer, recipient, user, roles, project,
- expires_in=None):
+ def samlize_token(self, issuer, recipient, user, user_domain_name, roles,
+ project, project_domain_name, expires_in=None):
"""Convert Keystone attributes to a SAML assertion.
:param issuer: URL of the issuing party
@@ -50,10 +57,14 @@ class SAMLGenerator(object):
:type recipient: string
:param user: User name
:type user: string
+ :param user_domain_name: User Domain name
+ :type user_domain_name: string
:param roles: List of role names
:type roles: list
:param project: Project name
:type project: string
+ :param project_domain_name: Project Domain name
+ :type project_domain_name: string
:param expires_in: Sets how long the assertion is valid for, in seconds
:type expires_in: int
@@ -64,8 +75,8 @@ class SAMLGenerator(object):
status = self._create_status()
saml_issuer = self._create_issuer(issuer)
subject = self._create_subject(user, expiration_time, recipient)
- attribute_statement = self._create_attribute_statement(user, roles,
- project)
+ attribute_statement = self._create_attribute_statement(
+ user, user_domain_name, roles, project, project_domain_name)
authn_statement = self._create_authn_statement(issuer, expiration_time)
signature = self._create_signature()
@@ -84,7 +95,7 @@ class SAMLGenerator(object):
expires_in = CONF.saml.assertion_expiration_time
now = timeutils.utcnow()
future = now + datetime.timedelta(seconds=expires_in)
- return timeutils.isotime(future, subsecond=True)
+ return utils.isotime(future, subsecond=True)
def _create_status(self):
"""Create an object that represents a SAML Status.
@@ -150,58 +161,64 @@ class SAMLGenerator(object):
subject.name_id = name_id
return subject
- def _create_attribute_statement(self, user, roles, project):
+ def _create_attribute_statement(self, user, user_domain_name, roles,
+ project, project_domain_name):
"""Create an object that represents a SAML AttributeStatement.
- <ns0:AttributeStatement
- xmlns:ns0="urn:oasis:names:tc:SAML:2.0:assertion"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <ns0:AttributeStatement>
<ns0:Attribute Name="openstack_user">
<ns0:AttributeValue
xsi:type="xs:string">test_user</ns0:AttributeValue>
</ns0:Attribute>
+ <ns0:Attribute Name="openstack_user_domain">
+ <ns0:AttributeValue
+ xsi:type="xs:string">Default</ns0:AttributeValue>
+ </ns0:Attribute>
<ns0:Attribute Name="openstack_roles">
<ns0:AttributeValue
xsi:type="xs:string">admin</ns0:AttributeValue>
<ns0:AttributeValue
xsi:type="xs:string">member</ns0:AttributeValue>
</ns0:Attribute>
- <ns0:Attribute Name="openstack_projects">
+ <ns0:Attribute Name="openstack_project">
<ns0:AttributeValue
xsi:type="xs:string">development</ns0:AttributeValue>
</ns0:Attribute>
+ <ns0:Attribute Name="openstack_project_domain">
+ <ns0:AttributeValue
+ xsi:type="xs:string">Default</ns0:AttributeValue>
+ </ns0:Attribute>
</ns0:AttributeStatement>
:return: XML <AttributeStatement> object
"""
- openstack_user = 'openstack_user'
- user_attribute = saml.Attribute()
- user_attribute.name = openstack_user
- user_value = saml.AttributeValue()
- user_value.set_text(user)
- user_attribute.attribute_value = user_value
-
- openstack_roles = 'openstack_roles'
- roles_attribute = saml.Attribute()
- roles_attribute.name = openstack_roles
-
- for role in roles:
- role_value = saml.AttributeValue()
- role_value.set_text(role)
- roles_attribute.attribute_value.append(role_value)
-
- openstack_project = 'openstack_project'
- project_attribute = saml.Attribute()
- project_attribute.name = openstack_project
- project_value = saml.AttributeValue()
- project_value.set_text(project)
- project_attribute.attribute_value = project_value
+
+ def _build_attribute(attribute_name, attribute_values):
+ attribute = saml.Attribute()
+ attribute.name = attribute_name
+
+ for value in attribute_values:
+ attribute_value = saml.AttributeValue()
+ attribute_value.set_text(value)
+ attribute.attribute_value.append(attribute_value)
+
+ return attribute
+
+ user_attribute = _build_attribute('openstack_user', [user])
+ roles_attribute = _build_attribute('openstack_roles', roles)
+ project_attribute = _build_attribute('openstack_project', [project])
+ project_domain_attribute = _build_attribute(
+ 'openstack_project_domain', [project_domain_name])
+ user_domain_attribute = _build_attribute(
+ 'openstack_user_domain', [user_domain_name])
attribute_statement = saml.AttributeStatement()
attribute_statement.attribute.append(user_attribute)
attribute_statement.attribute.append(roles_attribute)
attribute_statement.attribute.append(project_attribute)
+ attribute_statement.attribute.append(project_domain_attribute)
+ attribute_statement.attribute.append(user_domain_attribute)
return attribute_statement
def _create_authn_statement(self, issuer, expiration_time):
@@ -224,7 +241,7 @@ class SAMLGenerator(object):
"""
authn_statement = saml.AuthnStatement()
- authn_statement.authn_instant = timeutils.isotime()
+ authn_statement.authn_instant = utils.isotime()
authn_statement.session_index = uuid.uuid4().hex
authn_statement.session_not_on_or_after = expiration_time
@@ -261,7 +278,7 @@ class SAMLGenerator(object):
"""
assertion = saml.Assertion()
assertion.id = self.assertion_id
- assertion.issue_instant = timeutils.isotime()
+ assertion.issue_instant = utils.isotime()
assertion.version = '2.0'
assertion.issuer = issuer
assertion.signature = signature
@@ -289,7 +306,7 @@ class SAMLGenerator(object):
response = samlp.Response()
response.id = uuid.uuid4().hex
response.destination = recipient
- response.issue_instant = timeutils.isotime()
+ response.issue_instant = utils.isotime()
response.version = '2.0'
response.issuer = issuer
response.status = status
@@ -397,6 +414,7 @@ def _sign_assertion(assertion):
command_list = [xmlsec_binary, '--sign', '--privkey-pem', certificates,
'--id-attr:ID', 'Assertion']
+ file_path = None
try:
# NOTE(gyee): need to make the namespace prefixes explicit so
# they won't get reassigned when we wrap the assertion into
@@ -405,15 +423,19 @@ def _sign_assertion(assertion):
nspair={'saml': saml2.NAMESPACE,
'xmldsig': xmldsig.NAMESPACE}))
command_list.append(file_path)
- stdout = subprocess.check_output(command_list)
+ stdout = subprocess.check_output(command_list,
+ stderr=subprocess.STDOUT)
except Exception as e:
msg = _LE('Error when signing assertion, reason: %(reason)s')
msg = msg % {'reason': e}
+ if hasattr(e, 'output'):
+ msg += ' output: %(output)s' % {'output': e.output}
LOG.error(msg)
raise exception.SAMLSigningError(reason=e)
finally:
try:
- os.remove(file_path)
+ if file_path:
+ os.remove(file_path)
except OSError:
pass
@@ -556,3 +578,31 @@ class MetadataGenerator(object):
if value is None:
return False
return True
+
+
+class ECPGenerator(object):
+ """A class for generating an ECP assertion."""
+
+ @staticmethod
+ def generate_ecp(saml_assertion, relay_state_prefix):
+ ecp_generator = ECPGenerator()
+ header = ecp_generator._create_header(relay_state_prefix)
+ body = ecp_generator._create_body(saml_assertion)
+ envelope = soapenv.Envelope(header=header, body=body)
+ return envelope
+
+ def _create_header(self, relay_state_prefix):
+ relay_state_text = relay_state_prefix + uuid.uuid4().hex
+ relay_state = ecp.RelayState(actor=client_base.ACTOR,
+ must_understand='1',
+ text=relay_state_text)
+ header = soapenv.Header()
+ header.extension_elements = (
+ [saml2.element_to_extension_element(relay_state)])
+ return header
+
+ def _create_body(self, saml_assertion):
+ body = soapenv.Body()
+ body.extension_elements = (
+ [saml2.element_to_extension_element(saml_assertion)])
+ return body
diff --git a/keystone-moon/keystone/contrib/federation/migrate_repo/versions/001_add_identity_provider_table.py b/keystone-moon/keystone/contrib/federation/migrate_repo/versions/001_add_identity_provider_table.py
index cfb6f2c4..9a4d574b 100644
--- a/keystone-moon/keystone/contrib/federation/migrate_repo/versions/001_add_identity_provider_table.py
+++ b/keystone-moon/keystone/contrib/federation/migrate_repo/versions/001_add_identity_provider_table.py
@@ -40,12 +40,3 @@ def upgrade(migrate_engine):
mysql_charset='utf8')
federation_protocol_table.create(migrate_engine, checkfirst=True)
-
-
-def downgrade(migrate_engine):
- meta = sql.MetaData()
- meta.bind = migrate_engine
- tables = ['federation_protocol', 'identity_provider']
- for table_name in tables:
- table = sql.Table(table_name, meta, autoload=True)
- table.drop()
diff --git a/keystone-moon/keystone/contrib/federation/migrate_repo/versions/002_add_mapping_tables.py b/keystone-moon/keystone/contrib/federation/migrate_repo/versions/002_add_mapping_tables.py
index f827f9a9..9a155f5c 100644
--- a/keystone-moon/keystone/contrib/federation/migrate_repo/versions/002_add_mapping_tables.py
+++ b/keystone-moon/keystone/contrib/federation/migrate_repo/versions/002_add_mapping_tables.py
@@ -25,13 +25,3 @@ def upgrade(migrate_engine):
mysql_engine='InnoDB',
mysql_charset='utf8')
mapping_table.create(migrate_engine, checkfirst=True)
-
-
-def downgrade(migrate_engine):
- meta = sql.MetaData()
- meta.bind = migrate_engine
- # Drop previously created tables
- tables = ['mapping']
- for table_name in tables:
- table = sql.Table(table_name, meta, autoload=True)
- table.drop()
diff --git a/keystone-moon/keystone/contrib/federation/migrate_repo/versions/003_mapping_id_nullable_false.py b/keystone-moon/keystone/contrib/federation/migrate_repo/versions/003_mapping_id_nullable_false.py
index eb8b2378..1731b0d3 100644
--- a/keystone-moon/keystone/contrib/federation/migrate_repo/versions/003_mapping_id_nullable_false.py
+++ b/keystone-moon/keystone/contrib/federation/migrate_repo/versions/003_mapping_id_nullable_false.py
@@ -27,9 +27,3 @@ def upgrade(migrate_engine):
values(mapping_id=''))
migrate_engine.execute(stmt)
federation_protocol.c.mapping_id.alter(nullable=False)
-
-
-def downgrade(migrate_engine):
- meta = sa.MetaData(bind=migrate_engine)
- federation_protocol = sa.Table('federation_protocol', meta, autoload=True)
- federation_protocol.c.mapping_id.alter(nullable=True)
diff --git a/keystone-moon/keystone/contrib/federation/migrate_repo/versions/004_add_remote_id_column.py b/keystone-moon/keystone/contrib/federation/migrate_repo/versions/004_add_remote_id_column.py
index dbe5d1f1..2e0aaf93 100644
--- a/keystone-moon/keystone/contrib/federation/migrate_repo/versions/004_add_remote_id_column.py
+++ b/keystone-moon/keystone/contrib/federation/migrate_repo/versions/004_add_remote_id_column.py
@@ -21,10 +21,3 @@ def upgrade(migrate_engine):
idp_table = utils.get_table(migrate_engine, 'identity_provider')
remote_id = sql.Column('remote_id', sql.String(256), nullable=True)
idp_table.create_column(remote_id)
-
-
-def downgrade(migrate_engine):
- meta = sql.MetaData()
- meta.bind = migrate_engine
- idp_table = utils.get_table(migrate_engine, 'identity_provider')
- idp_table.drop_column('remote_id')
diff --git a/keystone-moon/keystone/contrib/federation/migrate_repo/versions/005_add_service_provider_table.py b/keystone-moon/keystone/contrib/federation/migrate_repo/versions/005_add_service_provider_table.py
index bff6a252..1594f893 100644
--- a/keystone-moon/keystone/contrib/federation/migrate_repo/versions/005_add_service_provider_table.py
+++ b/keystone-moon/keystone/contrib/federation/migrate_repo/versions/005_add_service_provider_table.py
@@ -29,10 +29,3 @@ def upgrade(migrate_engine):
mysql_charset='utf8')
sp_table.create(migrate_engine, checkfirst=True)
-
-
-def downgrade(migrate_engine):
- meta = sql.MetaData()
- meta.bind = migrate_engine
- table = sql.Table('service_provider', meta, autoload=True)
- table.drop()
diff --git a/keystone-moon/keystone/contrib/federation/migrate_repo/versions/006_fixup_service_provider_attributes.py b/keystone-moon/keystone/contrib/federation/migrate_repo/versions/006_fixup_service_provider_attributes.py
index 8a42ce3a..dc18f548 100644
--- a/keystone-moon/keystone/contrib/federation/migrate_repo/versions/006_fixup_service_provider_attributes.py
+++ b/keystone-moon/keystone/contrib/federation/migrate_repo/versions/006_fixup_service_provider_attributes.py
@@ -38,11 +38,3 @@ def upgrade(migrate_engine):
sp_table.c.auth_url.alter(nullable=False)
sp_table.c.sp_url.alter(nullable=False)
-
-
-def downgrade(migrate_engine):
- meta = sql.MetaData()
- meta.bind = migrate_engine
- sp_table = sql.Table(_SP_TABLE_NAME, meta, autoload=True)
- sp_table.c.auth_url.alter(nullable=True)
- sp_table.c.sp_url.alter(nullable=True)
diff --git a/keystone-moon/keystone/contrib/federation/migrate_repo/versions/007_add_remote_id_table.py b/keystone-moon/keystone/contrib/federation/migrate_repo/versions/007_add_remote_id_table.py
new file mode 100644
index 00000000..cd571245
--- /dev/null
+++ b/keystone-moon/keystone/contrib/federation/migrate_repo/versions/007_add_remote_id_table.py
@@ -0,0 +1,41 @@
+# 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 sqlalchemy as orm
+
+
+def upgrade(migrate_engine):
+ meta = orm.MetaData()
+ meta.bind = migrate_engine
+ idp_table = orm.Table('identity_provider', meta, autoload=True)
+ remote_id_table = orm.Table(
+ 'idp_remote_ids',
+ meta,
+ orm.Column('idp_id',
+ orm.String(64),
+ orm.ForeignKey('identity_provider.id',
+ ondelete='CASCADE')),
+ orm.Column('remote_id',
+ orm.String(255),
+ primary_key=True),
+ mysql_engine='InnoDB',
+ mysql_charset='utf8')
+
+ remote_id_table.create(migrate_engine, checkfirst=True)
+
+ select = orm.sql.select([idp_table.c.id, idp_table.c.remote_id])
+ for identity in migrate_engine.execute(select):
+ remote_idp_entry = {'idp_id': identity.id,
+ 'remote_id': identity.remote_id}
+ remote_id_table.insert(remote_idp_entry).execute()
+
+ idp_table.drop_column('remote_id')
diff --git a/keystone-moon/keystone/contrib/federation/migrate_repo/versions/008_add_relay_state_to_sp.py b/keystone-moon/keystone/contrib/federation/migrate_repo/versions/008_add_relay_state_to_sp.py
new file mode 100644
index 00000000..150dcfed
--- /dev/null
+++ b/keystone-moon/keystone/contrib/federation/migrate_repo/versions/008_add_relay_state_to_sp.py
@@ -0,0 +1,39 @@
+# 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_config import cfg
+from oslo_db.sqlalchemy import utils
+import sqlalchemy as sql
+
+
+CONF = cfg.CONF
+_SP_TABLE_NAME = 'service_provider'
+_RELAY_STATE_PREFIX = 'relay_state_prefix'
+
+
+def upgrade(migrate_engine):
+ meta = sql.MetaData()
+ meta.bind = migrate_engine
+
+ idp_table = utils.get_table(migrate_engine, _SP_TABLE_NAME)
+ relay_state_prefix_default = CONF.saml.relay_state_prefix
+ relay_state_prefix = sql.Column(_RELAY_STATE_PREFIX, sql.String(256),
+ nullable=False,
+ server_default=relay_state_prefix_default)
+ idp_table.create_column(relay_state_prefix)
+
+
+def downgrade(migrate_engine):
+ meta = sql.MetaData()
+ meta.bind = migrate_engine
+ idp_table = utils.get_table(migrate_engine, _SP_TABLE_NAME)
+ idp_table.drop_column(_RELAY_STATE_PREFIX)
diff --git a/keystone-moon/keystone/contrib/federation/routers.py b/keystone-moon/keystone/contrib/federation/routers.py
index 9a6224b7..d8fa8175 100644
--- a/keystone-moon/keystone/contrib/federation/routers.py
+++ b/keystone-moon/keystone/contrib/federation/routers.py
@@ -36,44 +36,45 @@ class FederationExtension(wsgi.V3ExtensionRouter):
The API looks like::
- PUT /OS-FEDERATION/identity_providers/$identity_provider
+ PUT /OS-FEDERATION/identity_providers/{idp_id}
GET /OS-FEDERATION/identity_providers
- GET /OS-FEDERATION/identity_providers/$identity_provider
- DELETE /OS-FEDERATION/identity_providers/$identity_provider
- PATCH /OS-FEDERATION/identity_providers/$identity_provider
+ 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/
- $identity_provider/protocols/$protocol
+ {idp_id}/protocols/{protocol_id}
GET /OS-FEDERATION/identity_providers/
- $identity_provider/protocols
+ {idp_id}/protocols
GET /OS-FEDERATION/identity_providers/
- $identity_provider/protocols/$protocol
+ {idp_id}/protocols/{protocol_id}
PATCH /OS-FEDERATION/identity_providers/
- $identity_provider/protocols/$protocol
+ {idp_id}/protocols/{protocol_id}
DELETE /OS-FEDERATION/identity_providers/
- $identity_provider/protocols/$protocol
+ {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
+ 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/$service_provider
+ PUT /OS-FEDERATION/service_providers/{sp_id}
GET /OS-FEDERATION/service_providers
- GET /OS-FEDERATION/service_providers/$service_provider
- DELETE /OS-FEDERATION/service_providers/$service_provider
- PATCH /OS-FEDERATION/service_providers/$service_provider
+ 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 /OS-FEDERATION/identity_providers/{identity_provider}/
+ protocols/{protocol}/auth
+ POST /OS-FEDERATION/identity_providers/{identity_provider}/
+ protocols/{protocol}/auth
POST /auth/OS-FEDERATION/saml2
+ POST /auth/OS-FEDERATION/saml2/ecp
GET /OS-FEDERATION/saml2/metadata
GET /auth/OS-FEDERATION/websso/{protocol_id}
@@ -191,6 +192,8 @@ class FederationExtension(wsgi.V3ExtensionRouter):
path=self._construct_url('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}/'
@@ -202,8 +205,6 @@ class FederationExtension(wsgi.V3ExtensionRouter):
'identity_provider': IDP_ID_PARAMETER_RELATION,
'protocol': PROTOCOL_ID_PARAMETER_RELATION,
})
-
- # Auth operations
self._add_resource(
mapper, auth_controller,
path='/auth' + self._construct_url('saml2'),
@@ -211,6 +212,11 @@ class FederationExtension(wsgi.V3ExtensionRouter):
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'),
diff --git a/keystone-moon/keystone/contrib/federation/schema.py b/keystone-moon/keystone/contrib/federation/schema.py
index 645e1129..17818a98 100644
--- a/keystone-moon/keystone/contrib/federation/schema.py
+++ b/keystone-moon/keystone/contrib/federation/schema.py
@@ -58,7 +58,8 @@ _service_provider_properties = {
'auth_url': parameter_types.url,
'sp_url': parameter_types.url,
'description': validation.nullable(parameter_types.description),
- 'enabled': parameter_types.boolean
+ 'enabled': parameter_types.boolean,
+ 'relay_state_prefix': validation.nullable(parameter_types.description)
}
service_provider_create = {
diff --git a/keystone-moon/keystone/contrib/federation/utils.py b/keystone-moon/keystone/contrib/federation/utils.py
index 939fe9a0..b0db3cdd 100644
--- a/keystone-moon/keystone/contrib/federation/utils.py
+++ b/keystone-moon/keystone/contrib/federation/utils.py
@@ -21,7 +21,6 @@ from oslo_log import log
from oslo_utils import timeutils
import six
-from keystone.contrib import federation
from keystone import exception
from keystone.i18n import _, _LW
@@ -191,14 +190,37 @@ def validate_groups_cardinality(group_ids, mapping_id):
raise exception.MissingGroups(mapping_id=mapping_id)
-def validate_idp(idp, assertion):
- """Check if the IdP providing the assertion is the one registered for
- the mapping
+def get_remote_id_parameter(protocol):
+ # NOTE(marco-fargetta): Since we support any protocol ID, we attempt to
+ # retrieve the remote_id_attribute of the protocol ID. If it's not
+ # registered in the config, then register the option and try again.
+ # This allows the user to register protocols other than oidc and saml2.
+ remote_id_parameter = None
+ try:
+ remote_id_parameter = CONF[protocol]['remote_id_attribute']
+ except AttributeError:
+ CONF.register_opt(cfg.StrOpt('remote_id_attribute'),
+ group=protocol)
+ try:
+ remote_id_parameter = CONF[protocol]['remote_id_attribute']
+ except AttributeError:
+ pass
+ if not remote_id_parameter:
+ LOG.debug('Cannot find "remote_id_attribute" in configuration '
+ 'group %s. Trying default location in '
+ 'group federation.', protocol)
+ remote_id_parameter = CONF.federation.remote_id_attribute
+
+ return remote_id_parameter
+
+
+def validate_idp(idp, protocol, assertion):
+ """Validate the IdP providing the assertion is registered for the mapping.
"""
- remote_id_parameter = CONF.federation.remote_id_attribute
- if not remote_id_parameter or not idp['remote_id']:
- LOG.warning(_LW('Impossible to identify the IdP %s '),
- idp['id'])
+
+ remote_id_parameter = get_remote_id_parameter(protocol)
+ if not remote_id_parameter or not idp['remote_ids']:
+ LOG.debug('Impossible to identify the IdP %s ', idp['id'])
# If nothing is defined, the administrator may want to
# allow the mapping of every IdP
return
@@ -206,10 +228,9 @@ def validate_idp(idp, assertion):
idp_remote_identifier = assertion[remote_id_parameter]
except KeyError:
msg = _('Could not find Identity Provider identifier in '
- 'environment, check [federation] remote_id_attribute '
- 'for details.')
+ 'environment')
raise exception.ValidationError(msg)
- if idp_remote_identifier != idp['remote_id']:
+ if idp_remote_identifier not in idp['remote_ids']:
msg = _('Incoming identity provider identifier not included '
'among the accepted identifiers.')
raise exception.Forbidden(msg)
@@ -265,7 +286,7 @@ def validate_groups(group_ids, mapping_id, identity_api):
# TODO(marek-denis): Optimize this function, so the number of calls to the
# backend are minimized.
def transform_to_group_ids(group_names, mapping_id,
- identity_api, assignment_api):
+ identity_api, resource_api):
"""Transform groups identitified by name/domain to their ids
Function accepts list of groups identified by a name and domain giving
@@ -296,7 +317,7 @@ def transform_to_group_ids(group_names, mapping_id,
:type mapping_id: str
:param identity_api: identity_api object
- :param assignment_api: assignment_api object
+ :param resource_api: resource manager object
:returns: generator object with group ids
@@ -317,7 +338,7 @@ def transform_to_group_ids(group_names, mapping_id,
"""
domain_id = (domain.get('id') or
- assignment_api.get_domain_by_name(
+ resource_api.get_domain_by_name(
domain.get('name')).get('id'))
return domain_id
@@ -334,7 +355,7 @@ def transform_to_group_ids(group_names, mapping_id,
def get_assertion_params_from_env(context):
LOG.debug('Environment variables: %s', context['environment'])
prefix = CONF.federation.assertion_prefix
- for k, v in context['environment'].items():
+ for k, v in list(context['environment'].items()):
if k.startswith(prefix):
yield (k, v)
@@ -487,8 +508,8 @@ class RuleProcessor(object):
"""
def extract_groups(groups_by_domain):
- for groups in groups_by_domain.values():
- for group in {g['name']: g for g in groups}.values():
+ for groups in list(groups_by_domain.values()):
+ for group in list({g['name']: g for g in groups}.values()):
yield group
def normalize_user(user):
@@ -506,8 +527,7 @@ class RuleProcessor(object):
if user_type == UserType.EPHEMERAL:
user['domain'] = {
- 'id': (CONF.federation.federated_domain_name or
- federation.FEDERATED_DOMAIN_KEYWORD)
+ 'id': CONF.federation.federated_domain_name
}
# initialize the group_ids as a set to eliminate duplicates
@@ -586,7 +606,7 @@ class RuleProcessor(object):
LOG.debug('direct_maps: %s', direct_maps)
LOG.debug('local: %s', local)
new = {}
- for k, v in six.iteritems(local):
+ for k, v in local.items():
if isinstance(v, dict):
new_value = self._update_local_mapping(v, direct_maps)
else:
@@ -644,7 +664,7 @@ class RuleProcessor(object):
}
:returns: identity values used to update local
- :rtype: keystone.contrib.federation.utils.DirectMaps
+ :rtype: keystone.contrib.federation.utils.DirectMaps or None
"""
@@ -686,10 +706,10 @@ class RuleProcessor(object):
# If a blacklist or whitelist is used, we want to map to the
# whole list instead of just its values separately.
- if blacklisted_values:
+ if blacklisted_values is not None:
direct_map_values = [v for v in direct_map_values
if v not in blacklisted_values]
- elif whitelisted_values:
+ elif whitelisted_values is not None:
direct_map_values = [v for v in direct_map_values
if v in whitelisted_values]
diff --git a/keystone-moon/keystone/contrib/moon/algorithms.py b/keystone-moon/keystone/contrib/moon/algorithms.py
index 8644e02d..30305fc1 100644
--- a/keystone-moon/keystone/contrib/moon/algorithms.py
+++ b/keystone-moon/keystone/contrib/moon/algorithms.py
@@ -22,18 +22,19 @@ sub_meta_rule_dict = {
}
rule_dict = [
- ["high", "vm_admin", "medium"],
- ["high", "vm_admin", "low"],
- ["medium", "vm_admin", "low"],
- ["high", "vm_access", "high"],
- ["high", "vm_access", "medium"],
- ["high", "vm_access", "low"],
- ["medium", "vm_access", "medium"],
- ["medium", "vm_access", "low"],
- ["low", "vm_access", "low"]
+ ["high", "vm_admin", "medium", True],
+ ["high", "vm_admin", "low", True],
+ ["medium", "vm_admin", "low", True],
+ ["high", "vm_access", "high", True],
+ ["high", "vm_access", "medium", True],
+ ["high", "vm_access", "low", True],
+ ["medium", "vm_access", "medium", True],
+ ["medium", "vm_access", "low", True],
+ ["low", "vm_access", "low", True]
]
"""
+
def inclusion(authz_buffer, sub_meta_rule_dict, rule_list):
_cat = []
for subject_cat in sub_meta_rule_dict['subject_categories']:
@@ -46,14 +47,10 @@ def inclusion(authz_buffer, sub_meta_rule_dict, rule_list):
if object_cat in authz_buffer['object_assignments']:
_cat.append(authz_buffer['object_assignments'][object_cat])
- print("authz_buffer", authz_buffer)
- print("rule_list", rule_list)
- print("_cat", _cat)
for _element in itertools.product(*_cat):
# Add the boolean at the end
_element = list(_element)
_element.append(True)
- print("_element", _element)
if _element in rule_list:
return True
@@ -66,6 +63,13 @@ def comparison(authz_buffer, sub_meta_rule_dict, rule_list):
def all_true(decision_buffer):
for _rule in decision_buffer:
- if decision_buffer[_rule] is False:
+ if decision_buffer[_rule] == False:
return False
- return True \ No newline at end of file
+ return True
+
+
+def one_true(decision_buffer):
+ for _rule in decision_buffer:
+ if decision_buffer[_rule] == True:
+ return True
+ return False
diff --git a/keystone-moon/keystone/contrib/moon/backends/memory.py b/keystone-moon/keystone/contrib/moon/backends/memory.py
index 675240e5..7a996847 100644
--- a/keystone-moon/keystone/contrib/moon/backends/memory.py
+++ b/keystone-moon/keystone/contrib/moon/backends/memory.py
@@ -6,6 +6,7 @@
from uuid import uuid4
from glob import glob
import os
+import json
from keystone import config
from keystone.contrib.moon.core import ConfigurationDriver
@@ -19,12 +20,12 @@ class ConfigurationConnector(ConfigurationDriver):
super(ConfigurationConnector, self).__init__()
self.aggregation_algorithms_dict = dict()
self.aggregation_algorithms_dict[uuid4().hex] = {'name': 'all_true', 'description': 'all_true'}
+ self.aggregation_algorithms_dict[uuid4().hex] = {'name': 'one_true', 'description': 'one_true'}
self.sub_meta_rule_algorithms_dict = dict()
self.sub_meta_rule_algorithms_dict[uuid4().hex] = {'name': 'inclusion', 'description': 'inclusion'}
self.sub_meta_rule_algorithms_dict[uuid4().hex] = {'name': 'comparison', 'description': 'comparison'}
def get_policy_templates_dict(self):
- # TODO (dthom): this function should return a dictionary of all policy templates as:
"""
:return: {
template_id1: {name: template_name, description: template_description},
@@ -33,11 +34,15 @@ class ConfigurationConnector(ConfigurationDriver):
}
"""
nodes = glob(os.path.join(CONF.moon.policy_directory, "*"))
- return {
- "authz_templates": [os.path.basename(n) for n in nodes if os.path.isdir(n)]
- }
-
- def get_aggregation_algorithm_dict(self):
+ templates = dict()
+ for node in nodes:
+ templates[os.path.basename(node)] = dict()
+ metadata = json.load(open(os.path.join(node, "metadata.json")))
+ templates[os.path.basename(node)]["name"] = metadata["name"]
+ templates[os.path.basename(node)]["description"] = metadata["description"]
+ return templates
+
+ def get_aggregation_algorithms_dict(self):
return self.aggregation_algorithms_dict
def get_sub_meta_rule_algorithms_dict(self):
diff --git a/keystone-moon/keystone/contrib/moon/backends/sql.py b/keystone-moon/keystone/contrib/moon/backends/sql.py
index 7a75af39..cb64c1f7 100644
--- a/keystone-moon/keystone/contrib/moon/backends/sql.py
+++ b/keystone-moon/keystone/contrib/moon/backends/sql.py
@@ -887,7 +887,7 @@ class IntraExtensionConnector(IntraExtensionDriver):
def set_aggregation_algorithm_dict(self, intra_extension_id, aggregation_algorithm_id, aggregation_algorithm_dict):
with sql.transaction() as session:
query = session.query(AggregationAlgorithm)
- query = query.filter_by(intra_extension_id=intra_extension_id, id=aggregation_algorithm_id)
+ query = query.filter_by(intra_extension_id=intra_extension_id)
ref = query.first()
new_ref = AggregationAlgorithm.from_dict(
{
@@ -896,21 +896,18 @@ class IntraExtensionConnector(IntraExtensionDriver):
'intra_extension_id': intra_extension_id
}
)
- if not ref:
- session.add(new_ref)
- else:
- for attr in AggregationAlgorithm.attributes:
- if attr != 'id':
- setattr(ref, attr, getattr(new_ref, attr))
+ if ref:
+ session.delete(ref)
+ session.add(new_ref)
session.flush()
return self.get_aggregation_algorithm_dict(intra_extension_id)
- # def del_aggregation_algorithm(self, intra_extension_id, aggregation_algorithm_id):
- # with sql.transaction() as session:
- # query = session.query(AggregationAlgorithm)
- # query = query.filter_by(intra_extension_id=intra_extension_id, id=aggregation_algorithm_id)
- # ref = query.first()
- # session.delete(ref)
+ def del_aggregation_algorithm(self, intra_extension_id, aggregation_algorithm_id):
+ with sql.transaction() as session:
+ query = session.query(AggregationAlgorithm)
+ query = query.filter_by(intra_extension_id=intra_extension_id, id=aggregation_algorithm_id)
+ ref = query.first()
+ session.delete(ref)
# Getter and Setter for sub_meta_rule
diff --git a/keystone-moon/keystone/contrib/moon/core.py b/keystone-moon/keystone/contrib/moon/core.py
index d82c9fcc..4a68cdaa 100644
--- a/keystone-moon/keystone/contrib/moon/core.py
+++ b/keystone-moon/keystone/contrib/moon/core.py
@@ -25,9 +25,9 @@ from keystone.contrib.moon.algorithms import *
CONF = config.CONF
LOG = log.getLogger(__name__)
-ADMIN_ID = None # default user_id for internal invocation
-ROOT_EXTENSION_ID = None
-ROOT_EXTENSION_MODEL = "policy_root"
+# ADMIN_ID = None # default user_id for internal invocation
+# ROOT_EXTENSION_ID = None
+# ROOT_EXTENSION_MODEL = "policy_root"
_OPTS = [
@@ -52,9 +52,9 @@ _OPTS = [
cfg.StrOpt('policy_directory',
default='/etc/keystone/policies',
help='Local directory where all policies are stored.'),
- cfg.StrOpt('super_extension_directory',
- default='/etc/keystone/super_extension',
- help='Local directory where SuperExtension configuration is stored.'),
+ cfg.StrOpt('root_policy_directory',
+ default='policy_root',
+ help='Local directory where Root IntraExtension configuration is stored.'),
]
CONF.register_opts(_OPTS, group='moon')
@@ -108,29 +108,29 @@ def enforce(action_names, object_name, **extra):
_action_name_list = action_names
_object_name = object_name
- def get_root_extension(self, args, kwargs):
- if not ROOT_EXTENSION_ID:
- global ROOT_EXTENSION_MODEL, ROOT_EXTENSION_ID, ADMIN_ID
- try:
- # if it is the first time we passed here, the root extension may be not initialized
- # specially during unittest. So we raise RootExtensionNotInitialized to authorize the
- # current creation process
- if 'intra_extension_dict' in kwargs:
- intra_extension_dict = kwargs['intra_extension_dict']
- else:
- intra_extension_dict = args[2]
- if isinstance(intra_extension_dict, dict) and \
- "model" in intra_extension_dict and \
- intra_extension_dict["model"] == "policy_root":
- raise RootExtensionNotInitialized()
- except KeyError:
- pass
- return ROOT_EXTENSION_ID
+ # def get_root_extension(self, args, kwargs):
+ # if not ROOT_EXTENSION_ID:
+ # global ROOT_EXTENSION_MODEL, ROOT_EXTENSION_ID, ADMIN_ID
+ # try:
+ # # if it is the first time we passed here, the root extension may be not initialized
+ # # specially during unittest. So we raise RootExtensionNotInitialized to authorize the
+ # # current creation process
+ # if 'intra_extension_dict' in kwargs:
+ # intra_extension_dict = kwargs['intra_extension_dict']
+ # else:
+ # intra_extension_dict = args[2]
+ # if isinstance(intra_extension_dict, dict) and \
+ # "model" in intra_extension_dict and \
+ # intra_extension_dict["model"] == "policy_root":
+ # raise RootExtensionNotInitialized()
+ # except KeyError:
+ # pass
+ # return ROOT_EXTENSION_ID
def wrap(func):
def wrapped(*args, **kwargs):
- global ADMIN_ID, ROOT_EXTENSION_ID
+ # global ADMIN_ID, ROOT_EXTENSION_ID
returned_value_for_func = None
self = args[0]
try:
@@ -140,46 +140,42 @@ def enforce(action_names, object_name, **extra):
intra_extension_id = None
intra_admin_extension_id = None
- try:
- intra_root_extension_id = get_root_extension(self, args, kwargs)
- # FIXME (asteroide): intra_root_extension_id is not used at all...
- except RootExtensionNotInitialized:
- # Root extension is not initialized, the current requested function must be the creation
- # of this root extension
- returned_value_for_func = func(*args, **kwargs)
- # after the creation, we must update ROOT_EXTENSION_ID and ADMIN_ID
- intra_extensions_dict = self.admin_api.driver.get_intra_extensions_dict()
- for ext in intra_extensions_dict:
- if intra_extensions_dict[ext]["model"] == ROOT_EXTENSION_MODEL:
- ROOT_EXTENSION_ID = ext
- break
- if not ROOT_EXTENSION_ID:
- raise RootExtensionUnknown()
- subjects_dict = self.admin_api.driver.get_subjects_dict(returned_value_for_func['id'])
- for subject_id in subjects_dict:
- if subjects_dict[subject_id]["name"] == "admin":
- ADMIN_ID = subject_id
- break
- if not ADMIN_ID:
- raise RootExtensionUnknown()
- # if all is OK, return values from func (creation of the root extension)
- return returned_value_for_func
+ # try:
+ intra_root_extension_id = self.root_api.get_root_extension_id()
+ # except RootExtensionNotInitialized:
+ # # Root extension is not initialized, the current requested function must be the creation
+ # # of this root extension
+ # returned_value_for_func = func(*args, **kwargs)
+ # # after the creation, we must update ROOT_EXTENSION_ID and ADMIN_ID
+ # intra_extensions_dict = self.admin_api.driver.get_intra_extensions_dict()
+ # for ext in intra_extensions_dict:
+ # if intra_extensions_dict[ext]["model"] == ROOT_EXTENSION_MODEL:
+ # ROOT_EXTENSION_ID = ext
+ # break
+ # if not ROOT_EXTENSION_ID:
+ # raise RootExtensionUnknown()
+ # subjects_dict = self.admin_api.driver.get_subjects_dict(returned_value_for_func['id'])
+ # for subject_id in subjects_dict:
+ # if subjects_dict[subject_id]["name"] == "admin":
+ # ADMIN_ID = subject_id
+ # break
+ # if not ADMIN_ID:
+ # raise RootExtensionUnknown()
+ # # if all is OK, return values from func (creation of the root extension)
+ # return returned_value_for_func
try:
intra_extension_id = args[2]
except IndexError:
- print("IndexError", kwargs)
if 'intra_extension_id' in kwargs:
intra_extension_id = kwargs['intra_extension_id']
else:
- print("in else", intra_root_extension_id)
intra_extension_id = intra_root_extension_id
- if ADMIN_ID and user_id == ADMIN_ID:
+ if user_id == self.root_api.get_root_admin_id():
# TODO: check if there is no security hole here
returned_value_for_func = func(*args, **kwargs)
else:
intra_extensions_dict = self.admin_api.driver.get_intra_extensions_dict()
- print(intra_extension_id, intra_extensions_dict)
if intra_extension_id not in intra_extensions_dict:
raise IntraExtensionUnknown()
tenants_dict = self.tenant_api.driver.get_tenants_dict()
@@ -213,7 +209,10 @@ def enforce(action_names, object_name, **extra):
# if we found the object in intra_root_extension_id, so we change the intra_admin_extension_id
# into intra_root_extension_id and we modify the ID of the subject
subjects_dict = self.admin_api.driver.get_subjects_dict(intra_admin_extension_id)
- subject_name = subjects_dict[user_id]["name"]
+ try:
+ subject_name = subjects_dict[user_id]["name"]
+ except KeyError:
+ raise SubjectUnknown()
intra_admin_extension_id = intra_root_extension_id
subjects_dict = self.admin_api.driver.get_subjects_dict(intra_admin_extension_id)
user_id = None
@@ -221,7 +220,7 @@ def enforce(action_names, object_name, **extra):
if subjects_dict[_subject_id]["name"] == subject_name:
user_id = _subject_id
if not user_id:
- raise SubjectUnknown("Subject Unknown for Root intraExtension...")
+ raise SubjectUnknown("Subject {} Unknown for Root IntraExtension...".format(subject_name))
if type(_action_name_list) in (str, unicode):
action_name_list = (_action_name_list, )
else:
@@ -256,9 +255,11 @@ def enforce(action_names, object_name, **extra):
@dependency.provider('configuration_api')
-@dependency.requires('moonlog_api', 'admin_api', 'tenant_api')
+@dependency.requires('moonlog_api', 'admin_api', 'tenant_api', 'root_api')
class ConfigurationManager(manager.Manager):
+ driver_namespace = 'keystone.moon.configuration'
+
def __init__(self):
super(ConfigurationManager, self).__init__(CONF.moon.configuration_driver)
@@ -278,7 +279,7 @@ class ConfigurationManager(manager.Manager):
def get_policy_template_id_from_name(self, user_id, policy_template_name):
policy_templates_dict = self.driver.get_policy_templates_dict()
for policy_template_id in policy_templates_dict:
- if policy_templates_dict[policy_template_id]['name'] is policy_template_name:
+ if policy_templates_dict[policy_template_id]['name'] == policy_template_name:
return policy_template_id
return None
@@ -298,7 +299,7 @@ class ConfigurationManager(manager.Manager):
def get_aggregation_algorithm_id_from_name(self, user_id, aggregation_algorithm_name):
aggregation_algorithms_dict = self.driver.get_aggregation_algorithms_dict()
for aggregation_algorithm_id in aggregation_algorithms_dict:
- if aggregation_algorithms_dict[aggregation_algorithm_id]['name'] is aggregation_algorithm_name:
+ if aggregation_algorithms_dict[aggregation_algorithm_id]['name'] == aggregation_algorithm_name:
return aggregation_algorithm_id
return None
@@ -318,15 +319,17 @@ class ConfigurationManager(manager.Manager):
def get_sub_meta_rule_algorithm_id_from_name(self, sub_meta_rule_algorithm_name):
sub_meta_rule_algorithms_dict = self.driver.get_sub_meta_rule_algorithms_dict()
for sub_meta_rule_algorithm_id in sub_meta_rule_algorithms_dict:
- if sub_meta_rule_algorithms_dict[sub_meta_rule_algorithm_id]['name'] is sub_meta_rule_algorithm_name:
+ if sub_meta_rule_algorithms_dict[sub_meta_rule_algorithm_id]['name'] == sub_meta_rule_algorithm_name:
return sub_meta_rule_algorithm_id
return None
@dependency.provider('tenant_api')
-@dependency.requires('moonlog_api', 'admin_api', 'configuration_api')
+@dependency.requires('moonlog_api', 'admin_api', 'configuration_api', 'root_api', 'resource_api')
class TenantManager(manager.Manager):
+ driver_namespace = 'keystone.moon.tenant'
+
def __init__(self):
super(TenantManager, self).__init__(CONF.moon.tenant_driver)
@@ -348,38 +351,66 @@ class TenantManager(manager.Manager):
"""
return self.driver.get_tenants_dict()
+ def __get_keystone_tenant_dict(self, tenant_id="", tenant_name=""):
+ tenants = self.resource_api.list_projects()
+ for tenant in tenants:
+ if tenant_id and tenant_id == tenant['id']:
+ return tenant
+ if tenant_name and tenant_name == tenant['name']:
+ return tenant
+ if not tenant_id:
+ tenant_id = uuid4().hex
+ if not tenant_name:
+ tenant_name = tenant_id
+ tenant = {
+ "id": tenant_id,
+ "name": tenant_name,
+ "description": "Auto generated tenant from Moon platform",
+ "enabled": True,
+ "domain_id": "default"
+ }
+ keystone_tenant = self.resource_api.create_project(tenant["id"], tenant)
+ return keystone_tenant
+
@filter_input
@enforce(("read", "write"), "tenants")
def add_tenant_dict(self, user_id, tenant_dict):
tenants_dict = self.driver.get_tenants_dict()
for tenant_id in tenants_dict:
- if tenants_dict[tenant_id]['name'] is tenant_dict['name']:
+ if tenants_dict[tenant_id]['name'] == tenant_dict['name']:
raise TenantAddedNameExisting()
+ # Check (and eventually sync) Keystone tenant
+ if 'id' not in tenant_dict:
+ tenant_dict['id'] = None
+ keystone_tenant = self.__get_keystone_tenant_dict(tenant_dict['id'], tenant_dict['name'])
+ tenant_dict.update(keystone_tenant)
# Sync users between intra_authz_extension and intra_admin_extension
if tenant_dict['intra_admin_extension_id']:
if not tenant_dict['intra_authz_extension_id']:
raise TenantNoIntraAuthzExtension()
- authz_subjects_dict = self.admin_api.get_subjects_dict(ADMIN_ID, tenant_dict['intra_authz_extension_id'])
- admin_subjects_dict = self.admin_api.get_subjects_dict(ADMIN_ID, tenant_dict['intra_admin_extension_id'])
- for _subject_id in authz_subjects_dict:
- if _subject_id not in admin_subjects_dict:
- self.admin_api.add_subject_dict(ADMIN_ID, tenant_dict['intra_admin_extension_id'], authz_subjects_dict[_subject_id])
- for _subject_id in admin_subjects_dict:
- if _subject_id not in authz_subjects_dict:
- self.admin_api.add_subject_dict(ADMIN_ID, tenant_dict['intra_authz_extension_id'], admin_subjects_dict[_subject_id])
-
- # TODO (dthom): check whether we can replace the below code by the above one
- # authz_subjects_dict = self.admin_api.get_subjects_dict(ADMIN_ID, tenant_dict['intra_authz_extension_id'])
- # authz_subject_names_list = [authz_subjects_dict[subject_id]["name"] for subject_id in authz_subjects_dict]
- # admin_subjects_dict = self.admin_api.get_subjects_dict(ADMIN_ID, tenant_dict['intra_admin_extension_id'])
- # admin_subject_names_list = [admin_subjects_dict[subject_id]["name"] for subject_id in admin_subjects_dict]
+ # authz_subjects_dict = self.admin_api.get_subjects_dict(self.root_api.get_root_admin_id(), tenant_dict['intra_authz_extension_id'])
+ # admin_subjects_dict = self.admin_api.get_subjects_dict(self.root_api.get_root_admin_id(), tenant_dict['intra_admin_extension_id'])
# for _subject_id in authz_subjects_dict:
- # if authz_subjects_dict[_subject_id]["name"] not in admin_subject_names_list:
- # self.admin_api.add_subject_dict(ADMIN_ID, tenant_dict['intra_admin_extension_id'], authz_subjects_dict[_subject_id])
+ # if _subject_id not in admin_subjects_dict:
+ # self.admin_api.add_subject_dict(self.root_api.get_root_admin_id(), tenant_dict['intra_admin_extension_id'], authz_subjects_dict[_subject_id])
# for _subject_id in admin_subjects_dict:
- # if admin_subjects_dict[_subject_id]["name"] not in authz_subject_names_list:
- # self.admin_api.add_subject_dict(ADMIN_ID, tenant_dict['intra_authz_extension_id'], admin_subjects_dict[_subject_id])
+ # if _subject_id not in authz_subjects_dict:
+ # self.admin_api.add_subject_dict(self.root_api.get_root_admin_id(), tenant_dict['intra_authz_extension_id'], admin_subjects_dict[_subject_id])
+
+ # TODO (ateroide): check whether we can replace the below code by the above one
+ # NOTE (ateroide): at a first glance: no, subject_id changes depending on which intra_extesion is used
+ # we must use name which is constant.
+ authz_subjects_dict = self.admin_api.get_subjects_dict(self.root_api.get_root_admin_id(), tenant_dict['intra_authz_extension_id'])
+ authz_subject_names_list = [authz_subjects_dict[subject_id]["name"] for subject_id in authz_subjects_dict]
+ admin_subjects_dict = self.admin_api.get_subjects_dict(self.root_api.get_root_admin_id(), tenant_dict['intra_admin_extension_id'])
+ admin_subject_names_list = [admin_subjects_dict[subject_id]["name"] for subject_id in admin_subjects_dict]
+ for _subject_id in authz_subjects_dict:
+ if authz_subjects_dict[_subject_id]["name"] not in admin_subject_names_list:
+ self.admin_api.add_subject_dict(self.root_api.get_root_admin_id(), tenant_dict['intra_admin_extension_id'], authz_subjects_dict[_subject_id])
+ for _subject_id in admin_subjects_dict:
+ if admin_subjects_dict[_subject_id]["name"] not in authz_subject_names_list:
+ self.admin_api.add_subject_dict(self.root_api.get_root_admin_id(), tenant_dict['intra_authz_extension_id'], admin_subjects_dict[_subject_id])
return self.driver.add_tenant_dict(tenant_dict['id'], tenant_dict)
@@ -409,52 +440,24 @@ class TenantManager(manager.Manager):
if tenant_dict['intra_admin_extension_id']:
if not tenant_dict['intra_authz_extension_id']:
raise TenantNoIntraAuthzExtension
- authz_subjects_dict = self.admin_api.get_subjects_dict(ADMIN_ID, tenant_dict['intra_authz_extension_id'])
- admin_subjects_dict = self.admin_api.get_subjects_dict(ADMIN_ID, tenant_dict['intra_admin_extension_id'])
+ authz_subjects_dict = self.admin_api.get_subjects_dict(self.root_api.get_root_admin_id(), tenant_dict['intra_authz_extension_id'])
+ authz_subject_names_list = [authz_subjects_dict[subject_id]["name"] for subject_id in authz_subjects_dict]
+ admin_subjects_dict = self.admin_api.get_subjects_dict(self.root_api.get_root_admin_id(), tenant_dict['intra_admin_extension_id'])
+ admin_subject_names_list = [admin_subjects_dict[subject_id]["name"] for subject_id in admin_subjects_dict]
for _subject_id in authz_subjects_dict:
- if _subject_id not in admin_subjects_dict:
- self.admin_api.add_subject_dict(ADMIN_ID, tenant_dict['intra_admin_extension_id'], authz_subjects_dict[_subject_id])
+ if authz_subjects_dict[_subject_id]["name"] not in admin_subject_names_list:
+ self.admin_api.add_subject_dict(self.root_api.get_root_admin_id(), tenant_dict['intra_admin_extension_id'], authz_subjects_dict[_subject_id])
for _subject_id in admin_subjects_dict:
- if _subject_id not in authz_subjects_dict:
- self.admin_api.add_subject_dict(ADMIN_ID, tenant_dict['intra_authz_extension_id'], admin_subjects_dict[_subject_id])
+ if admin_subjects_dict[_subject_id]["name"] not in authz_subject_names_list:
+ self.admin_api.add_subject_dict(self.root_api.get_root_admin_id(), tenant_dict['intra_authz_extension_id'], admin_subjects_dict[_subject_id])
return self.driver.set_tenant_dict(tenant_id, tenant_dict)
- # TODO (dthom): move the following 2 functions to perimeter functions
- @filter_input
- def get_subject_dict_from_keystone_id(self, tenant_id, intra_extension_id, keystone_id):
- tenants_dict = self.driver.get_tenants_dict()
- if tenant_id not in tenants_dict:
- raise TenantUnknown()
- if intra_extension_id not in (tenants_dict[tenant_id]['intra_authz_extension_id'],
- tenants_dict[tenant_id]['intra_admin_extension_id'], ):
- raise IntraExtensionUnknown()
- # Note (asteroide): We used ADMIN_ID because the user requesting this information may only know his keystone_id
- # and not the subject ID in the requested intra_extension.
- subjects_dict = self.admin_api.get_subjects_dict(ADMIN_ID, intra_extension_id)
- for subject_id in subjects_dict:
- if keystone_id is subjects_dict[subject_id]['keystone_id']:
- return {subject_id: subjects_dict[subject_id]}
-
- @filter_input
- def get_subject_dict_from_keystone_name(self, tenant_id, intra_extension_id, keystone_name):
- tenants_dict = self.driver.get_tenants_dict()
- if tenant_id not in tenants_dict:
- raise TenantUnknown()
- if intra_extension_id not in (tenants_dict[tenant_id]['intra_authz_extension_id'],
- tenants_dict[tenant_id]['intra_admin_extension_id'], ):
- raise IntraExtensionUnknown()
- # Note (asteroide): We used ADMIN_ID because the user requesting this information may only know his
- # keystone_name and not the subject ID in the requested intra_extension.
- subjects_dict = self.admin_api.get_subjects_dict(ADMIN_ID, intra_extension_id)
- for subject_id in subjects_dict:
- if keystone_name is subjects_dict[subject_id]['keystone_name']:
- return {subject_id: subjects_dict[subject_id]}
-
-
-@dependency.requires('identity_api', 'tenant_api', 'configuration_api', 'authz_api', 'admin_api', 'moonlog_api')
+@dependency.requires('identity_api', 'tenant_api', 'configuration_api', 'authz_api', 'admin_api', 'moonlog_api', 'root_api')
class IntraExtensionManager(manager.Manager):
+ driver_namespace = 'keystone.moon.intraextension'
+
def __init__(self):
driver = CONF.moon.intraextension_driver
super(IntraExtensionManager, self).__init__(driver)
@@ -501,6 +504,7 @@ class IntraExtensionManager(manager.Manager):
authz_buffer['subject_assignments'] = dict()
authz_buffer['object_assignments'] = dict()
authz_buffer['action_assignments'] = dict()
+
for _subject_category in meta_data_dict['subject_categories']:
authz_buffer['subject_assignments'][_subject_category] = list(subject_assignment_dict[_subject_category])
for _object_category in meta_data_dict['object_categories']:
@@ -543,6 +547,8 @@ class IntraExtensionManager(manager.Manager):
aggregation_algorithm_id = aggregation_algorithm_dict.keys()[0]
if aggregation_algorithm_dict[aggregation_algorithm_id]['name'] == 'all_true':
decision = all_true(decision_buffer)
+ elif aggregation_algorithm_dict[aggregation_algorithm_id]['name'] == 'one_true':
+ decision = one_true(decision_buffer)
if not decision:
raise AuthzException("{} {}-{}-{}".format(intra_extension_id, subject_id, action_id, object_id))
return decision
@@ -607,7 +613,12 @@ class IntraExtensionManager(manager.Manager):
subject_dict = dict()
# We suppose that all subjects can be mapped to a true user in Keystone
for _subject in json_perimeter['subjects']:
- keystone_user = self.identity_api.get_user_by_name(_subject, "default")
+ try:
+ keystone_user = self.identity_api.get_user_by_name(_subject, "default")
+ except exception.UserNotFound:
+ # TODO (asteroide): must add a configuration option to allow that exception
+ # maybe a debug option for unittest
+ keystone_user = {'id': "", 'name': _subject}
subject_id = uuid4().hex
subject_dict[subject_id] = keystone_user
subject_dict[subject_id]['keystone_id'] = keystone_user["id"]
@@ -774,8 +785,6 @@ class IntraExtensionManager(manager.Manager):
sub_rule_id = self.driver.get_uuid_from_name(intra_extension_dict["id"],
sub_rule_name,
self.driver.SUB_META_RULE)
- # print(sub_rule_name)
- # print(self.get_sub_meta_rule_relations("admin", ie["id"]))
# if sub_rule_name not in self.get_sub_meta_rule_relations("admin", ie["id"])["sub_meta_rule_relations"]:
# raise IntraExtensionException("Bad sub_rule_name name {} in rules".format(sub_rule_name))
rules[sub_rule_id] = list()
@@ -833,6 +842,32 @@ class IntraExtensionManager(manager.Manager):
self.__load_rule_file(ie_dict, template_dir)
return ref
+ def load_root_intra_extension_dict(self, policy_template):
+ # Note (asteroide): Only one root Extension is authorized
+ # and this extension is created at the very beginning of the server
+ # so we don't need to use enforce here
+ for key in self.driver.get_intra_extensions_dict():
+ # Note (asteroide): if there is at least one Intra Extension, it implies that
+ # the Root Intra Extension had already been created...
+ return
+ ie_dict = dict()
+ ie_dict['id'] = uuid4().hex
+ ie_dict["name"] = "policy_root"
+ ie_dict["model"] = filter_input(policy_template)
+ ie_dict["genre"] = "admin"
+ ie_dict["description"] = "policy_root"
+ ref = self.driver.set_intra_extension_dict(ie_dict['id'], ie_dict)
+ self.moonlog_api.debug("Creation of IE: {}".format(ref))
+ # read the template given by "model" and populate default variables
+ template_dir = os.path.join(CONF.moon.policy_directory, ie_dict["model"])
+ self.__load_metadata_file(ie_dict, template_dir)
+ self.__load_perimeter_file(ie_dict, template_dir)
+ self.__load_scope_file(ie_dict, template_dir)
+ self.__load_assignment_file(ie_dict, template_dir)
+ self.__load_metarule_file(ie_dict, template_dir)
+ self.__load_rule_file(ie_dict, template_dir)
+ return ref
+
@enforce("read", "intra_extensions")
def get_intra_extension_dict(self, user_id, intra_extension_id):
"""
@@ -858,7 +893,7 @@ class IntraExtensionManager(manager.Manager):
for rule_id in self.driver.get_rules_dict(intra_extension_id, sub_meta_rule_id):
self.driver.del_rule(intra_extension_id, sub_meta_rule_id, rule_id)
self.driver.del_sub_meta_rule(intra_extension_id, sub_meta_rule_id)
- for aggregation_algorithm_id in self.driver.get_aggregation_algorithms_dict(intra_extension_id):
+ for aggregation_algorithm_id in self.driver.get_aggregation_algorithm_dict(intra_extension_id):
self.driver.del_aggregation_algorithm(intra_extension_id, aggregation_algorithm_id)
for subject_id in self.driver.get_subjects_dict(intra_extension_id):
self.driver.del_subject(intra_extension_id, subject_id)
@@ -1049,6 +1084,7 @@ class IntraExtensionManager(manager.Manager):
def add_subject_dict(self, user_id, intra_extension_id, subject_dict):
subjects_dict = self.driver.get_subjects_dict(intra_extension_id)
for subject_id in subjects_dict:
+ print(subjects_dict[subject_id]["name"], subject_dict['name'])
if subjects_dict[subject_id]["name"] == subject_dict['name']:
raise SubjectNameExisting()
# Next line will raise an error if user is not present in Keystone database
@@ -1091,6 +1127,37 @@ class IntraExtensionManager(manager.Manager):
return self.driver.set_subject_dict(intra_extension_id, subject_dict["id"], subject_dict)
@filter_input
+ def get_subject_dict_from_keystone_id(self, tenant_id, intra_extension_id, keystone_id):
+ tenants_dict = self.tenant_api.driver.get_tenants_dict()
+ if tenant_id not in tenants_dict:
+ raise TenantUnknown()
+ if intra_extension_id not in (tenants_dict[tenant_id]['intra_authz_extension_id'],
+ tenants_dict[tenant_id]['intra_admin_extension_id'], ):
+ raise IntraExtensionUnknown()
+ # Note (asteroide): We used self.root_api.get_root_admin_id() because the user requesting this information
+ # may only know his keystone_id and not the subject ID in the requested intra_extension.
+ subjects_dict = self.get_subjects_dict(self.root_api.get_root_admin_id(), intra_extension_id)
+ for subject_id in subjects_dict:
+ if keystone_id == subjects_dict[subject_id]['keystone_id']:
+ return {subject_id: subjects_dict[subject_id]}
+
+ @filter_input
+ def get_subject_dict_from_keystone_name(self, tenant_id, intra_extension_id, keystone_name):
+ tenants_dict = self.tenant_api.driver.get_tenants_dict()
+ if tenant_id not in tenants_dict:
+ raise TenantUnknown()
+ if intra_extension_id not in (tenants_dict[tenant_id]['intra_authz_extension_id'],
+ tenants_dict[tenant_id]['intra_admin_extension_id'], ):
+ raise IntraExtensionUnknown()
+ # Note (asteroide): We used self.root_api.get_root_admin_id() because the user requesting this information
+ # may only know his keystone_name and not the subject ID in the requested intra_extension.
+ subjects_dict = self.get_subjects_dict(self.root_api.get_root_admin_id(), intra_extension_id)
+ for subject_id in subjects_dict:
+ if keystone_name == subjects_dict[subject_id]['keystone_name']:
+ return {subject_id: subjects_dict[subject_id]}
+
+
+ @filter_input
@enforce("read", "objects")
def get_objects_dict(self, user_id, intra_extension_id):
return self.driver.get_objects_dict(intra_extension_id)
@@ -1539,7 +1606,7 @@ class IntraExtensionManager(manager.Manager):
@enforce(("read", "write"), "aggregation_algorithm")
def set_aggregation_algorithm_dict(self, user_id, intra_extension_id, aggregation_algorithm_id, aggregation_algorithm_dict):
if aggregation_algorithm_id:
- if aggregation_algorithm_id not in self.configuration_api.get_aggregation_algorithms_dict(ADMIN_ID):
+ if aggregation_algorithm_id not in self.configuration_api.get_aggregation_algorithms_dict(self.root_api.get_root_admin_id()):
raise AggregationAlgorithmUnknown()
else:
aggregation_algorithm_id = uuid4().hex
@@ -1577,7 +1644,9 @@ class IntraExtensionManager(manager.Manager):
sub_meta_rule_dict['action_categories'] == sub_meta_rules_dict[_sub_meta_rule_id]["action_categories"] and \
sub_meta_rule_dict['algorithm'] == sub_meta_rules_dict[_sub_meta_rule_id]["algorithm"]:
raise SubMetaRuleExisting()
- if sub_meta_rule_dict['algorithm'] not in self.configuration_api.get_sub_meta_rule_algorithms_dict(user_id):
+ algorithm_names = map(lambda x: x['name'],
+ self.configuration_api.get_sub_meta_rule_algorithms_dict(user_id).values())
+ if sub_meta_rule_dict['algorithm'] not in algorithm_names:
raise SubMetaRuleAlgorithmNotExisting()
sub_meta_rule_id = uuid4().hex
# TODO (dthom): add new sub-meta-rule to rule dict
@@ -1682,10 +1751,10 @@ class IntraExtensionAuthzManager(IntraExtensionManager):
elif genre == "admin":
genre = "intra_admin_extension_id"
- tenants_dict = self.tenant_api.get_tenants_dict(ADMIN_ID)
+ tenants_dict = self.tenant_api.get_tenants_dict(self.root_api.get_root_admin_id())
tenant_id = None
for _tenant_id in tenants_dict:
- if tenants_dict[_tenant_id]["name"] is tenant_name:
+ if tenants_dict[_tenant_id]["name"] == tenant_name:
tenant_id = _tenant_id
break
if not tenant_id:
@@ -1697,8 +1766,9 @@ class IntraExtensionAuthzManager(IntraExtensionManager):
subjects_dict = self.driver.get_subjects_dict(intra_extension_id)
subject_id = None
for _subject_id in subjects_dict:
- if subjects_dict[_subject_id]['keystone_name'] is subject_name:
- subject_id = subjects_dict[_subject_id]['keystone_id']
+ if subjects_dict[_subject_id]['keystone_name'] == subject_name:
+ # subject_id = subjects_dict[_subject_id]['keystone_id']
+ subject_id = _subject_id
break
if not subject_id:
raise SubjectUnknown()
@@ -1725,7 +1795,7 @@ class IntraExtensionAuthzManager(IntraExtensionManager):
def add_subject_dict(self, user_id, intra_extension_id, subject_dict):
subject = super(IntraExtensionAuthzManager, self).add_subject_dict(user_id, intra_extension_id, subject_dict)
subject_id, subject_value = subject.iteritems().next()
- tenants_dict = self.tenant_api.get_tenants_dict(ADMIN_ID)
+ tenants_dict = self.tenant_api.get_tenants_dict(self.root_api.get_root_admin_id())
for tenant_id in tenants_dict:
if tenants_dict[tenant_id]["intra_authz_extension_id"] == intra_extension_id:
_subjects = self.driver.get_subjects_dict(tenants_dict[tenant_id]["intra_admin_extension_id"])
@@ -1742,7 +1812,7 @@ class IntraExtensionAuthzManager(IntraExtensionManager):
def del_subject(self, user_id, intra_extension_id, subject_id):
subject_name = self.driver.get_subjects_dict(intra_extension_id)[subject_id]["name"]
super(IntraExtensionAuthzManager, self).del_subject(user_id, intra_extension_id, subject_id)
- tenants_dict = self.tenant_api.get_tenants_dict(ADMIN_ID)
+ tenants_dict = self.tenant_api.get_tenants_dict(self.root_api.get_root_admin_id())
for tenant_id in tenants_dict:
if tenants_dict[tenant_id]["intra_authz_extension_id"] == intra_extension_id:
subject_id = self.driver.get_uuid_from_name(tenants_dict[tenant_id]["intra_admin_extension_id"],
@@ -1760,7 +1830,7 @@ class IntraExtensionAuthzManager(IntraExtensionManager):
def set_subject_dict(self, user_id, intra_extension_id, subject_id, subject_dict):
subject = super(IntraExtensionAuthzManager, self).set_subject_dict(user_id, intra_extension_id, subject_dict)
subject_id, subject_value = subject.iteritems().next()
- tenants_dict = self.tenant_api.get_tenants_dict(ADMIN_ID)
+ tenants_dict = self.tenant_api.get_tenants_dict(self.root_api.get_root_admin_id())
for tenant_id in tenants_dict:
if tenants_dict[tenant_id]["intra_authz_extension_id"] == intra_extension_id:
self.driver.set_subject_dict(tenants_dict[tenant_id]["intra_admin_extension_id"], uuid4().hex, subject_value)
@@ -1770,110 +1840,110 @@ class IntraExtensionAuthzManager(IntraExtensionManager):
break
return subject
- # def add_subject_category(self, user_id, intra_extension_id, subject_category_dict):
- # raise AuthzException()
- #
- # def del_subject_category(self, user_id, intra_extension_id, subject_category_id):
- # raise AuthzException()
- #
- # def set_subject_category(self, user_id, intra_extension_id, subject_category_id, subject_category_dict):
- # raise AuthzException()
- #
- # def add_object_category(self, user_id, intra_extension_id, object_category_dict):
- # raise AuthzException()
- #
- # def del_object_category(self, user_id, intra_extension_id, object_category_id):
- # raise AuthzException()
- #
- # def add_action_category(self, user_id, intra_extension_id, action_category_name):
- # raise AuthzException()
- #
- # def del_action_category(self, user_id, intra_extension_id, action_category_id):
- # raise AuthzException()
- #
- # def add_object_dict(self, user_id, intra_extension_id, object_name):
- # raise AuthzException()
- #
- # def set_object_dict(self, user_id, intra_extension_id, object_id, object_dict):
- # raise AuthzException()
- #
- # def del_object(self, user_id, intra_extension_id, object_id):
- # raise AuthzException()
- #
- # def add_action_dict(self, user_id, intra_extension_id, action_name):
- # raise AuthzException()
- #
- # def set_action_dict(self, user_id, intra_extension_id, action_id, action_dict):
- # raise AuthzException()
- #
- # def del_action(self, user_id, intra_extension_id, action_id):
- # raise AuthzException()
- #
- # def add_subject_scope_dict(self, user_id, intra_extension_id, subject_category_id, subject_scope_dict):
- # raise AuthzException()
- #
- # def del_subject_scope(self, user_id, intra_extension_id, subject_category_id, subject_scope_id):
- # raise AuthzException()
- #
- # def set_subject_scope_dict(self, user_id, intra_extension_id, subject_category_id, subject_scope_id, subject_scope_name):
- # raise AuthzException()
- #
- # def add_object_scope_dict(self, user_id, intra_extension_id, object_category_id, object_scope_name):
- # raise AuthzException()
- #
- # def del_object_scope(self, user_id, intra_extension_id, object_category_id, object_scope_id):
- # raise AuthzException()
- #
- # def set_object_scope_dict(self, user_id, intra_extension_id, object_category_id, object_scope_id, object_scope_name):
- # raise AuthzException()
- #
- # def add_action_scope_dict(self, user_id, intra_extension_id, action_category_id, action_scope_name):
- # raise AuthzException()
- #
- # def del_action_scope(self, user_id, intra_extension_id, action_category_id, action_scope_id):
- # raise AuthzException()
- #
- # def add_subject_assignment_list(self, user_id, intra_extension_id, subject_id, subject_category_id, subject_scope_id):
- # raise AuthzException()
- #
- # def del_subject_assignment(self, user_id, intra_extension_id, subject_id, subject_category_id, subject_scope_id):
- # raise AuthzException()
- #
- # def add_object_assignment_list(self, user_id, intra_extension_id, object_id, object_category_id, object_scope_id):
- # raise AuthzException()
- #
- # def del_object_assignment(self, user_id, intra_extension_id, object_id, object_category_id, object_scope_id):
- # raise AuthzException()
- #
- # def add_action_assignment_list(self, user_id, intra_extension_id, action_id, action_category_id, action_scope_id):
- # raise AuthzException()
- #
- # def del_action_assignment(self, user_id, intra_extension_id, action_id, action_category_id, action_scope_id):
- # raise AuthzException()
- #
- # def set_aggregation_algorithm_dict(self, user_id, intra_extension_id, aggregation_algorithm_id, aggregation_algorithm_dict):
- # raise AuthzException()
- #
- # def del_aggregation_algorithm_dict(self, user_id, intra_extension_id, aggregation_algorithm_id):
- # raise AuthzException()
- #
- # def add_sub_meta_rule_dict(self, user_id, intra_extension_id, sub_meta_rule_dict):
- # raise AuthzException()
- #
- # def del_sub_meta_rule(self, user_id, intra_extension_id, sub_meta_rule_id):
- # raise AuthzException()
- #
- # def set_sub_meta_rule_dict(self, user_id, intra_extension_id, sub_meta_rule_id, sub_meta_rule_dict):
- # raise AuthzException()
- #
- # def add_rule_dict(self, user_id, intra_extension_id, sub_meta_rule_id, rule_list):
- # raise AuthzException()
- #
- # def del_rule(self, user_id, intra_extension_id, sub_meta_rule_id, rule_id):
- # raise AuthzException()
- #
- # def set_rule_dict(self, user_id, intra_extension_id, sub_meta_rule_id, rule_id, rule_list):
- # raise AuthzException()
+ def add_subject_category(self, user_id, intra_extension_id, subject_category_dict):
+ raise AuthzException()
+
+ def del_subject_category(self, user_id, intra_extension_id, subject_category_id):
+ raise AuthzException()
+
+ def set_subject_category(self, user_id, intra_extension_id, subject_category_id, subject_category_dict):
+ raise AuthzException()
+
+ def add_object_category(self, user_id, intra_extension_id, object_category_dict):
+ raise AuthzException()
+
+ def del_object_category(self, user_id, intra_extension_id, object_category_id):
+ raise AuthzException()
+
+ def add_action_category(self, user_id, intra_extension_id, action_category_name):
+ raise AuthzException()
+
+ def del_action_category(self, user_id, intra_extension_id, action_category_id):
+ raise AuthzException()
+
+ def add_object_dict(self, user_id, intra_extension_id, object_name):
+ raise AuthzException()
+
+ def set_object_dict(self, user_id, intra_extension_id, object_id, object_dict):
+ raise AuthzException()
+
+ def del_object(self, user_id, intra_extension_id, object_id):
+ raise AuthzException()
+
+ def add_action_dict(self, user_id, intra_extension_id, action_name):
+ raise AuthzException()
+
+ def set_action_dict(self, user_id, intra_extension_id, action_id, action_dict):
+ raise AuthzException()
+
+ def del_action(self, user_id, intra_extension_id, action_id):
+ raise AuthzException()
+
+ def add_subject_scope_dict(self, user_id, intra_extension_id, subject_category_id, subject_scope_dict):
+ raise AuthzException()
+
+ def del_subject_scope(self, user_id, intra_extension_id, subject_category_id, subject_scope_id):
+ raise AuthzException()
+
+ def set_subject_scope_dict(self, user_id, intra_extension_id, subject_category_id, subject_scope_id, subject_scope_name):
+ raise AuthzException()
+
+ def add_object_scope_dict(self, user_id, intra_extension_id, object_category_id, object_scope_name):
+ raise AuthzException()
+
+ def del_object_scope(self, user_id, intra_extension_id, object_category_id, object_scope_id):
+ raise AuthzException()
+
+ def set_object_scope_dict(self, user_id, intra_extension_id, object_category_id, object_scope_id, object_scope_name):
+ raise AuthzException()
+
+ def add_action_scope_dict(self, user_id, intra_extension_id, action_category_id, action_scope_name):
+ raise AuthzException()
+
+ def del_action_scope(self, user_id, intra_extension_id, action_category_id, action_scope_id):
+ raise AuthzException()
+
+ def add_subject_assignment_list(self, user_id, intra_extension_id, subject_id, subject_category_id, subject_scope_id):
+ raise AuthzException()
+
+ def del_subject_assignment(self, user_id, intra_extension_id, subject_id, subject_category_id, subject_scope_id):
+ raise AuthzException()
+
+ def add_object_assignment_list(self, user_id, intra_extension_id, object_id, object_category_id, object_scope_id):
+ raise AuthzException()
+
+ def del_object_assignment(self, user_id, intra_extension_id, object_id, object_category_id, object_scope_id):
+ raise AuthzException()
+
+ def add_action_assignment_list(self, user_id, intra_extension_id, action_id, action_category_id, action_scope_id):
+ raise AuthzException()
+
+ def del_action_assignment(self, user_id, intra_extension_id, action_id, action_category_id, action_scope_id):
+ raise AuthzException()
+
+ def set_aggregation_algorithm_dict(self, user_id, intra_extension_id, aggregation_algorithm_id, aggregation_algorithm_dict):
+ raise AuthzException()
+
+ def del_aggregation_algorithm_dict(self, user_id, intra_extension_id, aggregation_algorithm_id):
+ raise AuthzException()
+
+ def add_sub_meta_rule_dict(self, user_id, intra_extension_id, sub_meta_rule_dict):
+ raise AuthzException()
+
+ def del_sub_meta_rule(self, user_id, intra_extension_id, sub_meta_rule_id):
+ raise AuthzException()
+
+ def set_sub_meta_rule_dict(self, user_id, intra_extension_id, sub_meta_rule_id, sub_meta_rule_dict):
+ raise AuthzException()
+
+ def add_rule_dict(self, user_id, intra_extension_id, sub_meta_rule_id, rule_list):
+ raise AuthzException()
+
+ def del_rule(self, user_id, intra_extension_id, sub_meta_rule_id, rule_id):
+ raise AuthzException()
+
+ def set_rule_dict(self, user_id, intra_extension_id, sub_meta_rule_id, rule_id, rule_list):
+ raise AuthzException()
@dependency.provider('admin_api')
@@ -1885,7 +1955,7 @@ class IntraExtensionAdminManager(IntraExtensionManager):
def add_subject_dict(self, user_id, intra_extension_id, subject_dict):
subject = super(IntraExtensionAdminManager, self).add_subject_dict(user_id, intra_extension_id, subject_dict)
subject_id, subject_value = subject.iteritems().next()
- tenants_dict = self.tenant_api.get_tenants_dict(ADMIN_ID)
+ tenants_dict = self.tenant_api.get_tenants_dict(self.root_api.get_root_admin_id())
for tenant_id in tenants_dict:
if tenants_dict[tenant_id]["intra_authz_extension_id"] == intra_extension_id:
_subjects = self.driver.get_subjects_dict(tenants_dict[tenant_id]["intra_admin_extension_id"])
@@ -1902,7 +1972,7 @@ class IntraExtensionAdminManager(IntraExtensionManager):
def del_subject(self, user_id, intra_extension_id, subject_id):
subject_name = self.driver.get_subjects_dict(intra_extension_id)[subject_id]["name"]
super(IntraExtensionAdminManager, self).del_subject(user_id, intra_extension_id, subject_id)
- tenants_dict = self.tenant_api.get_tenants_dict(ADMIN_ID)
+ tenants_dict = self.tenant_api.get_tenants_dict(self.root_api.get_root_admin_id())
for tenant_id in tenants_dict:
if tenants_dict[tenant_id]["intra_authz_extension_id"] == intra_extension_id:
subject_id = self.driver.get_uuid_from_name(tenants_dict[tenant_id]["intra_admin_extension_id"],
@@ -1920,7 +1990,7 @@ class IntraExtensionAdminManager(IntraExtensionManager):
def set_subject_dict(self, user_id, intra_extension_id, subject_id, subject_dict):
subject = super(IntraExtensionAdminManager, self).set_subject_dict(user_id, intra_extension_id, subject_dict)
subject_id, subject_value = subject.iteritems().next()
- tenants_dict = self.tenant_api.get_tenants_dict(ADMIN_ID)
+ tenants_dict = self.tenant_api.get_tenants_dict(self.root_api.get_root_admin_id())
for tenant_id in tenants_dict:
if tenants_dict[tenant_id]["intra_authz_extension_id"] == intra_extension_id:
self.driver.set_subject_dict(tenants_dict[tenant_id]["intra_admin_extension_id"], uuid4().hex, subject_value)
@@ -1931,29 +2001,78 @@ class IntraExtensionAdminManager(IntraExtensionManager):
return subject
def add_object_dict(self, user_id, intra_extension_id, object_name):
- raise ObjectsWriteNoAuthorized()
+ if "admin" == self.get_intra_extension_dict(self.root_api.get_root_admin_id(), intra_extension_id)['genre']:
+ raise ObjectsWriteNoAuthorized()
+ return super(IntraExtensionAdminManager, self).add_object_dict(user_id, intra_extension_id, object_name)
def set_object_dict(self, user_id, intra_extension_id, object_id, object_dict):
- raise ObjectsWriteNoAuthorized()
+ if "admin" == self.get_intra_extension_dict(self.root_api.get_root_admin_id(), intra_extension_id)['genre']:
+ raise ObjectsWriteNoAuthorized()
+ return super(IntraExtensionAdminManager, self).set_object_dict(user_id, intra_extension_id, object_id, object_dict)
def del_object(self, user_id, intra_extension_id, object_id):
- raise ObjectsWriteNoAuthorized()
+ if "admin" == self.get_intra_extension_dict(self.root_api.get_root_admin_id(), intra_extension_id)['genre']:
+ raise ObjectsWriteNoAuthorized()
+ return super(IntraExtensionAdminManager, self).del_object(user_id, intra_extension_id, object_id)
def add_action_dict(self, user_id, intra_extension_id, action_name):
- raise ActionsWriteNoAuthorized()
+ if "admin" == self.get_intra_extension_dict(self.root_api.get_root_admin_id(), intra_extension_id)['genre']:
+ raise ActionsWriteNoAuthorized()
+ return super(IntraExtensionAdminManager, self).add_action_dict(user_id, intra_extension_id, action_name)
def set_action_dict(self, user_id, intra_extension_id, action_id, action_dict):
- raise ActionsWriteNoAuthorized()
+ if "admin" == self.get_intra_extension_dict(self.root_api.get_root_admin_id(), intra_extension_id)['genre']:
+ raise ActionsWriteNoAuthorized()
+ return super(IntraExtensionAdminManager, self).set_action_dict(user_id, intra_extension_id, action_id, action_dict)
def del_action(self, user_id, intra_extension_id, action_id):
- raise ActionsWriteNoAuthorized()
+ if "admin" == self.get_intra_extension_dict(self.root_api.get_root_admin_id(), intra_extension_id)['genre']:
+ raise ActionsWriteNoAuthorized()
+ return super(IntraExtensionAdminManager, self).del_action(user_id, intra_extension_id, action_id)
+
+
+@dependency.provider('root_api')
+@dependency.requires('moonlog_api', 'admin_api', 'tenant_api')
+class IntraExtensionRootManager(IntraExtensionManager):
+
+ def __init__(self):
+ super(IntraExtensionRootManager, self).__init__()
+ extensions = self.admin_api.driver.get_intra_extensions_dict()
+ for extension_id, extension_dict in extensions.iteritems():
+ if extension_dict["model"] == CONF.moon.root_policy_directory:
+ self.root_extension_id = extension_id
+ else:
+ extension = self.admin_api.load_root_intra_extension_dict(CONF.moon.root_policy_directory)
+ self.root_extension_id = extension['id']
+ self.root_admin_id = self.__compute_admin_id_for_root_extension()
+
+ def get_root_extension_dict(self):
+ """
+
+ :return: {id: {"name": "xxx"}}
+ """
+ return {self.root_extension_id: self.admin_api.driver.get_intra_extensions_dict()[self.root_extension_id]}
+
+ def __compute_admin_id_for_root_extension(self):
+ for subject_id, subject_dict in self.admin_api.driver.get_subjects_dict(self.root_extension_id).iteritems():
+ if subject_dict["name"] == "admin":
+ return subject_id
+ raise RootExtensionNotInitialized()
+
+ def get_root_extension_id(self):
+ return self.root_extension_id
+
+ def get_root_admin_id(self):
+ return self.root_admin_id
@dependency.provider('moonlog_api')
# Next line is mandatory in order to force keystone to process dependencies.
-@dependency.requires('identity_api', 'tenant_api', 'configuration_api', 'authz_api', 'admin_api')
+@dependency.requires('identity_api', 'tenant_api', 'configuration_api', 'authz_api', 'admin_api', 'root_api')
class LogManager(manager.Manager):
+ driver_namespace = 'keystone.moon.log'
+
def __init__(self):
driver = CONF.moon.log_driver
super(LogManager, self).__init__(driver)
diff --git a/keystone-moon/keystone/contrib/moon/extension.py b/keystone-moon/keystone/contrib/moon/extension.py
deleted file mode 100644
index efee55c5..00000000
--- a/keystone-moon/keystone/contrib/moon/extension.py
+++ /dev/null
@@ -1,740 +0,0 @@
-# Copyright 2015 Open Platform for NFV Project, Inc. and its contributors
-# This software is distributed under the terms and conditions of the 'Apache-2.0'
-# license which can be found in the file 'LICENSE' in this package distribution
-# or at 'http://www.apache.org/licenses/LICENSE-2.0'.
-
-import os.path
-import copy
-import json
-import itertools
-from uuid import uuid4
-import logging
-
-LOG = logging.getLogger("moon.authz")
-
-
-class Metadata:
-
- def __init__(self):
- self.__name = ''
- self.__model = ''
- self.__genre = ''
- self.__description = ''
- self.__subject_categories = list()
- self.__object_categories = list()
- self.__meta_rule = dict()
- self.__meta_rule['sub_meta_rules'] = list()
- self.__meta_rule['aggregation'] = ''
-
- def load_from_json(self, extension_setting_dir):
- metadata_path = os.path.join(extension_setting_dir, 'metadata.json')
- f = open(metadata_path)
- json_metadata = json.load(f)
- self.__name = json_metadata['name']
- self.__model = json_metadata['model']
- self.__genre = json_metadata['genre']
- self.__description = json_metadata['description']
- self.__subject_categories = copy.deepcopy(json_metadata['subject_categories'])
- self.__object_categories = copy.deepcopy(json_metadata['object_categories'])
- self.__meta_rule = copy.deepcopy(json_metadata['meta_rule'])
-
- def get_name(self):
- return self.__name
-
- def get_genre(self):
- return self.__genre
-
- def get_model(self):
- return self.__model
-
- def get_subject_categories(self):
- return self.__subject_categories
-
- def get_object_categories(self):
- return self.__object_categories
-
- def get_meta_rule(self):
- return self.__meta_rule
-
- def get_meta_rule_aggregation(self):
- return self.__meta_rule['aggregation']
-
- def get_data(self):
- data = dict()
- data["name"] = self.get_name()
- data["model"] = self.__model
- data["genre"] = self.__genre
- data["description"] = self.__description
- data["subject_categories"] = self.get_subject_categories()
- data["object_categories"] = self.get_object_categories()
- data["meta_rule"] = dict(self.get_meta_rule())
- return data
-
- def set_data(self, data):
- self.__name = data["name"]
- self.__model = data["model"]
- self.__genre = data["genre"]
- self.__description = data["description"]
- self.__subject_categories = list(data["subject_categories"])
- self.__object_categories = list(data["object_categories"])
- self.__meta_rule = dict(data["meta_rule"])
-
-
-class Configuration:
- def __init__(self):
- self.__subject_category_values = dict()
- # examples: { "role": {"admin", "dev", }, }
- self.__object_category_values = dict()
- self.__rules = list()
-
- def load_from_json(self, extension_setting_dir):
- configuration_path = os.path.join(extension_setting_dir, 'configuration.json')
- f = open(configuration_path)
- json_configuration = json.load(f)
- self.__subject_category_values = copy.deepcopy(json_configuration['subject_category_values'])
- self.__object_category_values = copy.deepcopy(json_configuration['object_category_values'])
- self.__rules = copy.deepcopy(json_configuration['rules']) # TODO: currently a list, will be a dict with sub-meta-rule as key
-
- def get_subject_category_values(self):
- return self.__subject_category_values
-
- def get_object_category_values(self):
- return self.__object_category_values
-
- def get_rules(self):
- return self.__rules
-
- def get_data(self):
- data = dict()
- data["subject_category_values"] = self.get_subject_category_values()
- data["object_category_values"] = self.get_object_category_values()
- data["rules"] = self.get_rules()
- return data
-
- def set_data(self, data):
- self.__subject_category_values = list(data["subject_category_values"])
- self.__object_category_values = list(data["object_category_values"])
- self.__rules = list(data["rules"])
-
-
-class Perimeter:
- def __init__(self):
- self.__subjects = list()
- self.__objects = list()
-
- def load_from_json(self, extension_setting_dir):
- perimeter_path = os.path.join(extension_setting_dir, 'perimeter.json')
- f = open(perimeter_path)
- json_perimeter = json.load(f)
- self.__subjects = copy.deepcopy(json_perimeter['subjects'])
- self.__objects = copy.deepcopy(json_perimeter['objects'])
- # print(self.__subjects)
- # print(self.__objects)
-
- def get_subjects(self):
- return self.__subjects
-
- def get_objects(self):
- return self.__objects
-
- def get_data(self):
- data = dict()
- data["subjects"] = self.get_subjects()
- data["objects"] = self.get_objects()
- return data
-
- def set_data(self, data):
- self.__subjects = list(data["subjects"])
- self.__objects = list(data["objects"])
-
-
-class Assignment:
- def __init__(self):
- self.__subject_category_assignments = dict()
- # examples: { "role": {"user1": {"dev"}, "user2": {"admin",}}, } TODO: limit to one value for each attr
- self.__object_category_assignments = dict()
-
- def load_from_json(self, extension_setting_dir):
- assignment_path = os.path.join(extension_setting_dir, 'assignment.json')
- f = open(assignment_path)
- json_assignment = json.load(f)
-
- self.__subject_category_assignments = dict(copy.deepcopy(json_assignment['subject_category_assignments']))
- self.__object_category_assignments = dict(copy.deepcopy(json_assignment['object_category_assignments']))
-
- def get_subject_category_assignments(self):
- return self.__subject_category_assignments
-
- def get_object_category_assignments(self):
- return self.__object_category_assignments
-
- def get_data(self):
- data = dict()
- data["subject_category_assignments"] = self.get_subject_category_assignments()
- data["object_category_assignments"] = self.get_object_category_assignments()
- return data
-
- def set_data(self, data):
- self.__subject_category_assignments = list(data["subject_category_assignments"])
- self.__object_category_assignments = list(data["object_category_assignments"])
-
-
-class AuthzData:
- def __init__(self, sub, obj, act):
- self.validation = "False" # "OK, KO, Out of Scope" # "auth": False,
- self.subject = sub
- self.object = str(obj)
- self.action = str(act)
- self.type = "" # intra-tenant, inter-tenant, Out of Scope
- self.subject_attrs = dict()
- self.object_attrs = dict()
- self.requesting_tenant = "" # "subject_tenant": subject_tenant,
- self.requested_tenant = "" # "object_tenant": object_tenant,
-
- def __str__(self):
- return """AuthzData:
- validation={}
- subject={}
- object={}
- action={}
- """.format(self.validation, self.subject, self.object, self.action)
-
-
-class Extension:
- def __init__(self):
- self.metadata = Metadata()
- self.configuration = Configuration()
- self.perimeter = Perimeter()
- self.assignment = Assignment()
-
- def load_from_json(self, extension_setting_dir):
- self.metadata.load_from_json(extension_setting_dir)
- self.configuration.load_from_json(extension_setting_dir)
- self.perimeter.load_from_json(extension_setting_dir)
- self.assignment.load_from_json(extension_setting_dir)
-
- def get_name(self):
- return self.metadata.get_name()
-
- def get_genre(self):
- return self.metadata.get_genre()
-
- def authz(self, sub, obj, act):
- authz_data = AuthzData(sub, obj, act)
- # authz_logger.warning('extension/authz request: [sub {}, obj {}, act {}]'.format(sub, obj, act))
-
- if authz_data.subject in self.perimeter.get_subjects() and authz_data.object in self.perimeter.get_objects():
-
- for subject_category in self.metadata.get_subject_categories():
- authz_data.subject_attrs[subject_category] = copy.copy(
- # self.assignment.get_subject_category_attr(subject_category, sub)
- self.assignment.get_subject_category_assignments()[subject_category][sub]
- )
- # authz_logger.warning('extension/authz subject attribute: [subject attr: {}]'.format(
- # #self.assignment.get_subject_category_attr(subject_category, sub))
- # self.assignment.get_subject_category_assignments()[subject_category][sub])
- # )
-
- for object_category in self.metadata.get_object_categories():
- if object_category == 'action':
- authz_data.object_attrs[object_category] = [act]
- # authz_logger.warning('extension/authz object attribute: [object attr: {}]'.format([act]))
- else:
- authz_data.object_attrs[object_category] = copy.copy(
- self.assignment.get_object_category_assignments()[object_category][obj]
- )
- # authz_logger.warning('extension/authz object attribute: [object attr: {}]'.format(
- # self.assignment.get_object_category_assignments()[object_category][obj])
- # )
-
- _aggregation_data = dict()
-
- for sub_meta_rule in self.metadata.get_meta_rule()["sub_meta_rules"].values():
- _tmp_relation_args = list()
-
- for sub_subject_category in sub_meta_rule["subject_categories"]:
- _tmp_relation_args.append(authz_data.subject_attrs[sub_subject_category])
-
- for sub_object_category in sub_meta_rule["object_categories"]:
- _tmp_relation_args.append(authz_data.object_attrs[sub_object_category])
-
- _relation_args = list(itertools.product(*_tmp_relation_args))
-
- if sub_meta_rule['relation'] == 'relation_super': # TODO: replace by Prolog Engine
- _aggregation_data['relation_super'] = dict()
- _aggregation_data['relation_super']['result'] = False
- for _relation_arg in _relation_args:
- if list(_relation_arg) in self.configuration.get_rules()[sub_meta_rule['relation']]:
- # authz_logger.warning(
- # 'extension/authz relation super OK: [sub_sl: {}, obj_sl: {}, action: {}]'.format(
- # _relation_arg[0], _relation_arg[1], _relation_arg[2]
- # )
- # )
- _aggregation_data['relation_super']['result'] = True
- break
- _aggregation_data['relation_super']['status'] = 'finished'
-
- elif sub_meta_rule['relation'] == 'permission':
- _aggregation_data['permission'] = dict()
- _aggregation_data['permission']['result'] = False
- for _relation_arg in _relation_args:
- if list(_relation_arg) in self.configuration.get_rules()[sub_meta_rule['relation']]:
- # authz_logger.warning(
- # 'extension/authz relation permission OK: [role: {}, object: {}, action: {}]'.format(
- # _relation_arg[0], _relation_arg[1], _relation_arg[2]
- # )
- # )
- _aggregation_data['permission']['result'] = True
- break
- _aggregation_data['permission']['status'] = 'finished'
-
- if self.metadata.get_meta_rule_aggregation() == 'and_true_aggregation':
- authz_data.validation = "OK"
- for relation in _aggregation_data:
- if _aggregation_data[relation]['status'] == 'finished' \
- and _aggregation_data[relation]['result'] == False:
- authz_data.validation = "KO"
- else:
- authz_data.validation = 'Out of Scope'
-
- return authz_data.validation
-
- # ---------------- metadate api ----------------
-
- def get_subject_categories(self):
- return self.metadata.get_subject_categories()
-
- def add_subject_category(self, category_id):
- if category_id in self.get_subject_categories():
- return "[ERROR] Add Subject Category: Subject Category Exists"
- else:
- self.get_subject_categories().append(category_id)
- self.configuration.get_subject_category_values()[category_id] = list()
- self.assignment.get_subject_category_assignments()[category_id] = dict()
- return self.get_subject_categories()
-
- def del_subject_category(self, category_id):
- if category_id in self.get_subject_categories():
- self.configuration.get_subject_category_values().pop(category_id)
- self.assignment.get_subject_category_assignments().pop(category_id)
- self.get_subject_categories().remove(category_id)
- return self.get_subject_categories()
- else:
- return "[ERROR] Del Subject Category: Subject Category Unknown"
-
- def get_object_categories(self):
- return self.metadata.get_object_categories()
-
- def add_object_category(self, category_id):
- if category_id in self.get_object_categories():
- return "[ERROR] Add Object Category: Object Category Exists"
- else:
- self.get_object_categories().append(category_id)
- self.configuration.get_object_category_values()[category_id] = list()
- self.assignment.get_object_category_assignments()[category_id] = dict()
- return self.get_object_categories()
-
- def del_object_category(self, category_id):
- if category_id in self.get_object_categories():
- self.configuration.get_object_category_values().pop(category_id)
- self.assignment.get_object_category_assignments().pop(category_id)
- self.get_object_categories().remove(category_id)
- return self.get_object_categories()
- else:
- return "[ERROR] Del Object Category: Object Category Unknown"
-
- def get_meta_rule(self):
- return self.metadata.get_meta_rule()
-
- # ---------------- configuration api ----------------
-
- def get_subject_category_values(self, category_id):
- return self.configuration.get_subject_category_values()[category_id]
-
- def add_subject_category_value(self, category_id, category_value):
- if category_value in self.configuration.get_subject_category_values()[category_id]:
- return "[ERROR] Add Subject Category Value: Subject Category Value Exists"
- else:
- self.configuration.get_subject_category_values()[category_id].append(category_value)
- return self.configuration.get_subject_category_values()[category_id]
-
- def del_subject_category_value(self, category_id, category_value):
- if category_value in self.configuration.get_subject_category_values()[category_id]:
- self.configuration.get_subject_category_values()[category_id].remove(category_value)
- return self.configuration.get_subject_category_values()[category_id]
- else:
- return "[ERROR] Del Subject Category Value: Subject Category Value Unknown"
-
- def get_object_category_values(self, category_id):
- return self.configuration.get_object_category_values()[category_id]
-
- def add_object_category_value(self, category_id, category_value):
- if category_value in self.configuration.get_object_category_values()[category_id]:
- return "[ERROR] Add Object Category Value: Object Category Value Exists"
- else:
- self.configuration.get_object_category_values()[category_id].append(category_value)
- return self.configuration.get_object_category_values()[category_id]
-
- def del_object_category_value(self, category_id, category_value):
- if category_value in self.configuration.get_object_category_values()[category_id]:
- self.configuration.get_object_category_values()[category_id].remove(category_value)
- return self.configuration.get_object_category_values()[category_id]
- else:
- return "[ERROR] Del Object Category Value: Object Category Value Unknown"
-
- def get_meta_rules(self):
- return self.metadata.get_meta_rule()
-
- def _build_rule_from_list(self, relation, rule):
- rule = list(rule)
- _rule = dict()
- _rule["sub_cat_value"] = dict()
- _rule["obj_cat_value"] = dict()
- if relation in self.metadata.get_meta_rule()["sub_meta_rules"]:
- _rule["sub_cat_value"][relation] = dict()
- _rule["obj_cat_value"][relation] = dict()
- for s_category in self.metadata.get_meta_rule()["sub_meta_rules"][relation]["subject_categories"]:
- _rule["sub_cat_value"][relation][s_category] = rule.pop(0)
- for o_category in self.metadata.get_meta_rule()["sub_meta_rules"][relation]["object_categories"]:
- _rule["obj_cat_value"][relation][o_category] = rule.pop(0)
- return _rule
-
- def get_rules(self, full=False):
- if not full:
- return self.configuration.get_rules()
- rules = dict()
- for key in self.configuration.get_rules():
- rules[key] = map(lambda x: self._build_rule_from_list(key, x), self.configuration.get_rules()[key])
- return rules
-
- def add_rule(self, sub_cat_value_dict, obj_cat_value_dict):
- for _relation in self.metadata.get_meta_rule()["sub_meta_rules"]:
- _sub_rule = list()
- for sub_subject_category in self.metadata.get_meta_rule()["sub_meta_rules"][_relation]["subject_categories"]:
- try:
- if sub_cat_value_dict[_relation][sub_subject_category] \
- in self.configuration.get_subject_category_values()[sub_subject_category]:
- _sub_rule.append(sub_cat_value_dict[_relation][sub_subject_category])
- else:
- return "[Error] Add Rule: Subject Category Value Unknown"
- except KeyError as e:
- # DThom: sometimes relation attribute is buggy, I don't know why...
- print(e)
-
- #BUG: when adding a new category in rules despite it was previously adding
- # data = {
- # "sub_cat_value":
- # {"relation_super":
- # {"subject_security_level": "high", "AMH_CAT": "AMH_VAL"}
- # },
- # "obj_cat_value":
- # {"relation_super":
- # {"object_security_level": "medium"}
- # }
- # }
- # traceback = """
- # Traceback (most recent call last):
- # File "/moon/gui/views_json.py", line 20, in wrapped
- # result = function(*args, **kwargs)
- # File "/moon/gui/views_json.py", line 429, in rules
- # obj_cat_value=filter_input(data["obj_cat_value"]))
- # File "/usr/local/lib/python2.7/dist-packages/moon/core/pap/core.py", line 380, in add_rule
- # obj_cat_value)
- # File "/usr/local/lib/python2.7/dist-packages/moon/core/pdp/extension.py", line 414, in add_rule
- # if obj_cat_value_dict[_relation][sub_object_category] \
- # KeyError: u'action'
- # """
- for sub_object_category in self.metadata.get_meta_rule()["sub_meta_rules"][_relation]["object_categories"]:
- if obj_cat_value_dict[_relation][sub_object_category] \
- in self.configuration.get_object_category_values()[sub_object_category]:
- _sub_rule.append(obj_cat_value_dict[_relation][sub_object_category])
- else:
- return "[Error] Add Rule: Object Category Value Unknown"
-
- if _sub_rule in self.configuration.get_rules()[_relation]:
- return "[Error] Add Rule: Rule Exists"
- else:
- self.configuration.get_rules()[_relation].append(_sub_rule)
- return {
- sub_cat_value_dict.keys()[0]: ({
- "sub_cat_value": copy.deepcopy(sub_cat_value_dict),
- "obj_cat_value": copy.deepcopy(obj_cat_value_dict)
- }, )
- }
- return self.configuration.get_rules()
-
- def del_rule(self, sub_cat_value_dict, obj_cat_value_dict):
- for _relation in self.metadata.get_meta_rule()["sub_meta_rules"]:
- _sub_rule = list()
- for sub_subject_category in self.metadata.get_meta_rule()["sub_meta_rules"][_relation]["subject_categories"]:
- _sub_rule.append(sub_cat_value_dict[_relation][sub_subject_category])
-
- for sub_object_category in self.metadata.get_meta_rule()["sub_meta_rules"][_relation]["object_categories"]:
- _sub_rule.append(obj_cat_value_dict[_relation][sub_object_category])
-
- if _sub_rule in self.configuration.get_rules()[_relation]:
- self.configuration.get_rules()[_relation].remove(_sub_rule)
- else:
- return "[Error] Del Rule: Rule Unknown"
- return self.configuration.get_rules()
-
- # ---------------- perimeter api ----------------
-
- def get_subjects(self):
- return self.perimeter.get_subjects()
-
- def get_objects(self):
- return self.perimeter.get_objects()
-
- def add_subject(self, subject_id):
- if subject_id in self.perimeter.get_subjects():
- return "[ERROR] Add Subject: Subject Exists"
- else:
- self.perimeter.get_subjects().append(subject_id)
- return self.perimeter.get_subjects()
-
- def del_subject(self, subject_id):
- if subject_id in self.perimeter.get_subjects():
- self.perimeter.get_subjects().remove(subject_id)
- return self.perimeter.get_subjects()
- else:
- return "[ERROR] Del Subject: Subject Unknown"
-
- def add_object(self, object_id):
- if object_id in self.perimeter.get_objects():
- return "[ERROR] Add Object: Object Exists"
- else:
- self.perimeter.get_objects().append(object_id)
- return self.perimeter.get_objects()
-
- def del_object(self, object_id):
- if object_id in self.perimeter.get_objects():
- self.perimeter.get_objects().remove(object_id)
- return self.perimeter.get_objects()
- else:
- return "[ERROR] Del Object: Object Unknown"
-
- # ---------------- assignment api ----------------
-
- def get_subject_assignments(self, category_id):
- if category_id in self.metadata.get_subject_categories():
- return self.assignment.get_subject_category_assignments()[category_id]
- else:
- return "[ERROR] Get Subject Assignment: Subject Category Unknown"
-
- def add_subject_assignment(self, category_id, subject_id, category_value):
- if category_id in self.metadata.get_subject_categories():
- if subject_id in self.perimeter.get_subjects():
- if category_value in self.configuration.get_subject_category_values()[category_id]:
- if category_id in self.assignment.get_subject_category_assignments().keys():
- if subject_id in self.assignment.get_subject_category_assignments()[category_id].keys():
- if category_value in self.assignment.get_subject_category_assignments()[category_id][subject_id]:
- return "[ERROR] Add Subject Assignment: Subject Assignment Exists"
- else:
- self.assignment.get_subject_category_assignments()[category_id][subject_id].extend([category_value])
- else:
- self.assignment.get_subject_category_assignments()[category_id][subject_id] = [category_value]
- else:
- self.assignment.get_subject_category_assignments()[category_id] = {subject_id: [category_value]}
- return self.assignment.get_subject_category_assignments()
- else:
- return "[ERROR] Add Subject Assignment: Subject Category Value Unknown"
- else:
- return "[ERROR] Add Subject Assignment: Subject Unknown"
- else:
- return "[ERROR] Add Subject Assignment: Subject Category Unknown"
-
- def del_subject_assignment(self, category_id, subject_id, category_value):
- if category_id in self.metadata.get_subject_categories():
- if subject_id in self.perimeter.get_subjects():
- if category_value in self.configuration.get_subject_category_values()[category_id]:
- if len(self.assignment.get_subject_category_assignments()[category_id][subject_id]) >= 2:
- self.assignment.get_subject_category_assignments()[category_id][subject_id].remove(category_value)
- else:
- self.assignment.get_subject_category_assignments()[category_id].pop(subject_id)
- return self.assignment.get_subject_category_assignments()
- else:
- return "[ERROR] Del Subject Assignment: Assignment Unknown"
- else:
- return "[ERROR] Del Subject Assignment: Subject Unknown"
- else:
- return "[ERROR] Del Subject Assignment: Subject Category Unknown"
-
- def get_object_assignments(self, category_id):
- if category_id in self.metadata.get_object_categories():
- return self.assignment.get_object_category_assignments()[category_id]
- else:
- return "[ERROR] Get Object Assignment: Object Category Unknown"
-
- def add_object_assignment(self, category_id, object_id, category_value):
- if category_id in self.metadata.get_object_categories():
- if object_id in self.perimeter.get_objects():
- if category_value in self.configuration.get_object_category_values()[category_id]:
- if category_id in self.assignment.get_object_category_assignments().keys():
- if object_id in self.assignment.get_object_category_assignments()[category_id].keys():
- if category_value in self.assignment.get_object_category_assignments()[category_id][object_id]:
- return "[ERROR] Add Object Assignment: Object Assignment Exists"
- else:
- self.assignment.get_object_category_assignments()[category_id][object_id].extend([category_value])
- else:
- self.assignment.get_object_category_assignments()[category_id][object_id] = [category_value]
- else:
- self.assignment.get_object_category_assignments()[category_id] = {object_id: [category_value]}
- return self.assignment.get_object_category_assignments()
- else:
- return "[ERROR] Add Object Assignment: Object Category Value Unknown"
- else:
- return "[ERROR] Add Object Assignment: Object Unknown"
- else:
- return "[ERROR] Add Object Assignment: Object Category Unknown"
-
- def del_object_assignment(self, category_id, object_id, category_value):
- if category_id in self.metadata.get_object_categories():
- if object_id in self.perimeter.get_objects():
- if category_value in self.configuration.get_object_category_values()[category_id]:
- if len(self.assignment.get_object_category_assignments()[category_id][object_id]) >= 2:
- self.assignment.get_object_category_assignments()[category_id][object_id].remove(category_value)
- else:
- self.assignment.get_object_category_assignments()[category_id].pop(object_id)
- return self.assignment.get_object_category_assignments()
- else:
- return "[ERROR] Del Object Assignment: Assignment Unknown"
- else:
- return "[ERROR] Del Object Assignment: Object Unknown"
- else:
- return "[ERROR] Del Object Assignment: Object Category Unknown"
-
- # ---------------- inter-extension API ----------------
-
- def create_requesting_collaboration(self, sub_list, vent_uuid, act):
- _sub_cat_values = dict()
- _obj_cat_values = dict()
-
- if type(self.add_object(vent_uuid)) is not list:
- return "[Error] Create Requesting Collaboration: No Success"
- for _relation in self.get_meta_rule()["sub_meta_rules"]:
- for _sub_cat_id in self.get_meta_rule()["sub_meta_rules"][_relation]["subject_categories"]:
- _sub_cat_value = str(uuid4())
- if type(self.add_subject_category_value(_sub_cat_id, _sub_cat_value)) is not list:
- return "[Error] Create Requesting Collaboration: No Success"
- _sub_cat_values[_relation] = {_sub_cat_id: _sub_cat_value}
- for _sub in sub_list:
- if type(self.add_subject_assignment(_sub_cat_id, _sub, _sub_cat_value)) is not dict:
- return "[Error] Create Requesting Collaboration: No Success"
-
- for _obj_cat_id in self.get_meta_rule()["sub_meta_rules"][_relation]["object_categories"]:
- if _obj_cat_id == 'action':
- _obj_cat_values[_relation][_obj_cat_id] = act
- else:
- _obj_cat_value = str(uuid4())
- if type(self.add_object_category_value(_obj_cat_id, _obj_cat_value)) is not list:
- return "[Error] Create Requesting Collaboration: No Success"
- if type(self.add_object_assignment(_obj_cat_id, vent_uuid, _obj_cat_value)) is not dict:
- return "[Error] Create Requesting Collaboration: No Success"
- _obj_cat_values[_relation] = {_obj_cat_id: _obj_cat_value}
-
- _rule = self.add_rule(_sub_cat_values, _obj_cat_values)
- if type(_rule) is not dict:
- return "[Error] Create Requesting Collaboration: No Success"
- return {"subject_category_value_dict": _sub_cat_values, "object_category_value_dict": _obj_cat_values,
- "rule": _rule}
-
- def destroy_requesting_collaboration(self, sub_list, vent_uuid, sub_cat_value_dict, obj_cat_value_dict):
- for _relation in self.get_meta_rule()["sub_meta_rules"]:
- for _sub_cat_id in self.get_meta_rule()["sub_meta_rules"][_relation]["subject_categories"]:
- for _sub in sub_list:
- if type(self.del_subject_assignment(_sub_cat_id, _sub, sub_cat_value_dict[_relation][_sub_cat_id]))\
- is not dict:
- return "[Error] Destroy Requesting Collaboration: No Success"
- if type(self.del_subject_category_value(_sub_cat_id, sub_cat_value_dict[_relation][_sub_cat_id])) \
- is not list:
- return "[Error] Destroy Requesting Collaboration: No Success"
-
- for _obj_cat_id in self.get_meta_rule()["sub_meta_rules"][_relation]["object_categories"]:
- if _obj_cat_id == "action":
- pass # TODO: reconsidering the action as object attribute
- else:
- if type(self.del_object_assignment(_obj_cat_id, vent_uuid, obj_cat_value_dict[_relation][_obj_cat_id])) is not dict:
- return "[Error] Destroy Requesting Collaboration: No Success"
- if type(self.del_object_category_value(_obj_cat_id, obj_cat_value_dict[_relation][_obj_cat_id])) is not list:
- return "[Error] Destroy Requesting Collaboration: No Success"
-
- if type(self.del_rule(sub_cat_value_dict, obj_cat_value_dict)) is not dict:
- return "[Error] Destroy Requesting Collaboration: No Success"
- if type(self.del_object(vent_uuid)) is not list:
- return "[Error] Destroy Requesting Collaboration: No Success"
- return "[Destroy Requesting Collaboration] OK"
-
- def create_requested_collaboration(self, vent_uuid, obj_list, act):
- _sub_cat_values = dict()
- _obj_cat_values = dict()
-
- if type(self.add_subject(vent_uuid)) is not list:
- return "[Error] Create Requested Collaboration: No Success"
-
- for _relation in self.get_meta_rule()["sub_meta_rules"]:
- for _sub_cat_id in self.get_meta_rule()["sub_meta_rules"][_relation]["subject_categories"]:
- _sub_cat_value = str(uuid4())
- if type(self.add_subject_category_value(_sub_cat_id, _sub_cat_value)) is not list:
- return "[Error] Create Requested Collaboration: No Success"
- _sub_cat_values[_relation] = {_sub_cat_id: _sub_cat_value}
- if type(self.add_subject_assignment(_sub_cat_id, vent_uuid, _sub_cat_value)) is not dict:
- return "[Error] Create Requested Collaboration: No Success"
-
- for _obj_cat_id in self.get_meta_rule()["sub_meta_rules"][_relation]["object_categories"]:
- if _obj_cat_id == 'action':
- _obj_cat_values[_relation][_obj_cat_id] = act
- else:
- _obj_cat_value = str(uuid4())
- if type(self.add_object_category_value(_obj_cat_id, _obj_cat_value)) is not list:
- return "[Error] Create Requested Collaboration: No Success"
- _obj_cat_values[_relation] = {_obj_cat_id: _obj_cat_value}
- for _obj in obj_list:
- if type(self.add_object_assignment(_obj_cat_id, _obj, _obj_cat_value)) is not dict:
- return "[Error] Create Requested Collaboration: No Success"
-
- _rule = self.add_rule(_sub_cat_values, _obj_cat_values)
- if type(_rule) is not dict:
- return "[Error] Create Requested Collaboration: No Success"
- return {"subject_category_value_dict": _sub_cat_values, "object_category_value_dict": _obj_cat_values,
- "rule": _rule}
-
- def destroy_requested_collaboration(self, vent_uuid, obj_list, sub_cat_value_dict, obj_cat_value_dict):
- for _relation in self.get_meta_rule()["sub_meta_rules"]:
- for _sub_cat_id in self.get_meta_rule()["sub_meta_rules"][_relation]["subject_categories"]:
- if type(self.del_subject_assignment(_sub_cat_id, vent_uuid, sub_cat_value_dict[_relation][_sub_cat_id])) is not dict:
- return "[Error] Destroy Requested Collaboration: No Success"
- if type(self.del_subject_category_value(_sub_cat_id, sub_cat_value_dict[_relation][_sub_cat_id])) is not list:
- return "[Error] Destroy Requested Collaboration: No Success"
-
- for _obj_cat_id in self.get_meta_rule()["sub_meta_rules"][_relation]["object_categories"]:
- if _obj_cat_id == "action":
- pass # TODO: reconsidering the action as object attribute
- else:
- for _obj in obj_list:
- if type(self.del_object_assignment(_obj_cat_id, _obj, obj_cat_value_dict[_relation][_obj_cat_id])) is not dict:
- return "[Error] Destroy Requested Collaboration: No Success"
- if type(self.del_object_category_value(_obj_cat_id, obj_cat_value_dict[_relation][_obj_cat_id])) is not list:
- return "[Error] Destroy Requested Collaboration: No Success"
-
- if type(self.del_rule(sub_cat_value_dict, obj_cat_value_dict)) is not dict:
- return "[Error] Destroy Requested Collaboration: No Success"
- if type(self.del_subject(vent_uuid)) is not list:
- return "[Error] Destroy Requested Collaboration: No Success"
- return "[Destroy Requested Collaboration] OK"
-
- # ---------------- sync_db api ----------------
-
- def get_data(self):
- data = dict()
- data["metadata"] = self.metadata.get_data()
- data["configuration"] = self.configuration.get_data()
- data["perimeter"] = self.perimeter.get_data()
- data["assignment"] = self.assignment.get_data()
- return data
-
- def set_data(self, extension_data):
- self.metadata.set_data(extension_data["metadata"])
- self.configuration.set_data(extension_data["configuration"])
- self.perimeter.set_data(extension_data["perimeter"])
- self.assignment.set_data(extension_data["assignment"])
diff --git a/keystone-moon/keystone/contrib/moon/migrate_repo/versions/001_moon.py b/keystone-moon/keystone/contrib/moon/migrate_repo/versions/001_moon.py
index af4d80bb..4fd26bef 100644
--- a/keystone-moon/keystone/contrib/moon/migrate_repo/versions/001_moon.py
+++ b/keystone-moon/keystone/contrib/moon/migrate_repo/versions/001_moon.py
@@ -21,11 +21,11 @@ def upgrade(migrate_engine):
mysql_charset='utf8')
intra_extension_table.create(migrate_engine, checkfirst=True)
- intra_extension_table.insert().values(id=uuid4().hex, intra_extension={
- 'name': "Root Extension",
- 'description': "The root intra extension",
- 'model': 'admin'
- })
+ # intra_extension_table.insert().values(id=uuid4().hex, intra_extension={
+ # 'name': "Root Extension",
+ # 'description': "The root intra extension",
+ # 'model': 'admin'
+ # })
tenant_table = sql.Table(
'tenants',
@@ -195,8 +195,6 @@ def upgrade(migrate_engine):
mysql_charset='utf8')
rules_table.create(migrate_engine, checkfirst=True)
- # TODO: load root_extension
-
def downgrade(migrate_engine):
meta = sql.MetaData()
diff --git a/keystone-moon/keystone/contrib/moon/routers.py b/keystone-moon/keystone/contrib/moon/routers.py
index 4da3b991..63915092 100644
--- a/keystone-moon/keystone/contrib/moon/routers.py
+++ b/keystone-moon/keystone/contrib/moon/routers.py
@@ -9,7 +9,7 @@ from keystone.contrib.moon import controllers
from keystone.common import wsgi
-class Routers(wsgi.RoutersBase):
+class Routers(wsgi.V3ExtensionRouter):
"""API Endpoints for the Moon extension.
"""
diff --git a/keystone-moon/keystone/contrib/oauth1/backends/sql.py b/keystone-moon/keystone/contrib/oauth1/backends/sql.py
index c6ab6e5a..a7876756 100644
--- a/keystone-moon/keystone/contrib/oauth1/backends/sql.py
+++ b/keystone-moon/keystone/contrib/oauth1/backends/sql.py
@@ -18,9 +18,9 @@ import uuid
from oslo_serialization import jsonutils
from oslo_utils import timeutils
-import six
from keystone.common import sql
+from keystone.common import utils
from keystone.contrib.oauth1 import core
from keystone import exception
from keystone.i18n import _
@@ -58,7 +58,7 @@ class RequestToken(sql.ModelBase, sql.DictBase):
return cls(**user_dict)
def to_dict(self):
- return dict(six.iteritems(self))
+ return dict(self.items())
class AccessToken(sql.ModelBase, sql.DictBase):
@@ -81,7 +81,7 @@ class AccessToken(sql.ModelBase, sql.DictBase):
return cls(**user_dict)
def to_dict(self):
- return dict(six.iteritems(self))
+ return dict(self.items())
class OAuth1(object):
@@ -163,7 +163,7 @@ class OAuth1(object):
if token_duration:
now = timeutils.utcnow()
future = now + datetime.timedelta(seconds=token_duration)
- expiry_date = timeutils.isotime(future, subsecond=True)
+ expiry_date = utils.isotime(future, subsecond=True)
ref = {}
ref['id'] = request_token_id
@@ -225,7 +225,7 @@ class OAuth1(object):
if token_duration:
now = timeutils.utcnow()
future = now + datetime.timedelta(seconds=token_duration)
- expiry_date = timeutils.isotime(future, subsecond=True)
+ expiry_date = utils.isotime(future, subsecond=True)
# add Access Token
ref = {}
diff --git a/keystone-moon/keystone/contrib/oauth1/controllers.py b/keystone-moon/keystone/contrib/oauth1/controllers.py
index fb5d0bc2..d12fc96b 100644
--- a/keystone-moon/keystone/contrib/oauth1/controllers.py
+++ b/keystone-moon/keystone/contrib/oauth1/controllers.py
@@ -20,12 +20,12 @@ from oslo_utils import timeutils
from keystone.common import controller
from keystone.common import dependency
+from keystone.common import utils
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.i18n import _
-from keystone.models import token_model
from keystone import notifications
@@ -84,10 +84,7 @@ class ConsumerCrudV3(controller.V3Controller):
@controller.protected()
def delete_consumer(self, context, consumer_id):
- user_token_ref = token_model.KeystoneToken(
- token_id=context['token_id'],
- token_data=self.token_provider_api.validate_token(
- context['token_id']))
+ user_token_ref = utils.get_token_ref(context)
payload = {'user_id': user_token_ref.user_id,
'consumer_id': consumer_id}
_emit_user_oauth_consumer_token_invalidate(payload)
@@ -382,10 +379,7 @@ class OAuthControllerV3(controller.V3Controller):
authed_roles.add(role['id'])
# verify the authorizing user has the roles
- user_token = token_model.KeystoneToken(
- token_id=context['token_id'],
- token_data=self.token_provider_api.validate_token(
- context['token_id']))
+ user_token = utils.get_token_ref(context)
user_id = user_token.user_id
project_id = req_token['requested_project_id']
user_roles = self.assignment_api.get_roles_for_user_and_project(
diff --git a/keystone-moon/keystone/contrib/oauth1/core.py b/keystone-moon/keystone/contrib/oauth1/core.py
index eeb3e114..d7f64dc4 100644
--- a/keystone-moon/keystone/contrib/oauth1/core.py
+++ b/keystone-moon/keystone/contrib/oauth1/core.py
@@ -12,7 +12,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-"""Extensions supporting OAuth1."""
+"""Main entry point into the OAuth1 service."""
from __future__ import absolute_import
@@ -151,6 +151,9 @@ class Manager(manager.Manager):
dynamically calls the backend.
"""
+
+ driver_namespace = 'keystone.oauth1'
+
_ACCESS_TOKEN = "OS-OAUTH1:access_token"
_REQUEST_TOKEN = "OS-OAUTH1:request_token"
_CONSUMER = "OS-OAUTH1:consumer"
diff --git a/keystone-moon/keystone/contrib/oauth1/migrate_repo/versions/001_add_oauth_tables.py b/keystone-moon/keystone/contrib/oauth1/migrate_repo/versions/001_add_oauth_tables.py
index a4fbf155..e0305351 100644
--- a/keystone-moon/keystone/contrib/oauth1/migrate_repo/versions/001_add_oauth_tables.py
+++ b/keystone-moon/keystone/contrib/oauth1/migrate_repo/versions/001_add_oauth_tables.py
@@ -55,13 +55,3 @@ def upgrade(migrate_engine):
sql.Column('consumer_id', sql.String(64), nullable=False),
sql.Column('expires_at', sql.String(64), nullable=True))
access_token_table.create(migrate_engine, checkfirst=True)
-
-
-def downgrade(migrate_engine):
- meta = sql.MetaData()
- meta.bind = migrate_engine
- # Operations to reverse the above upgrade go here.
- tables = ['consumer', 'request_token', 'access_token']
- for table_name in tables:
- table = sql.Table(table_name, meta, autoload=True)
- table.drop()
diff --git a/keystone-moon/keystone/contrib/oauth1/migrate_repo/versions/002_fix_oauth_tables_fk.py b/keystone-moon/keystone/contrib/oauth1/migrate_repo/versions/002_fix_oauth_tables_fk.py
index d39df8d5..174120e8 100644
--- a/keystone-moon/keystone/contrib/oauth1/migrate_repo/versions/002_fix_oauth_tables_fk.py
+++ b/keystone-moon/keystone/contrib/oauth1/migrate_repo/versions/002_fix_oauth_tables_fk.py
@@ -35,20 +35,3 @@ def upgrade(migrate_engine):
'ref_column': consumer_table.c.id}]
if meta.bind != 'sqlite':
migration_helpers.add_constraints(constraints)
-
-
-def downgrade(migrate_engine):
- meta = sql.MetaData()
- meta.bind = migrate_engine
- consumer_table = sql.Table('consumer', meta, autoload=True)
- request_token_table = sql.Table('request_token', meta, autoload=True)
- access_token_table = sql.Table('access_token', meta, autoload=True)
-
- constraints = [{'table': request_token_table,
- 'fk_column': 'consumer_id',
- 'ref_column': consumer_table.c.id},
- {'table': access_token_table,
- 'fk_column': 'consumer_id',
- 'ref_column': consumer_table.c.id}]
- if migrate_engine.name != 'sqlite':
- migration_helpers.remove_constraints(constraints)
diff --git a/keystone-moon/keystone/contrib/oauth1/migrate_repo/versions/003_consumer_description_nullalbe.py b/keystone-moon/keystone/contrib/oauth1/migrate_repo/versions/003_consumer_description_nullalbe.py
index e1cf8843..cf6ffb7c 100644
--- a/keystone-moon/keystone/contrib/oauth1/migrate_repo/versions/003_consumer_description_nullalbe.py
+++ b/keystone-moon/keystone/contrib/oauth1/migrate_repo/versions/003_consumer_description_nullalbe.py
@@ -20,10 +20,3 @@ def upgrade(migrate_engine):
meta.bind = migrate_engine
user_table = sql.Table('consumer', meta, autoload=True)
user_table.c.description.alter(nullable=True)
-
-
-def downgrade(migrate_engine):
- meta = sql.MetaData()
- meta.bind = migrate_engine
- user_table = sql.Table('consumer', meta, autoload=True)
- user_table.c.description.alter(nullable=False)
diff --git a/keystone-moon/keystone/contrib/oauth1/migrate_repo/versions/004_request_token_roles_nullable.py b/keystone-moon/keystone/contrib/oauth1/migrate_repo/versions/004_request_token_roles_nullable.py
index 6f1e2e81..6934eb6f 100644
--- a/keystone-moon/keystone/contrib/oauth1/migrate_repo/versions/004_request_token_roles_nullable.py
+++ b/keystone-moon/keystone/contrib/oauth1/migrate_repo/versions/004_request_token_roles_nullable.py
@@ -23,13 +23,3 @@ def upgrade(migrate_engine):
request_token_table.c.requested_roles.alter(name="role_ids")
access_token_table = sql.Table('access_token', meta, autoload=True)
access_token_table.c.requested_roles.alter(name="role_ids")
-
-
-def downgrade(migrate_engine):
- meta = sql.MetaData()
- meta.bind = migrate_engine
- request_token_table = sql.Table('request_token', meta, autoload=True)
- request_token_table.c.role_ids.alter(nullable=False)
- request_token_table.c.role_ids.alter(name="requested_roles")
- access_token_table = sql.Table('access_token', meta, autoload=True)
- access_token_table.c.role_ids.alter(name="requested_roles")
diff --git a/keystone-moon/keystone/contrib/oauth1/migrate_repo/versions/005_consumer_id_index.py b/keystone-moon/keystone/contrib/oauth1/migrate_repo/versions/005_consumer_id_index.py
index 428971f8..0627d21c 100644
--- a/keystone-moon/keystone/contrib/oauth1/migrate_repo/versions/005_consumer_id_index.py
+++ b/keystone-moon/keystone/contrib/oauth1/migrate_repo/versions/005_consumer_id_index.py
@@ -26,17 +26,10 @@ def upgrade(migrate_engine):
# indexes create automatically. That those indexes will have different
# names, depending on version of MySQL used. We shoud make this naming
# consistent, by reverting index name to a consistent condition.
- if any(i for i in table.indexes if i.columns.keys() == ['consumer_id']
+ if any(i for i in table.indexes if
+ list(i.columns.keys()) == ['consumer_id']
and i.name != 'consumer_id'):
# NOTE(i159): by this action will be made re-creation of an index
# with the new name. This can be considered as renaming under the
# MySQL rules.
sa.Index('consumer_id', table.c.consumer_id).create()
-
-
-def downgrade(migrate_engine):
- # NOTE(i159): index exists only in MySQL schemas, and got an inconsistent
- # name only when MySQL 5.5 renamed it after re-creation
- # (during migrations). So we just fixed inconsistency, there is no
- # necessity to revert it.
- pass
diff --git a/keystone-moon/keystone/contrib/oauth1/routers.py b/keystone-moon/keystone/contrib/oauth1/routers.py
index 35619ede..4b772eb5 100644
--- a/keystone-moon/keystone/contrib/oauth1/routers.py
+++ b/keystone-moon/keystone/contrib/oauth1/routers.py
@@ -44,17 +44,17 @@ class OAuth1Extension(wsgi.V3ExtensionRouter):
# 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
+ 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
+ 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
+ DELETE /users/{user_id}/OS-OAUTH1/access_tokens/{access_token_id}
# OAuth interfaces
POST /OS-OAUTH1/request_token # create a request token
diff --git a/keystone-moon/keystone/contrib/revoke/backends/kvs.py b/keystone-moon/keystone/contrib/revoke/backends/kvs.py
index cc41fbee..349ed6e3 100644
--- a/keystone-moon/keystone/contrib/revoke/backends/kvs.py
+++ b/keystone-moon/keystone/contrib/revoke/backends/kvs.py
@@ -13,12 +13,12 @@
import datetime
from oslo_config import cfg
+from oslo_log import versionutils
from oslo_utils import timeutils
from keystone.common import kvs
from keystone.contrib import revoke
from keystone import exception
-from keystone.openstack.common import versionutils
CONF = cfg.CONF
@@ -45,29 +45,30 @@ class Revoke(revoke.Driver):
except exception.NotFound:
return []
- def _prune_expired_events_and_get(self, last_fetch=None, new_event=None):
- pruned = []
+ def list_events(self, last_fetch=None):
results = []
+
+ with self._store.get_lock(_EVENT_KEY):
+ events = self._list_events()
+
+ for event in events:
+ revoked_at = event.revoked_at
+ if last_fetch is None or revoked_at > last_fetch:
+ results.append(event)
+ return results
+
+ def revoke(self, event):
+ pruned = []
expire_delta = datetime.timedelta(seconds=CONF.token.expiration)
oldest = timeutils.utcnow() - expire_delta
- # TODO(ayoung): Store the time of the oldest event so that the
- # prune process can be skipped if none of the events have timed out.
+
with self._store.get_lock(_EVENT_KEY) as lock:
events = self._list_events()
- if new_event is not None:
- events.append(new_event)
+ if event:
+ events.append(event)
for event in events:
revoked_at = event.revoked_at
if revoked_at > oldest:
pruned.append(event)
- if last_fetch is None or revoked_at > last_fetch:
- results.append(event)
self._store.set(_EVENT_KEY, pruned, lock)
- return results
-
- def list_events(self, last_fetch=None):
- return self._prune_expired_events_and_get(last_fetch=last_fetch)
-
- def revoke(self, event):
- self._prune_expired_events_and_get(new_event=event)
diff --git a/keystone-moon/keystone/contrib/revoke/backends/sql.py b/keystone-moon/keystone/contrib/revoke/backends/sql.py
index 1b0cde1e..dd7fdd19 100644
--- a/keystone-moon/keystone/contrib/revoke/backends/sql.py
+++ b/keystone-moon/keystone/contrib/revoke/backends/sql.py
@@ -33,7 +33,7 @@ class RevocationEvent(sql.ModelBase, sql.ModelDictMixin):
access_token_id = sql.Column(sql.String(64))
issued_before = sql.Column(sql.DateTime(), nullable=False)
expires_at = sql.Column(sql.DateTime())
- revoked_at = sql.Column(sql.DateTime(), nullable=False)
+ revoked_at = sql.Column(sql.DateTime(), nullable=False, index=True)
audit_id = sql.Column(sql.String(32))
audit_chain_id = sql.Column(sql.String(32))
@@ -81,7 +81,6 @@ class Revoke(revoke.Driver):
session.flush()
def list_events(self, last_fetch=None):
- self._prune_expired_events()
session = sql.get_session()
query = session.query(RevocationEvent).order_by(
RevocationEvent.revoked_at)
@@ -102,3 +101,4 @@ class Revoke(revoke.Driver):
session = sql.get_session()
with session.begin():
session.add(record)
+ self._prune_expired_events()
diff --git a/keystone-moon/keystone/contrib/revoke/core.py b/keystone-moon/keystone/contrib/revoke/core.py
index c7335690..e1ab87c8 100644
--- a/keystone-moon/keystone/contrib/revoke/core.py
+++ b/keystone-moon/keystone/contrib/revoke/core.py
@@ -10,11 +10,14 @@
# License for the specific language governing permissions and limitations
# under the License.
+"""Main entry point into the Revoke service."""
+
import abc
import datetime
from oslo_config import cfg
from oslo_log import log
+from oslo_log import versionutils
from oslo_utils import timeutils
import six
@@ -26,7 +29,6 @@ from keystone.contrib.revoke import model
from keystone import exception
from keystone.i18n import _
from keystone import notifications
-from keystone.openstack.common import versionutils
CONF = cfg.CONF
@@ -64,12 +66,17 @@ def revoked_before_cutoff_time():
@dependency.provider('revoke_api')
class Manager(manager.Manager):
- """Revoke API Manager.
+ """Default pivot point for the Revoke backend.
Performs common logic for recording revocations.
+ See :mod:`keystone.common.manager.Manager` for more details on
+ how this dynamically calls the backend.
+
"""
+ driver_namespace = 'keystone.revoke'
+
def __init__(self):
super(Manager, self).__init__(CONF.revoke.driver)
self._register_listeners()
@@ -109,11 +116,12 @@ class Manager(manager.Manager):
self.revoke(
model.RevokeEvent(access_token_id=payload['resource_info']))
- def _group_callback(self, service, resource_type, operation, payload):
- user_ids = (u['id'] for u in self.identity_api.list_users_in_group(
- payload['resource_info']))
- for uid in user_ids:
- self.revoke(model.RevokeEvent(user_id=uid))
+ def _role_assignment_callback(self, service, resource_type, operation,
+ payload):
+ info = payload['resource_info']
+ self.revoke_by_grant(role_id=info['role_id'], user_id=info['user_id'],
+ domain_id=info.get('domain_id'),
+ project_id=info.get('project_id'))
def _register_listeners(self):
callbacks = {
@@ -124,6 +132,7 @@ class Manager(manager.Manager):
['role', self._role_callback],
['user', self._user_callback],
['project', self._project_callback],
+ ['role_assignment', self._role_assignment_callback]
],
notifications.ACTIONS.disabled: [
['user', self._user_callback],
@@ -136,7 +145,7 @@ class Manager(manager.Manager):
]
}
- for event, cb_info in six.iteritems(callbacks):
+ for event, cb_info in callbacks.items():
for resource_type, callback_fns in cb_info:
notifications.register_event_callback(event, resource_type,
callback_fns)
diff --git a/keystone-moon/keystone/contrib/revoke/migrate_repo/versions/001_revoke_table.py b/keystone-moon/keystone/contrib/revoke/migrate_repo/versions/001_revoke_table.py
index 7927ce0c..8b59010e 100644
--- a/keystone-moon/keystone/contrib/revoke/migrate_repo/versions/001_revoke_table.py
+++ b/keystone-moon/keystone/contrib/revoke/migrate_repo/versions/001_revoke_table.py
@@ -34,14 +34,3 @@ def upgrade(migrate_engine):
sql.Column('expires_at', sql.DateTime()),
sql.Column('revoked_at', sql.DateTime(), index=True, nullable=False))
service_table.create(migrate_engine, checkfirst=True)
-
-
-def downgrade(migrate_engine):
- # Operations to reverse the above upgrade go here.
- meta = sql.MetaData()
- meta.bind = migrate_engine
-
- tables = ['revocation_event']
- for t in tables:
- table = sql.Table(t, meta, autoload=True)
- table.drop(migrate_engine, checkfirst=True)
diff --git a/keystone-moon/keystone/contrib/revoke/migrate_repo/versions/002_add_audit_id_and_chain_to_revoke_table.py b/keystone-moon/keystone/contrib/revoke/migrate_repo/versions/002_add_audit_id_and_chain_to_revoke_table.py
index bee6fb2a..b6d821d7 100644
--- a/keystone-moon/keystone/contrib/revoke/migrate_repo/versions/002_add_audit_id_and_chain_to_revoke_table.py
+++ b/keystone-moon/keystone/contrib/revoke/migrate_repo/versions/002_add_audit_id_and_chain_to_revoke_table.py
@@ -26,12 +26,3 @@ def upgrade(migrate_engine):
nullable=True)
event_table.create_column(audit_id_column)
event_table.create_column(audit_chain_column)
-
-
-def downgrade(migrate_engine):
- meta = sql.MetaData()
- meta.bind = migrate_engine
-
- event_table = sql.Table(_TABLE_NAME, meta, autoload=True)
- event_table.drop_column('audit_id')
- event_table.drop_column('audit_chain_id')
diff --git a/keystone-moon/keystone/contrib/revoke/model.py b/keystone-moon/keystone/contrib/revoke/model.py
index 5e92042d..1a23d57d 100644
--- a/keystone-moon/keystone/contrib/revoke/model.py
+++ b/keystone-moon/keystone/contrib/revoke/model.py
@@ -11,6 +11,9 @@
# under the License.
from oslo_utils import timeutils
+from six.moves import map
+
+from keystone.common import utils
# The set of attributes common between the RevokeEvent
@@ -43,6 +46,15 @@ _TOKEN_KEYS = ['identity_domain_id',
'trustor_id',
'trustee_id']
+# Alternative names to be checked in token for every field in
+# revoke tree.
+ALTERNATIVES = {
+ 'user_id': ['user_id', 'trustor_id', 'trustee_id'],
+ 'domain_id': ['identity_domain_id', 'assignment_domain_id'],
+ # For a domain-scoped token, the domain is in assignment_domain_id.
+ 'domain_scope_id': ['assignment_domain_id', ],
+}
+
REVOKE_KEYS = _NAMES + _EVENT_ARGS
@@ -100,10 +112,10 @@ class RevokeEvent(object):
if self.consumer_id is not None:
event['OS-OAUTH1:access_token_id'] = self.access_token_id
if self.expires_at is not None:
- event['expires_at'] = timeutils.isotime(self.expires_at)
+ event['expires_at'] = utils.isotime(self.expires_at)
if self.issued_before is not None:
- event['issued_before'] = timeutils.isotime(self.issued_before,
- subsecond=True)
+ event['issued_before'] = utils.isotime(self.issued_before,
+ subsecond=True)
return event
def key_for_name(self, name):
@@ -111,7 +123,7 @@ class RevokeEvent(object):
def attr_keys(event):
- return map(event.key_for_name, _EVENT_NAMES)
+ return list(map(event.key_for_name, _EVENT_NAMES))
class RevokeTree(object):
@@ -176,7 +188,52 @@ class RevokeTree(object):
del parent[key]
def add_events(self, revoke_events):
- return map(self.add_event, revoke_events or [])
+ return list(map(self.add_event, revoke_events or []))
+
+ @staticmethod
+ def _next_level_keys(name, token_data):
+ """Generate keys based on current field name and token data
+
+ Generate all keys to look for in the next iteration of revocation
+ event tree traversal.
+ """
+ yield '*'
+ if name == 'role_id':
+ # Roles are very special since a token has a list of them.
+ # If the revocation event matches any one of them,
+ # revoke the token.
+ for role_id in token_data.get('roles', []):
+ yield role_id
+ else:
+ # For other fields we try to get any branch that concur
+ # with any alternative field in the token.
+ for alt_name in ALTERNATIVES.get(name, [name]):
+ yield token_data[alt_name]
+
+ def _search(self, revoke_map, names, token_data):
+ """Search for revocation event by token_data
+
+ Traverse the revocation events tree looking for event matching token
+ data issued after the token.
+ """
+ if not names:
+ # The last (leaf) level is checked in a special way because we
+ # verify issued_at field differently.
+ try:
+ return revoke_map['issued_before'] > token_data['issued_at']
+ except KeyError:
+ return False
+
+ name, remaining_names = names[0], names[1:]
+
+ for key in self._next_level_keys(name, token_data):
+ subtree = revoke_map.get('%s=%s' % (name, key))
+ if subtree and self._search(subtree, remaining_names, token_data):
+ return True
+
+ # If we made it out of the loop then no element in revocation tree
+ # corresponds to our token and it is good.
+ return False
def is_revoked(self, token_data):
"""Check if a token matches the revocation event
@@ -195,58 +252,7 @@ class RevokeTree(object):
'consumer_id', 'access_token_id'
"""
- # Alternative names to be checked in token for every field in
- # revoke tree.
- alternatives = {
- 'user_id': ['user_id', 'trustor_id', 'trustee_id'],
- 'domain_id': ['identity_domain_id', 'assignment_domain_id'],
- # For a domain-scoped token, the domain is in assignment_domain_id.
- 'domain_scope_id': ['assignment_domain_id', ],
- }
- # Contains current forest (collection of trees) to be checked.
- partial_matches = [self.revoke_map]
- # We iterate over every layer of our revoke tree (except the last one).
- for name in _EVENT_NAMES:
- # bundle is the set of partial matches for the next level down
- # the tree
- bundle = []
- wildcard = '%s=*' % (name,)
- # For every tree in current forest.
- for tree in partial_matches:
- # If there is wildcard node on current level we take it.
- bundle.append(tree.get(wildcard))
- if name == 'role_id':
- # Roles are very special since a token has a list of them.
- # If the revocation event matches any one of them,
- # revoke the token.
- for role_id in token_data.get('roles', []):
- bundle.append(tree.get('role_id=%s' % role_id))
- else:
- # For other fields we try to get any branch that concur
- # with any alternative field in the token.
- for alt_name in alternatives.get(name, [name]):
- bundle.append(
- tree.get('%s=%s' % (name, token_data[alt_name])))
- # tree.get returns `None` if there is no match, so `bundle.append`
- # adds a 'None' entry. This call remoes the `None` entries.
- partial_matches = [x for x in bundle if x is not None]
- if not partial_matches:
- # If we end up with no branches to follow means that the token
- # is definitely not in the revoke tree and all further
- # iterations will be for nothing.
- return False
-
- # The last (leaf) level is checked in a special way because we verify
- # issued_at field differently.
- for leaf in partial_matches:
- try:
- if leaf['issued_before'] > token_data['issued_at']:
- return True
- except KeyError:
- pass
- # If we made it out of the loop then no element in revocation tree
- # corresponds to our token and it is good.
- return False
+ return self._search(self.revoke_map, _EVENT_NAMES, token_data)
def build_token_values_v2(access, default_domain_id):
diff --git a/keystone-moon/keystone/contrib/s3/core.py b/keystone-moon/keystone/contrib/s3/core.py
index 34095bf4..d3e06acc 100644
--- a/keystone-moon/keystone/contrib/s3/core.py
+++ b/keystone-moon/keystone/contrib/s3/core.py
@@ -25,6 +25,8 @@ import base64
import hashlib
import hmac
+import six
+
from keystone.common import extension
from keystone.common import json_home
from keystone.common import utils
@@ -32,6 +34,7 @@ from keystone.common import wsgi
from keystone.contrib.ec2 import controllers
from keystone import exception
+
EXTENSION_DATA = {
'name': 'OpenStack S3 API',
'namespace': 'http://docs.openstack.org/identity/api/ext/'
@@ -65,9 +68,15 @@ class S3Extension(wsgi.V3ExtensionRouter):
class S3Controller(controllers.Ec2Controller):
def check_signature(self, creds_ref, credentials):
msg = base64.urlsafe_b64decode(str(credentials['token']))
- key = str(creds_ref['secret'])
- signed = base64.encodestring(
- hmac.new(key, msg, hashlib.sha1).digest()).strip()
+ key = str(creds_ref['secret']).encode('utf-8')
+
+ if six.PY2:
+ b64_encode = base64.encodestring
+ else:
+ b64_encode = base64.encodebytes
+
+ signed = b64_encode(
+ hmac.new(key, msg, hashlib.sha1).digest()).decode('utf-8').strip()
if not utils.auth_str_equal(credentials['signature'], signed):
raise exception.Unauthorized('Credential signature mismatch')