summaryrefslogtreecommitdiffstats
path: root/keystone
diff options
context:
space:
mode:
Diffstat (limited to 'keystone')
-rw-r--r--keystone/common/wsgi.py37
-rw-r--r--keystone/tests/test_wsgi.py87
2 files changed, 113 insertions, 11 deletions
diff --git a/keystone/common/wsgi.py b/keystone/common/wsgi.py
index d515fde6..646bb4c4 100644
--- a/keystone/common/wsgi.py
+++ b/keystone/common/wsgi.py
@@ -29,6 +29,7 @@ import webob.exc
from keystone.common import config
from keystone.common import utils
from keystone import exception
+from keystone.openstack.common import gettextutils
from keystone.openstack.common import importutils
from keystone.openstack.common import jsonutils
from keystone.openstack.common import log as logging
@@ -123,7 +124,14 @@ def validate_token_bind(context, token_ref):
class Request(webob.Request):
- pass
+ def best_match_language(self):
+ """Determines the best available locale from the Accept-Language
+ HTTP header passed in the request.
+ """
+
+ return self.accept_language.best_match(
+ gettextutils.get_available_languages('keystone'),
+ default_match='en_US')
class BaseApplication(object):
@@ -231,16 +239,18 @@ class Application(BaseApplication):
LOG.warning(
_('Authorization failed. %(exception)s from %(remote_addr)s') %
{'exception': e, 'remote_addr': req.environ['REMOTE_ADDR']})
- return render_exception(e)
+ return render_exception(e, user_locale=req.best_match_language())
except exception.Error as e:
LOG.warning(e)
- return render_exception(e)
+ return render_exception(e, user_locale=req.best_match_language())
except TypeError as e:
LOG.exception(e)
- return render_exception(exception.ValidationError(e))
+ return render_exception(exception.ValidationError(e),
+ user_locale=req.best_match_language())
except Exception as e:
LOG.exception(e)
- return render_exception(exception.UnexpectedError(exception=e))
+ return render_exception(exception.UnexpectedError(exception=e),
+ user_locale=req.best_match_language())
if result is None:
return render_response(status=(204, 'No Content'))
@@ -364,13 +374,16 @@ class Middleware(Application):
return self.process_response(request, response)
except exception.Error as e:
LOG.warning(e)
- return render_exception(e)
+ return render_exception(e,
+ user_locale=request.best_match_language())
except TypeError as e:
LOG.exception(e)
- return render_exception(exception.ValidationError(e))
+ return render_exception(exception.ValidationError(e),
+ user_locale=request.best_match_language())
except Exception as e:
LOG.exception(e)
- return render_exception(exception.UnexpectedError(exception=e))
+ return render_exception(exception.UnexpectedError(exception=e),
+ user_locale=request.best_match_language())
class Debug(Middleware):
@@ -472,7 +485,8 @@ class Router(object):
match = req.environ['wsgiorg.routing_args'][1]
if not match:
return render_exception(
- exception.NotFound(_('The resource could not be found.')))
+ exception.NotFound(_('The resource could not be found.')),
+ user_locale=req.best_match_language())
app = match['controller']
return app
@@ -566,12 +580,13 @@ def render_response(body=None, status=None, headers=None):
headerlist=headers)
-def render_exception(error):
+def render_exception(error, user_locale=None):
"""Forms a WSGI response based on the current error."""
body = {'error': {
'code': error.code,
'title': error.title,
- 'message': unicode(error)
+ 'message': unicode(gettextutils.get_localized_message(error.args[0],
+ user_locale)),
}}
if isinstance(error, exception.AuthPluginException):
body['error']['identity'] = error.authentication
diff --git a/keystone/tests/test_wsgi.py b/keystone/tests/test_wsgi.py
index 781159e2..0dfa9467 100644
--- a/keystone/tests/test_wsgi.py
+++ b/keystone/tests/test_wsgi.py
@@ -14,8 +14,14 @@
# License for the specific language governing permissions and limitations
# under the License.
+import uuid
+
+from babel import localedata
+import gettext
+
from keystone.common import wsgi
from keystone import exception
+from keystone.openstack.common import gettextutils
from keystone.openstack.common import jsonutils
from keystone.tests import core as test
@@ -211,3 +217,84 @@ class WSGIFunctionTest(test.TestCase):
message = 'test = "param1" : "value"'
self.assertEqual(wsgi.mask_password(message),
'test = "param1" : "value"')
+
+
+class LocalizedResponseTest(test.TestCase):
+ def setUp(self):
+ super(LocalizedResponseTest, self).setUp()
+ gettextutils._AVAILABLE_LANGUAGES = []
+
+ def tearDown(self):
+ gettextutils._AVAILABLE_LANGUAGES = []
+ super(LocalizedResponseTest, self).tearDown()
+
+ def _set_expected_languages(self, all_locales=[], avail_locales=None):
+ # Override localedata.locale_identifiers to return some locales.
+ def returns_some_locales(*args, **kwargs):
+ return all_locales
+
+ self.stubs.Set(localedata, 'locale_identifiers', returns_some_locales)
+
+ # Override gettext.find to return other than None for some languages.
+ def fake_gettext_find(lang_id, *args, **kwargs):
+ found_ret = '/keystone/%s/LC_MESSAGES/keystone.mo' % lang_id
+ if avail_locales is None:
+ # All locales are available.
+ return found_ret
+ languages = kwargs['languages']
+ if languages[0] in avail_locales:
+ return found_ret
+ return None
+
+ self.stubs.Set(gettext, 'find', fake_gettext_find)
+
+ def test_request_match_default(self):
+ # The default language if no Accept-Language is provided is en_US
+ req = wsgi.Request.blank('/')
+ self.assertEquals(req.best_match_language(), 'en_US')
+
+ def test_request_match_language_expected(self):
+ # If Accept-Language is a supported language, best_match_language()
+ # returns it.
+
+ self._set_expected_languages(all_locales=['it'])
+
+ req = wsgi.Request.blank('/', headers={'Accept-Language': 'it'})
+ self.assertEquals(req.best_match_language(), 'it')
+
+ def test_request_match_language_unexpected(self):
+ # If Accept-Language is a language we do not support,
+ # best_match_language() returns the default.
+
+ self._set_expected_languages(all_locales=['it'])
+
+ req = wsgi.Request.blank('/', headers={'Accept-Language': 'zh'})
+ self.assertEquals(req.best_match_language(), 'en_US')
+
+ def test_localized_message(self):
+ # If the accept-language header is set on the request, the localized
+ # message is returned by calling get_localized_message.
+
+ LANG_ID = uuid.uuid4().hex
+ ORIGINAL_TEXT = uuid.uuid4().hex
+ TRANSLATED_TEXT = uuid.uuid4().hex
+
+ self._set_expected_languages(all_locales=[LANG_ID])
+
+ def fake_get_localized_message(message, user_locale):
+ if (user_locale == LANG_ID and
+ message == ORIGINAL_TEXT):
+ return TRANSLATED_TEXT
+
+ self.stubs.Set(gettextutils, 'get_localized_message',
+ fake_get_localized_message)
+
+ error = exception.NotFound(message=ORIGINAL_TEXT)
+ resp = wsgi.render_exception(error, user_locale=LANG_ID)
+ result = jsonutils.loads(resp.body)
+
+ exp = {'error': {'message': TRANSLATED_TEXT,
+ 'code': 404,
+ 'title': 'Not Found'}}
+
+ self.assertEqual(exp, result)