diff --git a/keystone/config.py b/keystone/config.py index d9ffe23..8080292 100644 --- a/keystone/config.py +++ b/keystone/config.py @@ -143,7 +143,7 @@ register_str('bind_host', default='0.0.0.0') register_str('compute_port') register_str('admin_port') register_str('public_port') - +register_int('max_param_size', default=64) # sql options register_str('connection', group='sql') diff --git a/keystone/exception.py b/keystone/exception.py index f1651bf..f9ca96b 100644 --- a/keystone/exception.py +++ b/keystone/exception.py @@ -51,6 +51,19 @@ class ValidationError(Error): title = 'Bad Request' +class ValidationSizeError(Error): + """Request attribute %(attribute)s must be less than or equal to %(size)i. + + The server could not comply with the request because the attribute + size is invalid (too large). + + The client is assumed to be in error. + + """ + code = 400 + title = 'Bad Request' + + class Unauthorized(Error): """The request you have made requires authentication.""" code = 401 diff --git a/keystone/service.py b/keystone/service.py index 830849e..7050ea3 100644 --- a/keystone/service.py +++ b/keystone/service.py @@ -19,6 +19,7 @@ import uuid import routes from keystone import catalog +from keystone import config from keystone import exception from keystone import identity from keystone import policy @@ -28,6 +29,7 @@ from keystone.common import utils from keystone.common import wsgi +CONF = config.CONF LOG = logging.getLogger(__name__) @@ -251,10 +253,22 @@ class TokenController(wsgi.Application): token_id = uuid.uuid4().hex if 'passwordCredentials' in auth: username = auth['passwordCredentials'].get('username', '') + if len(username) > CONF.max_param_size: + raise exception.ValidationSizeError(attribute='username', + size=CONF.max_param_size) password = auth['passwordCredentials'].get('password', '') + max_pw_size = utils.MAX_PASSWORD_LENGTH + if len(password) > max_pw_size: + raise exception.ValidationSizeError(attribute='password', + size=max_pw_size) tenant_name = auth.get('tenantName', None) - + if tenant_name and len(tenant_name) > CONF.max_param_size: + raise exception.ValidationSizeError(attribute='tenantName', + size=CONF.max_param_size) user_id = auth['passwordCredentials'].get('userId', None) + if user_id and len(user_id) > CONF.max_param_size: + raise exception.ValidationSizeError(attribute='userId', + size=CONF.max_param_size) if username: user_ref = self.identity_api.get_user_by_name( context=context, user_name=username) @@ -263,6 +277,12 @@ class TokenController(wsgi.Application): # more compat tenant_id = auth.get('tenantId', None) + if tenant_id and len(tenant_id) > CONF.max_param_size: + raise exception.ValidationSizeError(attribute='tenantId', + size=CONF.max_param_size) + if tenant_id and len(tenant_id) > CONF.max_param_size: + raise exception.ValidationSizeError(attribute='tenantId', + size=CONF.max_param_size) if tenant_name: tenant_ref = self.identity_api.get_tenant_by_name( context=context, tenant_name=tenant_name) @@ -304,9 +324,13 @@ class TokenController(wsgi.Application): elif 'token' in auth: token = auth['token'].get('id', None) - + if len(token) > CONF.max_param_size: + raise exception.ValidationSizeError(attribute='token', + size=CONF.max_param_size) tenant_name = auth.get('tenantName') - + if tenant_name and len(tenant_name) > CONF.max_param_size: + raise exception.ValidationSizeError(attribute='tenantName', + size=CONF.max_param_size) # more compat if tenant_name: tenant_ref = self.identity_api.get_tenant_by_name( diff --git a/tests/test_service.py b/tests/test_service.py new file mode 100644 index 0000000..80fa17f --- /dev/null +++ b/tests/test_service.py @@ -0,0 +1,98 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2012 Red Hat, Inc. +# +# 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 import config +from keystone import exception +from keystone import service +from keystone import test + +import default_fixtures + +CONF = config.CONF + + +def _build_user_auth(token=None, user_id=None, username=None, + password=None, tenant_id=None, tenant_name=None): + """Build auth dictionary. + + It will create an auth dictionary based on all the arguments + that it receives. + """ + auth_json = {} + if token is not None: + auth_json['token'] = token + if username or password: + auth_json['passwordCredentials'] = {} + if username is not None: + auth_json['passwordCredentials']['username'] = username + if user_id is not None: + auth_json['passwordCredentials']['userId'] = user_id + if password is not None: + auth_json['passwordCredentials']['password'] = password + if tenant_name is not None: + auth_json['tenantName'] = tenant_name + if tenant_id is not None: + auth_json['tenantId'] = tenant_id + return auth_json + + +class AuthTest(test.TestCase): + def setUp(self): + super(AuthTest, self).setUp() + + CONF.identity.driver = 'keystone.identity.backends.kvs.Identity' + self.load_backends() + self.load_fixtures(default_fixtures) + self.api = service.TokenController() + + def test_authenticate_user_id_too_large(self): + """Verify sending large 'userId' raises the right exception.""" + body_dict = _build_user_auth(user_id='0' * 65, username='FOO', + password='foo2') + self.assertRaises(exception.ValidationSizeError, self.api.authenticate, + {}, body_dict) + + def test_authenticate_username_too_large(self): + """Verify sending large 'username' raises the right exception.""" + body_dict = _build_user_auth(username='0' * 65, password='foo2') + self.assertRaises(exception.ValidationSizeError, self.api.authenticate, + {}, body_dict) + + def test_authenticate_tenant_id_too_large(self): + """Verify sending large 'tenantId' raises the right exception.""" + body_dict = _build_user_auth(username='FOO', password='foo2', + tenant_id='0' * 65) + self.assertRaises(exception.ValidationSizeError, self.api.authenticate, + {}, body_dict) + + def test_authenticate_tenant_name_too_large(self): + """Verify sending large 'tenantName' raises the right exception.""" + body_dict = _build_user_auth(username='FOO', password='foo2', + tenant_name='0' * 65) + self.assertRaises(exception.ValidationSizeError, self.api.authenticate, + {}, body_dict) + + def test_authenticate_token_too_large(self): + """Verify sending large 'token' raises the right exception.""" + body_dict = _build_user_auth(token={'id': '0' * 8193}) + self.assertRaises(exception.ValidationSizeError, self.api.authenticate, + {}, body_dict) + + def test_authenticate_password_too_large(self): + """Verify sending large 'password' raises the right exception.""" + body_dict = _build_user_auth(username='FOO', password='0' * 8193) + self.assertRaises(exception.ValidationSizeError, self.api.authenticate, + {}, body_dict)