# vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright (C) 2012 Yahoo! Inc. All Rights Reserved. # Copyright (C) 2012 New Dream Network, LLC (DreamHost) All Rights Reserved. # # 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. import binascii import getpass import logging import os import re LOG = logging.getLogger("devstack.passwords") PW_SECTION = 'passwords' def generate_random(length): """Returns a randomly generated password of the specified length.""" LOG.debug("Generating a pseudo-random password of %d characters", length) return binascii.hexlify(os.urandom((length + 1) / 2))[:length] class PasswordGenerator(object): def __init__(self, cfg, prompt_user=True): self.cfg = cfg self.prompt_user = prompt_user def _valid_password(self, pw): # FIXME: More efficient way to look for whitespace? if re.match(r"^(\s+)$", pw) or \ re.match(r"^(\s+)(\S+)(\s+)$", pw) or \ re.match(r"^(\S+)(\s+)$", pw) or \ re.match(r"^(\s+)(\S+)$", pw): return False return True def _prompt_user(self, prompt_text): LOG.debug('Asking the user for a %r password', prompt_text) message = ("Enter a password to use for %s " "[or press enter to get a generated one]: " % prompt_text ) rc = "" while True: rc = getpass.getpass(message) if len(rc) == 0 or self._valid_password(rc): break else: LOG.warn("Invalid password \"%s\" (please try again)" % (rc)) return rc def extract(self, option): return self.cfg.get(PW_SECTION, option) def _set_through(self, option, value): self.cfg.set(PW_SECTION, option, value) def get_password(self, option, prompt_text, length=8): """Returns a password identified by the configuration location.""" LOG.debug('Looking for password %s (%s)', option, prompt_text) # Look in the configuration file(s) password = None from_config = False if not password: password = self.cfg.get(PW_SECTION, option) if password: from_config = True # Optionally ask the user if not password and self.prompt_user: password = self._prompt_user(prompt_text) # If we still don't have a value, make one up. if not password: LOG.debug('No configured password for %s (%s)', option, prompt_text) password = generate_random(length) # Update via set through to the config if not from_config: self._set_through(option, password) return password