Skip to content

Commit

Permalink
Add HP-IDM extension to fix Bug 890411
Browse files Browse the repository at this point in the history
  • Loading branch information
Guang Yee authored and ziadsawalha committed Dec 29, 2011
1 parent f9ce911 commit fafc25b
Show file tree
Hide file tree
Showing 30 changed files with 703 additions and 44 deletions.
4 changes: 2 additions & 2 deletions doc/source/controllingservers.rst
Expand Up @@ -124,7 +124,7 @@ Here is an example showing how you can manually start the ``keystone-auth`` serv
keystone-legacy-auth: INFO certfile /etc/keystone/ssl/certs/keystone.pem
keystone-legacy-auth: INFO debug True
keystone-legacy-auth: INFO default_store sqlite
keystone-legacy-auth: INFO extensions osksadm,oskscatalog
keystone-legacy-auth: INFO extensions osksadm,oskscatalog,hpidm
keystone-legacy-auth: INFO hash-password True
keystone-legacy-auth: INFO keyfile /etc/keystone/ssl/private/keystonekey.pem
keystone-legacy-auth: INFO keystone-admin-role Admin
Expand Down Expand Up @@ -156,7 +156,7 @@ Here is an example showing how you can manually start the ``keystone-auth`` serv
admin : INFO certfile /etc/keystone/ssl/certs/keystone.pem
admin : INFO debug True
admin : INFO default_store sqlite
admin : INFO extensions osksadm,oskscatalog
admin : INFO extensions osksadm,oskscatalog,hpidm
admin : INFO hash-password True
admin : INFO keyfile /etc/keystone/ssl/private/keystonekey.pem
admin : INFO keystone-admin-role Admin
Expand Down
9 changes: 9 additions & 0 deletions doc/source/extensions.rst
Expand Up @@ -71,6 +71,15 @@ RAX-KEY

This is an Admin and Service API extension.

HP-IDM

This extension adds capability to filter roles with optional service IDs
for token validation to mitigate security risks with role name conflicts.
See https://bugs.launchpad.net/keystone/+bug/890411 for more details.

This is an Admin API extension. Applicable to validate token (GET)
and check token (HEAD) APIs only.

.. note::

The included extensions are in the process of being rewritten. Currently
Expand Down
14 changes: 13 additions & 1 deletion etc/keystone.conf
Expand Up @@ -28,7 +28,7 @@ service-header-mappings = {

#List of extensions currently loaded.
#Refer docs for list of supported extensions.
extensions= osksadm,oskscatalog
extensions= osksadm, oskscatalog, hpidm

# Address to bind the API server
# TODO Properties defined within app not available via pipeline.
Expand Down Expand Up @@ -74,6 +74,18 @@ keystone-service-admin-role = KeystoneServiceAdmin
#Tells whether password user need to be hashed in the backend
hash-password = True

# This property is applicable to hpidm extension only.
# It will be ignored if hpidm extension is disabled.
#
# Specify the global service ID to dictate how the global roles
# are to be returned/processed in validate token call. Notice
# that middle-ware or API clients must specify the exact same
# global service ID in order for Keystone to retrieve the
# global roles in validate token call. Otherwise, it will
# likely result in a 401 since the mismatched global ID
# may not exist in Keystone and therefore considered invalid.
global_service_id = global

[keystone.backends.sqlalchemy]
# SQLAlchemy connection string for the reference implementation registry
# server. Any valid SQLAlchemy connection string is fine.
Expand Down
2 changes: 1 addition & 1 deletion etc/ldap.conf
Expand Up @@ -14,7 +14,7 @@ default_store = sqlite
log_file = keystone.ldap.log
log_dir = .
backends = keystone.backends.sqlalchemy,keystone.backends.ldap
extensions= osksadm,oskscatalog
extensions= osksadm, oskscatalog, hpidm
service-header-mappings = {
'nova' : 'X-Server-Management-Url',
'swift' : 'X-Storage-Url',
Expand Down
2 changes: 1 addition & 1 deletion etc/memcache.conf
Expand Up @@ -23,7 +23,7 @@ default_store = sqlite
log_file = keystone.memcache.log
log_dir = .
backends = keystone.backends.sqlalchemy,keystone.backends.memcache
extensions= osksadm,oskscatalog
extensions= osksadm, oskscatalog, hpidm
service-header-mappings = {
'nova' : 'X-Server-Management-Url',
'swift' : 'X-Storage-Url',
Expand Down
1 change: 1 addition & 0 deletions etc/ssl.conf
Expand Up @@ -21,6 +21,7 @@ default_store = sqlite
log_file = keystone.ssl.log
log_dir = .
backends = keystone.backends.sqlalchemy
extensions= osksadm, oskscatalog, hpidm
service-header-mappings = {
'nova' : 'X-Server-Management-Url',
'swift' : 'X-Storage-Url',
Expand Down
4 changes: 4 additions & 0 deletions keystone/backends/__init__.py
Expand Up @@ -23,6 +23,7 @@

#Configs applicable to all backends.
SHOULD_HASH_PASSWORD = None
GLOBAL_SERVICE_ID = None # to facilitate global roles for validate tokens


def configure_backends(options):
Expand All @@ -38,3 +39,6 @@ def configure_backends(options):
if "hash-password" in options\
and ast.literal_eval(options["hash-password"]) == True:
SHOULD_HASH_PASSWORD = options["hash-password"]

global GLOBAL_SERVICE_ID
GLOBAL_SERVICE_ID = options.get("global_service_id", "global")
6 changes: 6 additions & 0 deletions keystone/backends/ldap/fakeldap.py
Expand Up @@ -93,6 +93,12 @@ def _match(key, value, attrs):
# This is a wild card search. Implemented as all or nothing for now.
if value == "*":
return True
if key == 'serviceId':
# for serviceId, the backend is returning a list of numbers
# make sure we convert them to strings first before comparing
# them
str_sids = map(lambda x: str(x), attrs[key])
return str(value) in str_sids
if key != "objectclass":
return value in attrs[key]
# it is an objectclass check, so check subclasses
Expand Down
33 changes: 33 additions & 0 deletions keystone/contrib/extensions/admin/hpidm/__init__.py
@@ -0,0 +1,33 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4

# Copyright 2010 OpenStack LLC.
# All Rights Reserved.
#
# 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.extensions.admin.extension import BaseExtensionHandler
from keystone.controllers.token import TokenController


class ExtensionHandler(BaseExtensionHandler):
def map_extension_methods(self, mapper, options):
token_controller = TokenController(options)

# Token Operations
mapper.connect("/tokens/{token_id}", controller=token_controller,
action="validate_token",
conditions=dict(method=["GET"]))
mapper.connect("/tokens/{tenant_id}",
controller=token_controller,
action="check_token", conditions=dict(method=["HEAD"]))
21 changes: 21 additions & 0 deletions keystone/contrib/extensions/admin/hpidm/extension.json
@@ -0,0 +1,21 @@
{
"extension": {
"name": "HP Token Validation Extension",
"namespace": "http://docs.openstack.org/identity/api/ext/HP-IDM/v1.0",
"alias": "HP-IDM",
"updated": "2011-12-06T19:00:00-00:00",
"description": "Validate token with the optional serviceId parameter so that only the roles associated with the given service IDs are returned. See https://bugs.launchpad.net/keystone/+bug/890411 for more details.",
"links": [
{
"rel": "describedby",
"type": "application/pdf",
"href": "https://github.com/openstack/keystone/raw/master/keystone/content/admin/HP-IDM-admin-devguide.pdf"
},
{
"rel": "describedby",
"type": "application/vnd.sun.wadl+xml",
"href": "https://raw.github.com/openstack/keystone/master/keystone/content/admin/HP-IDM-admin.wadl"
}
]
}
}
21 changes: 21 additions & 0 deletions keystone/contrib/extensions/admin/hpidm/extension.xml
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension
xmlns="http://docs.openstack.org/common/api/v1.0"
xmlns:atom="http://www.w3.org/2005/Atom"
name="HP Token Validation Extension"
namespace="http://docs.openstack.org/identity/api/ext/HP-IDM/v1.0"
alias="HP-IDM"
updated="2011-12-25T17:00:00-00:00">

<description>
Validate token with the optional serviceId parameter so that only the roles associated with the given service IDs are returned. See https://bugs.launchpad.net/keystone/+bug/890411 for more details.
</description>

<atom:link rel="describedby"
type="application/pdf"
href="https://github.com/openstack/keystone/raw/master/keystone/content/admin/HP-IDM-admin-devguide.pdf"/>
<atom:link rel="describedby"
type="application/vnd.sun.wadl+xml"
href="https://github.com/openstack/keystone/raw/master/keystone/content/admin/HP-IDM-admin.wadl"/>
</extension>

7 changes: 6 additions & 1 deletion keystone/controllers/token.py
Expand Up @@ -26,6 +26,7 @@

from keystone import utils
from keystone.common import wsgi
from keystone.logic import extension_reader
from keystone.logic.types import auth
from keystone.logic.types import fault
from keystone.logic import service
Expand Down Expand Up @@ -68,8 +69,12 @@ def authenticate_ec2(self, req):
def _validate_token(self, req, token_id):
"""Validates the token, and that it belongs to the specified tenant"""
belongs_to = req.GET.get('belongsTo')
service_ids = None
if extension_reader.is_extension_supported(self.options, 'hpidm'):
# service IDs are only relevant if hpidm extension is enabled
service_ids = req.GET.get('HP-IDM-serviceId')
return self.identity_service.validate_token(
utils.get_auth_token(req), token_id, belongs_to)
utils.get_auth_token(req), token_id, belongs_to, service_ids)

@utils.wrap_error
def validate_token(self, req, token_id):
Expand Down
26 changes: 24 additions & 2 deletions keystone/logic/extension_reader.py
Expand Up @@ -10,6 +10,29 @@
EXTENSIONS_PATH = 'contrib/extensions'


def get_supported_extensions(options):
"""
Returns list of supported extensions.
options - global configuration options
"""

return [extension.strip() for extension in
options.get(CONFIG_EXTENSION_PROPERTY,
DEFAULT_EXTENSIONS).split(',')]


def is_extension_supported(options, extension_name):
"""
Return True if the extension is enabled, False otherwise.
options - global configuration options
extension_name - extension name
extension_name is case-sensitive.
"""
if (extension_name is not None) and (options is not None):
return extension_name in get_supported_extensions(options)
return False


class ExtensionsReader(object):
"""Reader to read static extensions content"""

Expand Down Expand Up @@ -75,8 +98,7 @@ def __get_all_xml_extensions(self):
def __get_supported_extensions(self):
""" Returns list of supported extensions."""
if self.supported_extensions is None:
self.supported_extensions = self.options.get(
CONFIG_EXTENSION_PROPERTY, DEFAULT_EXTENSIONS).split(',')
self.supported_extensions = get_supported_extensions(self.options)
return self.supported_extensions

def __get_extension_json(self, extension_name):
Expand Down

0 comments on commit fafc25b

Please sign in to comment.