OpenStack library utils
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

941 lines
41KB

  1. # -*- coding: utf-8 -*-
  2. # Copyright 2011 OpenStack Foundation.
  3. # All Rights Reserved.
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  6. # not use this file except in compliance with the License. You may obtain
  7. # a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  13. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  14. # License for the specific language governing permissions and limitations
  15. # under the License.
  16. import collections
  17. import copy
  18. import math
  19. import ddt
  20. import mock
  21. from oslotest import base as test_base
  22. import six
  23. import testscenarios
  24. from oslo_utils import strutils
  25. from oslo_utils import units
  26. load_tests = testscenarios.load_tests_apply_scenarios
  27. class StrUtilsTest(test_base.BaseTestCase):
  28. @mock.patch("six.text_type")
  29. def test_bool_bool_from_string_no_text(self, mock_text):
  30. self.assertTrue(strutils.bool_from_string(True))
  31. self.assertFalse(strutils.bool_from_string(False))
  32. self.assertEqual(0, mock_text.call_count)
  33. def test_bool_bool_from_string(self):
  34. self.assertTrue(strutils.bool_from_string(True))
  35. self.assertFalse(strutils.bool_from_string(False))
  36. def test_bool_bool_from_string_default(self):
  37. self.assertTrue(strutils.bool_from_string('', default=True))
  38. self.assertFalse(strutils.bool_from_string('wibble', default=False))
  39. def _test_bool_from_string(self, c):
  40. self.assertTrue(strutils.bool_from_string(c('true')))
  41. self.assertTrue(strutils.bool_from_string(c('TRUE')))
  42. self.assertTrue(strutils.bool_from_string(c('on')))
  43. self.assertTrue(strutils.bool_from_string(c('On')))
  44. self.assertTrue(strutils.bool_from_string(c('yes')))
  45. self.assertTrue(strutils.bool_from_string(c('YES')))
  46. self.assertTrue(strutils.bool_from_string(c('yEs')))
  47. self.assertTrue(strutils.bool_from_string(c('1')))
  48. self.assertTrue(strutils.bool_from_string(c('T')))
  49. self.assertTrue(strutils.bool_from_string(c('t')))
  50. self.assertTrue(strutils.bool_from_string(c('Y')))
  51. self.assertTrue(strutils.bool_from_string(c('y')))
  52. self.assertFalse(strutils.bool_from_string(c('false')))
  53. self.assertFalse(strutils.bool_from_string(c('FALSE')))
  54. self.assertFalse(strutils.bool_from_string(c('off')))
  55. self.assertFalse(strutils.bool_from_string(c('OFF')))
  56. self.assertFalse(strutils.bool_from_string(c('no')))
  57. self.assertFalse(strutils.bool_from_string(c('0')))
  58. self.assertFalse(strutils.bool_from_string(c('42')))
  59. self.assertFalse(strutils.bool_from_string(c(
  60. 'This should not be True')))
  61. self.assertFalse(strutils.bool_from_string(c('F')))
  62. self.assertFalse(strutils.bool_from_string(c('f')))
  63. self.assertFalse(strutils.bool_from_string(c('N')))
  64. self.assertFalse(strutils.bool_from_string(c('n')))
  65. # Whitespace should be stripped
  66. self.assertTrue(strutils.bool_from_string(c(' 1 ')))
  67. self.assertTrue(strutils.bool_from_string(c(' true ')))
  68. self.assertFalse(strutils.bool_from_string(c(' 0 ')))
  69. self.assertFalse(strutils.bool_from_string(c(' false ')))
  70. def test_bool_from_string(self):
  71. self._test_bool_from_string(lambda s: s)
  72. def test_unicode_bool_from_string(self):
  73. self._test_bool_from_string(six.text_type)
  74. self.assertFalse(strutils.bool_from_string(u'使用', strict=False))
  75. exc = self.assertRaises(ValueError, strutils.bool_from_string,
  76. u'使用', strict=True)
  77. expected_msg = (u"Unrecognized value '使用', acceptable values are:"
  78. u" '0', '1', 'f', 'false', 'n', 'no', 'off', 'on',"
  79. u" 't', 'true', 'y', 'yes'")
  80. self.assertEqual(expected_msg, six.text_type(exc))
  81. def test_other_bool_from_string(self):
  82. self.assertFalse(strutils.bool_from_string(None))
  83. self.assertFalse(strutils.bool_from_string(mock.Mock()))
  84. def test_int_bool_from_string(self):
  85. self.assertTrue(strutils.bool_from_string(1))
  86. self.assertFalse(strutils.bool_from_string(-1))
  87. self.assertFalse(strutils.bool_from_string(0))
  88. self.assertFalse(strutils.bool_from_string(2))
  89. def test_strict_bool_from_string(self):
  90. # None isn't allowed in strict mode
  91. exc = self.assertRaises(ValueError, strutils.bool_from_string, None,
  92. strict=True)
  93. expected_msg = ("Unrecognized value 'None', acceptable values are:"
  94. " '0', '1', 'f', 'false', 'n', 'no', 'off', 'on',"
  95. " 't', 'true', 'y', 'yes'")
  96. self.assertEqual(expected_msg, str(exc))
  97. # Unrecognized strings aren't allowed
  98. self.assertFalse(strutils.bool_from_string('Other', strict=False))
  99. exc = self.assertRaises(ValueError, strutils.bool_from_string, 'Other',
  100. strict=True)
  101. expected_msg = ("Unrecognized value 'Other', acceptable values are:"
  102. " '0', '1', 'f', 'false', 'n', 'no', 'off', 'on',"
  103. " 't', 'true', 'y', 'yes'")
  104. self.assertEqual(expected_msg, str(exc))
  105. # Unrecognized numbers aren't allowed
  106. exc = self.assertRaises(ValueError, strutils.bool_from_string, 2,
  107. strict=True)
  108. expected_msg = ("Unrecognized value '2', acceptable values are:"
  109. " '0', '1', 'f', 'false', 'n', 'no', 'off', 'on',"
  110. " 't', 'true', 'y', 'yes'")
  111. self.assertEqual(expected_msg, str(exc))
  112. # False-like values are allowed
  113. self.assertFalse(strutils.bool_from_string('f', strict=True))
  114. self.assertFalse(strutils.bool_from_string('false', strict=True))
  115. self.assertFalse(strutils.bool_from_string('off', strict=True))
  116. self.assertFalse(strutils.bool_from_string('n', strict=True))
  117. self.assertFalse(strutils.bool_from_string('no', strict=True))
  118. self.assertFalse(strutils.bool_from_string('0', strict=True))
  119. self.assertTrue(strutils.bool_from_string('1', strict=True))
  120. # Avoid font-similarity issues (one looks like lowercase-el, zero like
  121. # oh, etc...)
  122. for char in ('O', 'o', 'L', 'l', 'I', 'i'):
  123. self.assertRaises(ValueError, strutils.bool_from_string, char,
  124. strict=True)
  125. def test_int_from_bool_as_string(self):
  126. self.assertEqual(1, strutils.int_from_bool_as_string(True))
  127. self.assertEqual(0, strutils.int_from_bool_as_string(False))
  128. def test_is_valid_boolstr(self):
  129. self.assertTrue(strutils.is_valid_boolstr('true'))
  130. self.assertTrue(strutils.is_valid_boolstr('false'))
  131. self.assertTrue(strutils.is_valid_boolstr('yes'))
  132. self.assertTrue(strutils.is_valid_boolstr('no'))
  133. self.assertTrue(strutils.is_valid_boolstr('y'))
  134. self.assertTrue(strutils.is_valid_boolstr('n'))
  135. self.assertTrue(strutils.is_valid_boolstr('1'))
  136. self.assertTrue(strutils.is_valid_boolstr('0'))
  137. self.assertTrue(strutils.is_valid_boolstr(1))
  138. self.assertTrue(strutils.is_valid_boolstr(0))
  139. self.assertFalse(strutils.is_valid_boolstr('maybe'))
  140. self.assertFalse(strutils.is_valid_boolstr('only on tuesdays'))
  141. def test_slugify(self):
  142. to_slug = strutils.to_slug
  143. self.assertRaises(TypeError, to_slug, True)
  144. self.assertEqual(six.u("hello"), to_slug("hello"))
  145. self.assertEqual(six.u("two-words"), to_slug("Two Words"))
  146. self.assertEqual(six.u("ma-any-spa-ce-es"),
  147. to_slug("Ma-any\t spa--ce- es"))
  148. self.assertEqual(six.u("excamation"), to_slug("exc!amation!"))
  149. self.assertEqual(six.u("ampserand"), to_slug("&ampser$and"))
  150. self.assertEqual(six.u("ju5tnum8er"), to_slug("ju5tnum8er"))
  151. self.assertEqual(six.u("strip-"), to_slug(" strip - "))
  152. self.assertEqual(six.u("perche"), to_slug(six.b("perch\xc3\xa9")))
  153. self.assertEqual(six.u("strange"),
  154. to_slug("\x80strange", errors="ignore"))
  155. class StringToBytesTest(test_base.BaseTestCase):
  156. _unit_system = [
  157. ('si', dict(unit_system='SI')),
  158. ('iec', dict(unit_system='IEC')),
  159. ('mixed', dict(unit_system='mixed')),
  160. ('invalid_unit_system', dict(unit_system='KKK', assert_error=True)),
  161. ]
  162. _sign = [
  163. ('no_sign', dict(sign='')),
  164. ('positive', dict(sign='+')),
  165. ('negative', dict(sign='-')),
  166. ('invalid_sign', dict(sign='~', assert_error=True)),
  167. ]
  168. _magnitude = [
  169. ('integer', dict(magnitude='79')),
  170. ('decimal', dict(magnitude='7.9')),
  171. ('decimal_point_start', dict(magnitude='.9')),
  172. ('decimal_point_end', dict(magnitude='79.', assert_error=True)),
  173. ('invalid_literal', dict(magnitude='7.9.9', assert_error=True)),
  174. ('garbage_value', dict(magnitude='asdf', assert_error=True)),
  175. ]
  176. _unit_prefix = [
  177. ('no_unit_prefix', dict(unit_prefix='')),
  178. ('k', dict(unit_prefix='k')),
  179. ('K', dict(unit_prefix='K')),
  180. ('M', dict(unit_prefix='M')),
  181. ('G', dict(unit_prefix='G')),
  182. ('T', dict(unit_prefix='T')),
  183. ('Ki', dict(unit_prefix='Ki')),
  184. ('Mi', dict(unit_prefix='Mi')),
  185. ('Gi', dict(unit_prefix='Gi')),
  186. ('Ti', dict(unit_prefix='Ti')),
  187. ('invalid_unit_prefix', dict(unit_prefix='B', assert_error=True)),
  188. ]
  189. _unit_suffix = [
  190. ('b', dict(unit_suffix='b')),
  191. ('bit', dict(unit_suffix='bit')),
  192. ('B', dict(unit_suffix='B')),
  193. ('invalid_unit_suffix', dict(unit_suffix='Kg', assert_error=True)),
  194. ]
  195. _return_int = [
  196. ('return_dec', dict(return_int=False)),
  197. ('return_int', dict(return_int=True)),
  198. ]
  199. @classmethod
  200. def generate_scenarios(cls):
  201. cls.scenarios = testscenarios.multiply_scenarios(cls._unit_system,
  202. cls._sign,
  203. cls._magnitude,
  204. cls._unit_prefix,
  205. cls._unit_suffix,
  206. cls._return_int)
  207. def test_string_to_bytes(self):
  208. def _get_quantity(sign, magnitude, unit_suffix):
  209. res = float('%s%s' % (sign, magnitude))
  210. if unit_suffix in ['b', 'bit']:
  211. res /= 8
  212. return res
  213. def _get_constant(unit_prefix, unit_system):
  214. if not unit_prefix:
  215. return 1
  216. elif unit_system == 'SI':
  217. res = getattr(units, unit_prefix)
  218. elif unit_system == 'IEC':
  219. if unit_prefix.endswith('i'):
  220. res = getattr(units, unit_prefix)
  221. else:
  222. res = getattr(units, '%si' % unit_prefix)
  223. elif unit_system == 'mixed':
  224. # Note: this will return 'i' units as power-of-two,
  225. # and other units as power-of-ten. Additionally, for
  226. # compatability a "K" is interpreted as "k" in mixed
  227. # mode
  228. if unit_prefix == 'K':
  229. unit_prefix = 'k'
  230. res = getattr(units, unit_prefix)
  231. return res
  232. text = ''.join([self.sign, self.magnitude, self.unit_prefix,
  233. self.unit_suffix])
  234. err_si = self.unit_system == 'SI' and (self.unit_prefix == 'K' or
  235. self.unit_prefix.endswith('i'))
  236. err_iec = self.unit_system == 'IEC' and self.unit_prefix == 'k'
  237. if getattr(self, 'assert_error', False) or err_si or err_iec:
  238. self.assertRaises(ValueError, strutils.string_to_bytes,
  239. text, unit_system=self.unit_system,
  240. return_int=self.return_int)
  241. return
  242. quantity = _get_quantity(self.sign, self.magnitude, self.unit_suffix)
  243. constant = _get_constant(self.unit_prefix, self.unit_system)
  244. expected = quantity * constant
  245. actual = strutils.string_to_bytes(text, unit_system=self.unit_system,
  246. return_int=self.return_int)
  247. if self.return_int:
  248. self.assertEqual(actual, int(math.ceil(expected)))
  249. else:
  250. self.assertAlmostEqual(actual, expected)
  251. StringToBytesTest.generate_scenarios()
  252. class MaskPasswordTestCase(test_base.BaseTestCase):
  253. def test_json(self):
  254. # Test 'adminPass' w/o spaces
  255. payload = """{'adminPass':'TL0EfN33'}"""
  256. expected = """{'adminPass':'***'}"""
  257. self.assertEqual(expected, strutils.mask_password(payload))
  258. # Test 'adminPass' with spaces
  259. payload = """{ 'adminPass' : 'TL0EfN33' }"""
  260. expected = """{ 'adminPass' : '***' }"""
  261. self.assertEqual(expected, strutils.mask_password(payload))
  262. # Test 'admin_pass' w/o spaces
  263. payload = """{'admin_pass':'TL0EfN33'}"""
  264. expected = """{'admin_pass':'***'}"""
  265. self.assertEqual(expected, strutils.mask_password(payload))
  266. # Test 'admin_pass' with spaces
  267. payload = """{ 'admin_pass' : 'TL0EfN33' }"""
  268. expected = """{ 'admin_pass' : '***' }"""
  269. self.assertEqual(expected, strutils.mask_password(payload))
  270. # Test 'admin_password' w/o spaces
  271. payload = """{'admin_password':'TL0EfN33'}"""
  272. expected = """{'admin_password':'***'}"""
  273. self.assertEqual(expected, strutils.mask_password(payload))
  274. # Test 'admin_password' with spaces
  275. payload = """{ 'admin_password' : 'TL0EfN33' }"""
  276. expected = """{ 'admin_password' : '***' }"""
  277. self.assertEqual(expected, strutils.mask_password(payload))
  278. # Test 'password' w/o spaces
  279. payload = """{'password':'TL0EfN33'}"""
  280. expected = """{'password':'***'}"""
  281. self.assertEqual(expected, strutils.mask_password(payload))
  282. # Test 'password' with spaces
  283. payload = """{ 'password' : 'TL0EfN33' }"""
  284. expected = """{ 'password' : '***' }"""
  285. self.assertEqual(expected, strutils.mask_password(payload))
  286. # Test 'auth_password' w/o spaces
  287. payload = """{'auth_password':'TL0EfN33'}"""
  288. expected = """{'auth_password':'***'}"""
  289. self.assertEqual(expected, strutils.mask_password(payload))
  290. # Test 'auth_password' with spaces
  291. payload = """{ 'auth_password' : 'TL0EfN33' }"""
  292. expected = """{ 'auth_password' : '***' }"""
  293. self.assertEqual(expected, strutils.mask_password(payload))
  294. # Test 'secret_uuid' w/o spaces
  295. payload = """{'secret_uuid':'myuuid'}"""
  296. expected = """{'secret_uuid':'***'}"""
  297. self.assertEqual(expected, strutils.mask_password(payload))
  298. # Test 'secret_uuid' with spaces
  299. payload = """{ 'secret_uuid' : 'myuuid' }"""
  300. expected = """{ 'secret_uuid' : '***' }"""
  301. self.assertEqual(expected, strutils.mask_password(payload))
  302. # Test 'token' w/o spaces
  303. payload = """{'token':'token'}"""
  304. expected = """{'token':'***'}"""
  305. self.assertEqual(expected, strutils.mask_password(payload))
  306. # Test 'token' with spaces
  307. payload = """{ 'token' : 'token' }"""
  308. expected = """{ 'token' : '***' }"""
  309. self.assertEqual(expected, strutils.mask_password(payload))
  310. # Test 'fernetkey'
  311. payload = """{ 'fernetkey' : 'token' }"""
  312. expected = """{ 'fernetkey' : '***' }"""
  313. self.assertEqual(expected, strutils.mask_password(payload))
  314. # Test 'FernetKey'
  315. payload = """{ 'FernetKey' : 'token' }"""
  316. expected = """{ 'FernetKey' : '***' }"""
  317. self.assertEqual(expected, strutils.mask_password(payload))
  318. # Test 'sslkey'
  319. payload = """{ 'sslkey' : 'token' }"""
  320. expected = """{ 'sslkey' : '***' }"""
  321. self.assertEqual(expected, strutils.mask_password(payload))
  322. # Test 'SslKey'
  323. payload = """{ 'SslKey' : 'token' }"""
  324. expected = """{ 'SslKey' : '***' }"""
  325. self.assertEqual(expected, strutils.mask_password(payload))
  326. # Test 'passphrase'
  327. payload = """{ 'passphrase' : 'token' }"""
  328. expected = """{ 'passphrase' : '***' }"""
  329. self.assertEqual(expected, strutils.mask_password(payload))
  330. # Test 'PassPhrase'
  331. payload = """{ 'PassPhrase' : 'token' }"""
  332. expected = """{ 'PassPhrase' : '***' }"""
  333. self.assertEqual(expected, strutils.mask_password(payload))
  334. # Some real-life cases
  335. # Test 'KeystoneFernetKey1'
  336. payload = """{ 'KeystoneFernetKey1' : 'token' }"""
  337. expected = """{ 'KeystoneFernetKey1' : '***' }"""
  338. self.assertEqual(expected, strutils.mask_password(payload))
  339. # Test 'OctaviaCaKeyPassword'
  340. payload = """{ 'OctaviaCaKeyPassword' : 'token' }"""
  341. expected = """{ 'OctaviaCaKeyPassword' : '***' }"""
  342. self.assertEqual(expected, strutils.mask_password(payload))
  343. # Test 'OctaviaCaKeyPassphrase'
  344. payload = """{ 'OctaviaCaKeyPassphrase' : 'token' }"""
  345. expected = """{ 'OctaviaCaKeyPassphrase' : '***' }"""
  346. self.assertEqual(expected, strutils.mask_password(payload))
  347. def test_xml(self):
  348. # Test 'adminPass' w/o spaces
  349. payload = """<adminPass>TL0EfN33</adminPass>"""
  350. expected = """<adminPass>***</adminPass>"""
  351. self.assertEqual(expected, strutils.mask_password(payload))
  352. # Test 'adminPass' with spaces
  353. payload = """<adminPass>
  354. TL0EfN33
  355. </adminPass>"""
  356. expected = """<adminPass>***</adminPass>"""
  357. self.assertEqual(expected, strutils.mask_password(payload))
  358. # Test 'admin_pass' w/o spaces
  359. payload = """<admin_pass>TL0EfN33</admin_pass>"""
  360. expected = """<admin_pass>***</admin_pass>"""
  361. self.assertEqual(expected, strutils.mask_password(payload))
  362. # Test 'admin_pass' with spaces
  363. payload = """<admin_pass>
  364. TL0EfN33
  365. </admin_pass>"""
  366. expected = """<admin_pass>***</admin_pass>"""
  367. self.assertEqual(expected, strutils.mask_password(payload))
  368. # Test 'admin_password' w/o spaces
  369. payload = """<admin_password>TL0EfN33</admin_password>"""
  370. expected = """<admin_password>***</admin_password>"""
  371. self.assertEqual(expected, strutils.mask_password(payload))
  372. # Test 'admin_password' with spaces
  373. payload = """<admin_password>
  374. TL0EfN33
  375. </admin_password>"""
  376. expected = """<admin_password>***</admin_password>"""
  377. self.assertEqual(expected, strutils.mask_password(payload))
  378. # Test 'password' w/o spaces
  379. payload = """<password>TL0EfN33</password>"""
  380. expected = """<password>***</password>"""
  381. self.assertEqual(expected, strutils.mask_password(payload))
  382. # Test 'password' with spaces
  383. payload = """<password>
  384. TL0EfN33
  385. </password>"""
  386. expected = """<password>***</password>"""
  387. self.assertEqual(expected, strutils.mask_password(payload))
  388. # Test 'Password1' - case-insensitive + number
  389. payload = """<Password1>TL0EfN33</Password1>"""
  390. expected = """<Password1>***</Password1>"""
  391. self.assertEqual(expected, strutils.mask_password(payload))
  392. def test_xml_attribute(self):
  393. # Test 'adminPass' w/o spaces
  394. payload = """adminPass='TL0EfN33'"""
  395. expected = """adminPass='***'"""
  396. self.assertEqual(expected, strutils.mask_password(payload))
  397. # Test 'adminPass' with spaces
  398. payload = """adminPass = 'TL0EfN33'"""
  399. expected = """adminPass = '***'"""
  400. self.assertEqual(expected, strutils.mask_password(payload))
  401. # Test 'adminPass' with double quotes
  402. payload = """adminPass = "TL0EfN33\""""
  403. expected = """adminPass = "***\""""
  404. self.assertEqual(expected, strutils.mask_password(payload))
  405. # Test 'admin_pass' w/o spaces
  406. payload = """admin_pass='TL0EfN33'"""
  407. expected = """admin_pass='***'"""
  408. self.assertEqual(expected, strutils.mask_password(payload))
  409. # Test 'admin_pass' with spaces
  410. payload = """admin_pass = 'TL0EfN33'"""
  411. expected = """admin_pass = '***'"""
  412. self.assertEqual(expected, strutils.mask_password(payload))
  413. # Test 'admin_pass' with double quotes
  414. payload = """admin_pass = "TL0EfN33\""""
  415. expected = """admin_pass = "***\""""
  416. self.assertEqual(expected, strutils.mask_password(payload))
  417. # Test 'admin_password' w/o spaces
  418. payload = """admin_password='TL0EfN33'"""
  419. expected = """admin_password='***'"""
  420. self.assertEqual(expected, strutils.mask_password(payload))
  421. # Test 'admin_password' with spaces
  422. payload = """admin_password = 'TL0EfN33'"""
  423. expected = """admin_password = '***'"""
  424. self.assertEqual(expected, strutils.mask_password(payload))
  425. # Test 'admin_password' with double quotes
  426. payload = """admin_password = "TL0EfN33\""""
  427. expected = """admin_password = "***\""""
  428. self.assertEqual(expected, strutils.mask_password(payload))
  429. # Test 'password' w/o spaces
  430. payload = """password='TL0EfN33'"""
  431. expected = """password='***'"""
  432. self.assertEqual(expected, strutils.mask_password(payload))
  433. # Test 'password' with spaces
  434. payload = """password = 'TL0EfN33'"""
  435. expected = """password = '***'"""
  436. self.assertEqual(expected, strutils.mask_password(payload))
  437. # Test 'password' with double quotes
  438. payload = """password = "TL0EfN33\""""
  439. expected = """password = "***\""""
  440. self.assertEqual(expected, strutils.mask_password(payload))
  441. def test_json_message(self):
  442. payload = """body: {"changePassword": {"adminPass": "1234567"}}"""
  443. expected = """body: {"changePassword": {"adminPass": "***"}}"""
  444. self.assertEqual(expected, strutils.mask_password(payload))
  445. payload = """body: {"rescue": {"admin_pass": "1234567"}}"""
  446. expected = """body: {"rescue": {"admin_pass": "***"}}"""
  447. self.assertEqual(expected, strutils.mask_password(payload))
  448. payload = """body: {"rescue": {"admin_password": "1234567"}}"""
  449. expected = """body: {"rescue": {"admin_password": "***"}}"""
  450. self.assertEqual(expected, strutils.mask_password(payload))
  451. payload = """body: {"rescue": {"password": "1234567"}}"""
  452. expected = """body: {"rescue": {"password": "***"}}"""
  453. self.assertEqual(expected, strutils.mask_password(payload))
  454. payload = """body: {"rescue": {"encryption_key_id": "1234567"}}"""
  455. expected = """body: {"rescue": {"encryption_key_id": "***"}}"""
  456. self.assertEqual(expected, strutils.mask_password(payload))
  457. def test_xml_message(self):
  458. payload = """<?xml version="1.0" encoding="UTF-8"?>
  459. <rebuild
  460. xmlns="http://docs.openstack.org/compute/api/v1.1"
  461. name="foobar"
  462. imageRef="http://openstack.example.com/v1.1/32278/images/70a599e0-31e7"
  463. accessIPv4="1.2.3.4"
  464. accessIPv6="fe80::100"
  465. adminPass="seekr3t">
  466. <metadata>
  467. <meta key="My Server Name">Apache1</meta>
  468. </metadata>
  469. </rebuild>"""
  470. expected = """<?xml version="1.0" encoding="UTF-8"?>
  471. <rebuild
  472. xmlns="http://docs.openstack.org/compute/api/v1.1"
  473. name="foobar"
  474. imageRef="http://openstack.example.com/v1.1/32278/images/70a599e0-31e7"
  475. accessIPv4="1.2.3.4"
  476. accessIPv6="fe80::100"
  477. adminPass="***">
  478. <metadata>
  479. <meta key="My Server Name">Apache1</meta>
  480. </metadata>
  481. </rebuild>"""
  482. self.assertEqual(expected, strutils.mask_password(payload))
  483. payload = """<?xml version="1.0" encoding="UTF-8"?>
  484. <rescue xmlns="http://docs.openstack.org/compute/api/v1.1"
  485. admin_pass="MySecretPass"/>"""
  486. expected = """<?xml version="1.0" encoding="UTF-8"?>
  487. <rescue xmlns="http://docs.openstack.org/compute/api/v1.1"
  488. admin_pass="***"/>"""
  489. self.assertEqual(expected, strutils.mask_password(payload))
  490. payload = """<?xml version="1.0" encoding="UTF-8"?>
  491. <rescue xmlns="http://docs.openstack.org/compute/api/v1.1"
  492. admin_password="MySecretPass"/>"""
  493. expected = """<?xml version="1.0" encoding="UTF-8"?>
  494. <rescue xmlns="http://docs.openstack.org/compute/api/v1.1"
  495. admin_password="***"/>"""
  496. self.assertEqual(expected, strutils.mask_password(payload))
  497. payload = """<?xml version="1.0" encoding="UTF-8"?>
  498. <rescue xmlns="http://docs.openstack.org/compute/api/v1.1"
  499. password="MySecretPass"/>"""
  500. expected = """<?xml version="1.0" encoding="UTF-8"?>
  501. <rescue xmlns="http://docs.openstack.org/compute/api/v1.1"
  502. password="***"/>"""
  503. self.assertEqual(expected, strutils.mask_password(payload))
  504. def test_mask_password(self):
  505. payload = "test = 'password' : 'aaaaaa'"
  506. expected = "test = 'password' : '111'"
  507. self.assertEqual(expected,
  508. strutils.mask_password(payload, secret='111'))
  509. payload = 'mysqld --password "aaaaaa"'
  510. expected = 'mysqld --password "****"'
  511. self.assertEqual(expected,
  512. strutils.mask_password(payload, secret='****'))
  513. payload = 'mysqld --password aaaaaa'
  514. expected = 'mysqld --password ???'
  515. self.assertEqual(expected,
  516. strutils.mask_password(payload, secret='???'))
  517. payload = 'mysqld --password = "aaaaaa"'
  518. expected = 'mysqld --password = "****"'
  519. self.assertEqual(expected,
  520. strutils.mask_password(payload, secret='****'))
  521. payload = "mysqld --password = 'aaaaaa'"
  522. expected = "mysqld --password = '****'"
  523. self.assertEqual(expected,
  524. strutils.mask_password(payload, secret='****'))
  525. payload = "mysqld --password = aaaaaa"
  526. expected = "mysqld --password = ****"
  527. self.assertEqual(expected,
  528. strutils.mask_password(payload, secret='****'))
  529. payload = "test = password = aaaaaa"
  530. expected = "test = password = 111"
  531. self.assertEqual(expected,
  532. strutils.mask_password(payload, secret='111'))
  533. payload = "test = password= aaaaaa"
  534. expected = "test = password= 111"
  535. self.assertEqual(expected,
  536. strutils.mask_password(payload, secret='111'))
  537. payload = "test = password =aaaaaa"
  538. expected = "test = password =111"
  539. self.assertEqual(expected,
  540. strutils.mask_password(payload, secret='111'))
  541. payload = "test = password=aaaaaa"
  542. expected = "test = password=111"
  543. self.assertEqual(expected,
  544. strutils.mask_password(payload, secret='111'))
  545. payload = 'test = "original_password" : "aaaaaaaaa"'
  546. expected = 'test = "original_password" : "***"'
  547. self.assertEqual(expected, strutils.mask_password(payload))
  548. payload = 'test = "param1" : "value"'
  549. expected = 'test = "param1" : "value"'
  550. self.assertEqual(expected, strutils.mask_password(payload))
  551. payload = """{'adminPass':'TL0EfN33'}"""
  552. payload = six.text_type(payload)
  553. expected = """{'adminPass':'***'}"""
  554. self.assertEqual(expected, strutils.mask_password(payload))
  555. payload = """{'token':'mytoken'}"""
  556. payload = six.text_type(payload)
  557. expected = """{'token':'***'}"""
  558. self.assertEqual(expected, strutils.mask_password(payload))
  559. payload = ("test = 'node.session.auth.password','-v','TL0EfN33',"
  560. "'nomask'")
  561. expected = ("test = 'node.session.auth.password','-v','***',"
  562. "'nomask'")
  563. self.assertEqual(expected, strutils.mask_password(payload))
  564. payload = ("test = 'node.session.auth.password', '--password', "
  565. "'TL0EfN33', 'nomask'")
  566. expected = ("test = 'node.session.auth.password', '--password', "
  567. "'***', 'nomask'")
  568. self.assertEqual(expected, strutils.mask_password(payload))
  569. payload = ("test = 'node.session.auth.password', '--password', "
  570. "'TL0EfN33'")
  571. expected = ("test = 'node.session.auth.password', '--password', "
  572. "'***'")
  573. self.assertEqual(expected, strutils.mask_password(payload))
  574. payload = "test = node.session.auth.password -v TL0EfN33 nomask"
  575. expected = "test = node.session.auth.password -v *** nomask"
  576. self.assertEqual(expected, strutils.mask_password(payload))
  577. payload = ("test = node.session.auth.password --password TL0EfN33 "
  578. "nomask")
  579. expected = ("test = node.session.auth.password --password *** "
  580. "nomask")
  581. self.assertEqual(expected, strutils.mask_password(payload))
  582. payload = ("test = node.session.auth.password --password TL0EfN33")
  583. expected = ("test = node.session.auth.password --password ***")
  584. self.assertEqual(expected, strutils.mask_password(payload))
  585. payload = "test = cmd --password my\xe9\x80\x80pass"
  586. expected = ("test = cmd --password ***")
  587. self.assertEqual(expected, strutils.mask_password(payload))
  588. class TestMapping(collections.Mapping):
  589. """Test class for non-dict mappings"""
  590. def __init__(self):
  591. super(TestMapping, self).__init__()
  592. self.data = {'password': 'shhh',
  593. 'foo': 'bar',
  594. }
  595. def __getitem__(self, key):
  596. return self.data[key]
  597. def __iter__(self):
  598. return self.data.__iter__()
  599. def __len__(self):
  600. return len(self.data)
  601. class NestedMapping(TestMapping):
  602. """Test class that contains an instance of TestMapping"""
  603. def __init__(self):
  604. super(NestedMapping, self).__init__()
  605. self.data = {'nested': TestMapping()}
  606. class MaskDictionaryPasswordTestCase(test_base.BaseTestCase):
  607. def test_dictionary(self):
  608. payload = {'password': 'TL0EfN33'}
  609. expected = {'password': '***'}
  610. self.assertEqual(expected,
  611. strutils.mask_dict_password(payload))
  612. payload = {'user': 'admin', 'password': 'TL0EfN33'}
  613. expected = {'user': 'admin', 'password': '***'}
  614. self.assertEqual(expected,
  615. strutils.mask_dict_password(payload))
  616. payload = {'strval': 'somestring',
  617. 'dictval': {'user': 'admin', 'password': 'TL0EfN33'}}
  618. expected = {'strval': 'somestring',
  619. 'dictval': {'user': 'admin', 'password': '***'}}
  620. self.assertEqual(expected,
  621. strutils.mask_dict_password(payload))
  622. payload = {'strval': '--password abc',
  623. 'dont_change': 'this is fine',
  624. 'dictval': {'user': 'admin', 'password': b'TL0EfN33'}}
  625. expected = {'strval': '--password ***',
  626. 'dont_change': 'this is fine',
  627. 'dictval': {'user': 'admin', 'password': '***'}}
  628. self.assertEqual(expected,
  629. strutils.mask_dict_password(payload))
  630. payload = {'ipmi_password': 'KeDrahishvowphyecMornEm0or('}
  631. expected = {'ipmi_password': '***'}
  632. self.assertEqual(expected,
  633. strutils.mask_dict_password(payload))
  634. def test_do_no_harm(self):
  635. payload = {}
  636. expected = {}
  637. self.assertEqual(expected,
  638. strutils.mask_dict_password(payload))
  639. payload = {'somekey': 'somevalue',
  640. 'anotherkey': 'anothervalue'}
  641. expected = {'somekey': 'somevalue',
  642. 'anotherkey': 'anothervalue'}
  643. self.assertEqual(expected,
  644. strutils.mask_dict_password(payload))
  645. def test_do_an_int(self):
  646. payload = {}
  647. payload[1] = 2
  648. expected = payload.copy()
  649. self.assertEqual(expected,
  650. strutils.mask_dict_password(payload))
  651. def test_mask_values(self):
  652. payload = {'somekey': 'test = cmd --password my\xe9\x80\x80pass'}
  653. expected = {'somekey': 'test = cmd --password ***'}
  654. self.assertEqual(expected,
  655. strutils.mask_dict_password(payload))
  656. def test_other_non_str_values(self):
  657. payload = {'password': 'DK0PK1AK3', 'bool': True,
  658. 'dict': {'cat': 'meow', 'password': "*aa38skdjf"},
  659. 'float': 0.1, 'int': 123, 'list': [1, 2], 'none': None,
  660. 'str': 'foo'}
  661. expected = {'password': '***', 'bool': True,
  662. 'dict': {'cat': 'meow', 'password': '***'},
  663. 'float': 0.1, 'int': 123, 'list': [1, 2], 'none': None,
  664. 'str': 'foo'}
  665. self.assertEqual(expected,
  666. strutils.mask_dict_password(payload))
  667. def test_argument_untouched(self):
  668. """Make sure that the argument passed in is not modified"""
  669. payload = {'password': 'DK0PK1AK3', 'bool': True,
  670. 'dict': {'cat': 'meow', 'password': "*aa38skdjf"},
  671. 'float': 0.1, 'int': 123, 'list': [1, 2], 'none': None,
  672. 'str': 'foo'}
  673. pristine = copy.deepcopy(payload)
  674. # Send the payload into the function, to see if it gets modified
  675. strutils.mask_dict_password(payload)
  676. self.assertEqual(pristine, payload)
  677. def test_non_dict(self):
  678. expected = {'password': '***',
  679. 'foo': 'bar',
  680. }
  681. payload = TestMapping()
  682. self.assertEqual(expected, strutils.mask_dict_password(payload))
  683. def test_nested_non_dict(self):
  684. expected = {'nested': {'password': '***',
  685. 'foo': 'bar',
  686. }
  687. }
  688. payload = NestedMapping()
  689. self.assertEqual(expected, strutils.mask_dict_password(payload))
  690. class IsIntLikeTestCase(test_base.BaseTestCase):
  691. def test_is_int_like_true(self):
  692. self.assertTrue(strutils.is_int_like(1))
  693. self.assertTrue(strutils.is_int_like("1"))
  694. self.assertTrue(strutils.is_int_like("514"))
  695. self.assertTrue(strutils.is_int_like("0"))
  696. def test_is_int_like_false(self):
  697. self.assertFalse(strutils.is_int_like(1.1))
  698. self.assertFalse(strutils.is_int_like("1.1"))
  699. self.assertFalse(strutils.is_int_like("1.1.1"))
  700. self.assertFalse(strutils.is_int_like(None))
  701. self.assertFalse(strutils.is_int_like("0."))
  702. self.assertFalse(strutils.is_int_like("aaaaaa"))
  703. self.assertFalse(strutils.is_int_like("...."))
  704. self.assertFalse(strutils.is_int_like("1g"))
  705. self.assertFalse(
  706. strutils.is_int_like("0cc3346e-9fef-4445-abe6-5d2b2690ec64"))
  707. self.assertFalse(strutils.is_int_like("a1"))
  708. # NOTE(viktors): 12e3 - is a float number
  709. self.assertFalse(strutils.is_int_like("12e3"))
  710. # NOTE(viktors): Check integer numbers with base not 10
  711. self.assertFalse(strutils.is_int_like("0o51"))
  712. self.assertFalse(strutils.is_int_like("0xDEADBEEF"))
  713. class StringLengthTestCase(test_base.BaseTestCase):
  714. def test_check_string_length(self):
  715. self.assertIsNone(strutils.check_string_length(
  716. 'test', 'name', max_length=255))
  717. self.assertRaises(ValueError,
  718. strutils.check_string_length,
  719. '', 'name', min_length=1)
  720. self.assertRaises(ValueError,
  721. strutils.check_string_length,
  722. 'a' * 256, 'name', max_length=255)
  723. self.assertRaises(TypeError,
  724. strutils.check_string_length,
  725. 11, 'name', max_length=255)
  726. self.assertRaises(TypeError,
  727. strutils.check_string_length,
  728. dict(), 'name', max_length=255)
  729. def test_check_string_length_noname(self):
  730. self.assertIsNone(strutils.check_string_length(
  731. 'test', max_length=255))
  732. self.assertRaises(ValueError,
  733. strutils.check_string_length,
  734. '', min_length=1)
  735. self.assertRaises(ValueError,
  736. strutils.check_string_length,
  737. 'a' * 256, max_length=255)
  738. self.assertRaises(TypeError,
  739. strutils.check_string_length,
  740. 11, max_length=255)
  741. self.assertRaises(TypeError,
  742. strutils.check_string_length,
  743. dict(), max_length=255)
  744. class SplitPathTestCase(test_base.BaseTestCase):
  745. def test_split_path_failed(self):
  746. self.assertRaises(ValueError, strutils.split_path, '')
  747. self.assertRaises(ValueError, strutils.split_path, '/')
  748. self.assertRaises(ValueError, strutils.split_path, '//')
  749. self.assertRaises(ValueError, strutils.split_path, '//a')
  750. self.assertRaises(ValueError, strutils.split_path, '/a/c')
  751. self.assertRaises(ValueError, strutils.split_path, '//c')
  752. self.assertRaises(ValueError, strutils.split_path, '/a/c/')
  753. self.assertRaises(ValueError, strutils.split_path, '/a//')
  754. self.assertRaises(ValueError, strutils.split_path, '/a', 2)
  755. self.assertRaises(ValueError, strutils.split_path, '/a', 2, 3)
  756. self.assertRaises(ValueError, strutils.split_path, '/a', 2, 3, True)
  757. self.assertRaises(ValueError, strutils.split_path, '/a/c/o/r', 3, 3)
  758. self.assertRaises(ValueError, strutils.split_path, '/a', 5, 4)
  759. def test_split_path_success(self):
  760. self.assertEqual(strutils.split_path('/a'), ['a'])
  761. self.assertEqual(strutils.split_path('/a/'), ['a'])
  762. self.assertEqual(strutils.split_path('/a/c', 2), ['a', 'c'])
  763. self.assertEqual(strutils.split_path('/a/c/o', 3), ['a', 'c', 'o'])
  764. self.assertEqual(strutils.split_path('/a/c/o/r', 3, 3, True),
  765. ['a', 'c', 'o/r'])
  766. self.assertEqual(strutils.split_path('/a/c', 2, 3, True),
  767. ['a', 'c', None])
  768. self.assertEqual(strutils.split_path('/a/c/', 2), ['a', 'c'])
  769. self.assertEqual(strutils.split_path('/a/c/', 2, 3), ['a', 'c', ''])
  770. def test_split_path_invalid_path(self):
  771. try:
  772. strutils.split_path('o\nn e', 2)
  773. except ValueError as err:
  774. self.assertEqual(str(err), 'Invalid path: o%0An%20e')
  775. try:
  776. strutils.split_path('o\nn e', 2, 3, True)
  777. except ValueError as err:
  778. self.assertEqual(str(err), 'Invalid path: o%0An%20e')
  779. class SplitByCommas(test_base.BaseTestCase):
  780. def test_not_closed_quotes(self):
  781. self.assertRaises(ValueError, strutils.split_by_commas, '"ab","b""')
  782. def test_no_comma_before_opening_quotes(self):
  783. self.assertRaises(ValueError, strutils.split_by_commas, '"ab""b"')
  784. def test_quote_inside_unquoted(self):
  785. self.assertRaises(ValueError, strutils.split_by_commas, 'a"b,cd')
  786. def check(self, expect, input):
  787. self.assertEqual(expect, strutils.split_by_commas(input))
  788. def test_plain(self):
  789. self.check(["a,b", "ac"], '"a,b",ac')
  790. def test_with_backslash_inside_quoted(self):
  791. self.check(['abc"', 'de', 'fg,h', 'klm\\', '"nop'],
  792. r'"abc\"","de","fg,h","klm\\","\"nop"')
  793. def test_with_backslash_inside_unquoted(self):
  794. self.check([r'a\bc', 'de'], r'a\bc,de')
  795. def test_with_escaped_quotes_in_row_inside_quoted(self):
  796. self.check(['a"b""c', 'd'], r'"a\"b\"\"c",d')
  797. @ddt.ddt
  798. class ValidateIntegerTestCase(test_base.BaseTestCase):
  799. @ddt.unpack
  800. @ddt.data({"value": 42, "name": "answer", "output": 42},
  801. {"value": "42", "name": "answer", "output": 42},
  802. {"value": "7", "name": "lucky", "output": 7,
  803. "min_value": 7, "max_value": 8},
  804. {"value": 7, "name": "lucky", "output": 7,
  805. "min_value": 6, "max_value": 7},
  806. {"value": 300, "name": "Spartaaa!!!", "output": 300,
  807. "min_value": 300},
  808. {"value": "300", "name": "Spartaaa!!!", "output": 300,
  809. "max_value": 300})
  810. def test_valid_inputs(self, output, value, name, **kwargs):
  811. self.assertEqual(strutils.validate_integer(value, name,
  812. **kwargs), output)
  813. @ddt.unpack
  814. @ddt.data({"value": "im-not-an-int", "name": ''},
  815. {"value": 3.14, "name": "Pie"},
  816. {"value": "299", "name": "Sparta no-show",
  817. "min_value": 300, "max_value": 300},
  818. {"value": 55, "name": "doing 55 in a 54",
  819. "max_value": 54},
  820. {"value": six.unichr(129), "name": "UnicodeError",
  821. "max_value": 1000})
  822. def test_invalid_inputs(self, value, name, **kwargs):
  823. self.assertRaises(ValueError, strutils.validate_integer,
  824. value, name, **kwargs)