Add password-regex parameter
As it is now, python-tempestconf can't work with password_regex restriction given by keystone. To give users an option to set password that meets the required regex, we added a new option. Change-Id: Ide61a222d2dac79c15fbe41c058410e091531aef Signed-off-by: Katarina Strenkova <kstrenko@redhat.com>
This commit is contained in:
@@ -37,6 +37,9 @@ obtained by querying the cloud.
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import secrets
|
||||
import string
|
||||
import sys
|
||||
|
||||
import openstack
|
||||
@@ -258,6 +261,18 @@ def get_arg_parser():
|
||||
$ discover-tempest-config \\
|
||||
identity.username myname \\
|
||||
identity.password mypass""")
|
||||
parser.add_argument('--password-regex', default=None,
|
||||
help="""Define the password regex required to
|
||||
generate a password that meets these
|
||||
conditions. NOTE: In regular expression use
|
||||
double curly brackets `{{`instead of single
|
||||
curly bracket `{` to make the command pass.
|
||||
""")
|
||||
parser.add_argument('--password-length', default=8,
|
||||
help="""Specify the password length in combination
|
||||
with the --password-regex option to generate
|
||||
a password that meets these conditions.
|
||||
""")
|
||||
parser.add_argument('--debug', action='store_true', default=False,
|
||||
help='Print debugging information.')
|
||||
parser.add_argument('--verbose', '-v', action='store_true', default=False,
|
||||
@@ -451,6 +466,30 @@ def parse_overrides(overrides):
|
||||
return new_overrides
|
||||
|
||||
|
||||
def generate_password_by_regex(regex, length):
|
||||
"""Generate a new password that meets the required regex and length.
|
||||
|
||||
:type regex: string
|
||||
:type length: int
|
||||
"""
|
||||
characters = string.ascii_letters + string.digits + string.punctuation
|
||||
regex = regex.replace('{{', '{').replace('}}', '}')
|
||||
|
||||
try:
|
||||
regex_object = re.compile(regex)
|
||||
except re.error:
|
||||
raise Exception("The provided regex %s is invalid." % regex)
|
||||
|
||||
for _ in range(200):
|
||||
password = ''.join(secrets.choice(characters) for i in range(length))
|
||||
if regex_object.fullmatch(password):
|
||||
return password
|
||||
|
||||
raise Exception("Could not generate a password matching the specified"
|
||||
" regex. Check if the regex %s is possible with the"
|
||||
" given length %d." % (regex, length))
|
||||
|
||||
|
||||
def set_cloud_config_values(non_admin, cloud_creds, conf):
|
||||
"""Set values from client's cloud config file.
|
||||
|
||||
@@ -553,6 +592,12 @@ def config_tempest(**kwargs):
|
||||
clients = ClientManager(conf, credentials)
|
||||
|
||||
if kwargs.get('create', False) and kwargs.get('test_accounts') is None:
|
||||
if kwargs.get('password_regex') is not None:
|
||||
password_regex = kwargs.get('password_regex')
|
||||
password_length = kwargs.get('password_length')
|
||||
new_password = generate_password_by_regex(password_regex,
|
||||
int(password_length))
|
||||
conf.set('identity', 'password', new_password)
|
||||
users = Users(clients.projects, clients.roles, clients.users, conf)
|
||||
users.create_tempest_users()
|
||||
|
||||
@@ -632,6 +677,8 @@ def main():
|
||||
os_cloud=args.os_cloud,
|
||||
out=args.out,
|
||||
overrides=args.overrides,
|
||||
password_regex=args.password_regex,
|
||||
password_length=args.password_length,
|
||||
remove=args.remove,
|
||||
test_accounts=args.test_accounts,
|
||||
verbose=args.verbose,
|
||||
|
@@ -15,6 +15,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import re
|
||||
from unittest import mock
|
||||
|
||||
from fixtures import MonkeyPatch
|
||||
@@ -119,3 +120,38 @@ class TestOsClientConfigSupport(BaseConfigTempestTest):
|
||||
self.conf.get('auth', 'admin_username'),
|
||||
self.conf.get('auth', 'admin_password'),
|
||||
self.conf.get('auth', 'admin_project_name'))
|
||||
|
||||
|
||||
class TestGeneratePassword(BaseConfigTempestTest):
|
||||
|
||||
def test_generate_password_success_easy(self):
|
||||
regex = r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).+$'
|
||||
length = 12
|
||||
password = tool.generate_password_by_regex(regex, length)
|
||||
|
||||
self.assertIsNotNone(password)
|
||||
self.assertEqual(length, len(password))
|
||||
self.assertTrue(re.fullmatch(regex, password))
|
||||
|
||||
def test_generate_password_success_hard(self):
|
||||
regex = r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*_+-=]).{8}$'
|
||||
length = 8
|
||||
password = tool.generate_password_by_regex(regex, length)
|
||||
|
||||
self.assertIsNotNone(password)
|
||||
self.assertEqual(length, len(password))
|
||||
self.assertTrue(re.fullmatch(regex, password))
|
||||
|
||||
def test_generate_password_invalid_regex(self):
|
||||
invalid_regex = r'['
|
||||
exc = Exception
|
||||
self.assertRaises(exc,
|
||||
tool.generate_password_by_regex,
|
||||
invalid_regex, 8)
|
||||
|
||||
def test_generate_password_impossible(self):
|
||||
impossible_regex = r'^[a-z]{10}$'
|
||||
exc = Exception
|
||||
self.assertRaises(exc,
|
||||
tool.generate_password_by_regex,
|
||||
impossible_regex, 8)
|
||||
|
@@ -0,0 +1,11 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
New parameters, ``--password-regex`` and ``--password-length`` have been
|
||||
added to allow users to generate a password following these constraints.
|
||||
It is helpful for meeting password requirements given by services (e.g.
|
||||
Keystone). When using both parameters, ensure the value for
|
||||
``--password-length`` does not conflict with any length defined within
|
||||
the regular expression. Furthermore, to use a regex from the command
|
||||
line, you must modify it by changing all single curly brackets ({}) to
|
||||
double curly brackets ({{}}).
|
Reference in New Issue
Block a user