Skip to content

Commit b1663c9

Browse files
aaronorosenDean Troyer
authored andcommitted
Sync with oslo-incubator and add importutils
From oslo-incubator commit: c4bfdb94c25b4488da61d77184d97f8784f21a11 Change-Id: I81d1113d113faa609ab7713a0e04667b11786247
1 parent 1eb7aba commit b1663c9

File tree

4 files changed

+171
-43
lines changed

4 files changed

+171
-43
lines changed

openstack-common.conf

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
[DEFAULT]
22

33
# The list of modules to copy from openstack-common
4+
module=gettextutils
45
module=install_venv_common
6+
module=importutils
57
module=strutils
68

79
# The base module to hold the copy of openstack.common

openstackclient/openstack/common/gettextutils.py

Lines changed: 24 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
"""
2424

2525
import copy
26-
import functools
2726
import gettext
2827
import locale
2928
from logging import handlers
@@ -42,7 +41,7 @@ class TranslatorFactory(object):
4241
"""Create translator functions
4342
"""
4443

45-
def __init__(self, domain, lazy=False, localedir=None):
44+
def __init__(self, domain, localedir=None):
4645
"""Establish a set of translation functions for the domain.
4746
4847
:param domain: Name of translation domain,
@@ -55,7 +54,6 @@ def __init__(self, domain, lazy=False, localedir=None):
5554
:type localedir: str
5655
"""
5756
self.domain = domain
58-
self.lazy = lazy
5957
if localedir is None:
6058
localedir = os.environ.get(domain.upper() + '_LOCALEDIR')
6159
self.localedir = localedir
@@ -75,16 +73,19 @@ def _make_translation_func(self, domain=None):
7573
"""
7674
if domain is None:
7775
domain = self.domain
78-
if self.lazy:
79-
return functools.partial(Message, domain=domain)
80-
t = gettext.translation(
81-
domain,
82-
localedir=self.localedir,
83-
fallback=True,
84-
)
85-
if six.PY3:
86-
return t.gettext
87-
return t.ugettext
76+
t = gettext.translation(domain,
77+
localedir=self.localedir,
78+
fallback=True)
79+
# Use the appropriate method of the translation object based
80+
# on the python version.
81+
m = t.gettext if six.PY3 else t.ugettext
82+
83+
def f(msg):
84+
"""oslo.i18n.gettextutils translation function."""
85+
if USE_LAZY:
86+
return Message(msg, domain=domain)
87+
return m(msg)
88+
return f
8889

8990
@property
9091
def primary(self):
@@ -147,19 +148,11 @@ def enable_lazy():
147148
your project is importing _ directly instead of using the
148149
gettextutils.install() way of importing the _ function.
149150
"""
150-
# FIXME(dhellmann): This function will be removed in oslo.i18n,
151-
# because the TranslatorFactory makes it superfluous.
152-
global _, _LI, _LW, _LE, _LC, USE_LAZY
153-
tf = TranslatorFactory('openstackclient', lazy=True)
154-
_ = tf.primary
155-
_LI = tf.log_info
156-
_LW = tf.log_warning
157-
_LE = tf.log_error
158-
_LC = tf.log_critical
151+
global USE_LAZY
159152
USE_LAZY = True
160153

161154

162-
def install(domain, lazy=False):
155+
def install(domain):
163156
"""Install a _() function using the given translation domain.
164157
165158
Given a translation domain, install a _() function using gettext's
@@ -170,26 +163,14 @@ def install(domain, lazy=False):
170163
a translation-domain-specific environment variable (e.g.
171164
NOVA_LOCALEDIR).
172165
166+
Note that to enable lazy translation, enable_lazy must be
167+
called.
168+
173169
:param domain: the translation domain
174-
:param lazy: indicates whether or not to install the lazy _() function.
175-
The lazy _() introduces a way to do deferred translation
176-
of messages by installing a _ that builds Message objects,
177-
instead of strings, which can then be lazily translated into
178-
any available locale.
179170
"""
180-
if lazy:
181-
from six import moves
182-
tf = TranslatorFactory(domain, lazy=True)
183-
moves.builtins.__dict__['_'] = tf.primary
184-
else:
185-
localedir = '%s_LOCALEDIR' % domain.upper()
186-
if six.PY3:
187-
gettext.install(domain,
188-
localedir=os.environ.get(localedir))
189-
else:
190-
gettext.install(domain,
191-
localedir=os.environ.get(localedir),
192-
unicode=True)
171+
from six import moves
172+
tf = TranslatorFactory(domain)
173+
moves.builtins.__dict__['_'] = tf.primary
193174

194175

195176
class Message(six.text_type):
@@ -373,8 +354,8 @@ def get_available_languages(domain):
373354
'zh_Hant_HK': 'zh_HK',
374355
'zh_Hant': 'zh_TW',
375356
'fil': 'tl_PH'}
376-
for (locale, alias) in six.iteritems(aliases):
377-
if locale in language_list and alias not in language_list:
357+
for (locale_, alias) in six.iteritems(aliases):
358+
if locale_ in language_list and alias not in language_list:
378359
language_list.append(alias)
379360

380361
_AVAILABLE_LANGUAGES[domain] = language_list
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Copyright 2011 OpenStack Foundation.
2+
# All Rights Reserved.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
5+
# not use this file except in compliance with the License. You may obtain
6+
# a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
# License for the specific language governing permissions and limitations
14+
# under the License.
15+
16+
"""
17+
Import related utilities and helper functions.
18+
"""
19+
20+
import sys
21+
import traceback
22+
23+
24+
def import_class(import_str):
25+
"""Returns a class from a string including module and class."""
26+
mod_str, _sep, class_str = import_str.rpartition('.')
27+
__import__(mod_str)
28+
try:
29+
return getattr(sys.modules[mod_str], class_str)
30+
except AttributeError:
31+
raise ImportError('Class %s cannot be found (%s)' %
32+
(class_str,
33+
traceback.format_exception(*sys.exc_info())))
34+
35+
36+
def import_object(import_str, *args, **kwargs):
37+
"""Import a class and return an instance of it."""
38+
return import_class(import_str)(*args, **kwargs)
39+
40+
41+
def import_object_ns(name_space, import_str, *args, **kwargs):
42+
"""Tries to import object from default namespace.
43+
44+
Imports a class and return an instance of it, first by trying
45+
to find the class in a default namespace, then failing back to
46+
a full path if not found in the default namespace.
47+
"""
48+
import_value = "%s.%s" % (name_space, import_str)
49+
try:
50+
return import_class(import_value)(*args, **kwargs)
51+
except ImportError:
52+
return import_class(import_str)(*args, **kwargs)
53+
54+
55+
def import_module(import_str):
56+
"""Import a module."""
57+
__import__(import_str)
58+
return sys.modules[import_str]
59+
60+
61+
def import_versioned_module(version, submodule=None):
62+
module = 'openstackclient.v%s' % version
63+
if submodule:
64+
module = '.'.join((module, submodule))
65+
return import_module(module)
66+
67+
68+
def try_import(import_str, default=None):
69+
"""Try to import a module and if it fails return default."""
70+
try:
71+
return import_module(import_str)
72+
except ImportError:
73+
return default

openstackclient/openstack/common/strutils.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,39 @@
5050
SLUGIFY_HYPHENATE_RE = re.compile(r"[-\s]+")
5151

5252

53+
# NOTE(flaper87): The following globals are used by `mask_password`
54+
_SANITIZE_KEYS = ['adminPass', 'admin_pass', 'password', 'admin_password']
55+
56+
# NOTE(ldbragst): Let's build a list of regex objects using the list of
57+
# _SANITIZE_KEYS we already have. This way, we only have to add the new key
58+
# to the list of _SANITIZE_KEYS and we can generate regular expressions
59+
# for XML and JSON automatically.
60+
_SANITIZE_PATTERNS_2 = []
61+
_SANITIZE_PATTERNS_1 = []
62+
63+
# NOTE(amrith): Some regular expressions have only one parameter, some
64+
# have two parameters. Use different lists of patterns here.
65+
_FORMAT_PATTERNS_1 = [r'(%(key)s\s*[=]\s*)[^\s^\'^\"]+']
66+
_FORMAT_PATTERNS_2 = [r'(%(key)s\s*[=]\s*[\"\']).*?([\"\'])',
67+
r'(%(key)s\s+[\"\']).*?([\"\'])',
68+
r'([-]{2}%(key)s\s+)[^\'^\"^=^\s]+([\s]*)',
69+
r'(<%(key)s>).*?(</%(key)s>)',
70+
r'([\"\']%(key)s[\"\']\s*:\s*[\"\']).*?([\"\'])',
71+
r'([\'"].*?%(key)s[\'"]\s*:\s*u?[\'"]).*?([\'"])',
72+
r'([\'"].*?%(key)s[\'"]\s*,\s*\'--?[A-z]+\'\s*,\s*u?'
73+
'[\'"]).*?([\'"])',
74+
r'(%(key)s\s*--?[A-z]+\s*)\S+(\s*)']
75+
76+
for key in _SANITIZE_KEYS:
77+
for pattern in _FORMAT_PATTERNS_2:
78+
reg_ex = re.compile(pattern % {'key': key}, re.DOTALL)
79+
_SANITIZE_PATTERNS_2.append(reg_ex)
80+
81+
for pattern in _FORMAT_PATTERNS_1:
82+
reg_ex = re.compile(pattern % {'key': key}, re.DOTALL)
83+
_SANITIZE_PATTERNS_1.append(reg_ex)
84+
85+
5386
def int_from_bool_as_string(subject):
5487
"""Interpret a string as a boolean and return either 1 or 0.
5588
@@ -237,3 +270,42 @@ def to_slug(value, incoming=None, errors="strict"):
237270
"ascii", "ignore").decode("ascii")
238271
value = SLUGIFY_STRIP_RE.sub("", value).strip().lower()
239272
return SLUGIFY_HYPHENATE_RE.sub("-", value)
273+
274+
275+
def mask_password(message, secret="***"):
276+
"""Replace password with 'secret' in message.
277+
278+
:param message: The string which includes security information.
279+
:param secret: value with which to replace passwords.
280+
:returns: The unicode value of message with the password fields masked.
281+
282+
For example:
283+
284+
>>> mask_password("'adminPass' : 'aaaaa'")
285+
"'adminPass' : '***'"
286+
>>> mask_password("'admin_pass' : 'aaaaa'")
287+
"'admin_pass' : '***'"
288+
>>> mask_password('"password" : "aaaaa"')
289+
'"password" : "***"'
290+
>>> mask_password("'original_password' : 'aaaaa'")
291+
"'original_password' : '***'"
292+
>>> mask_password("u'original_password' : u'aaaaa'")
293+
"u'original_password' : u'***'"
294+
"""
295+
message = six.text_type(message)
296+
297+
# NOTE(ldbragst): Check to see if anything in message contains any key
298+
# specified in _SANITIZE_KEYS, if not then just return the message since
299+
# we don't have to mask any passwords.
300+
if not any(key in message for key in _SANITIZE_KEYS):
301+
return message
302+
303+
substitute = r'\g<1>' + secret + r'\g<2>'
304+
for pattern in _SANITIZE_PATTERNS_2:
305+
message = re.sub(pattern, substitute, message)
306+
307+
substitute = r'\g<1>' + secret
308+
for pattern in _SANITIZE_PATTERNS_1:
309+
message = re.sub(pattern, substitute, message)
310+
311+
return message

0 commit comments

Comments
 (0)