def check_credentials(group, command, config, protocol): """Check credentials using configuration :raises errors.CredentialError: if login failed, or if user has no permission """ if group not in ('user', 'role'): return _configure_connections(config) _persistence.init_thread() if not protocol: protocol = FABRIC_DEFAULT_PROTOCOL section = 'protocol.' + protocol username = config.get(section, 'user') password = config.get(section, 'password') realm = config.get(section, 'realm', vars=FABRIC_PROTOCOL_DEFAULTS) user = User.fetch_user(username, protocol=protocol) password_hash = _hash_password(username, password, protocol, config, realm) if user is None or user.password_hash != password_hash: _LOGGER.info("Failed login for user %s/%s", username, protocol) raise _errors.CredentialError("Login failed") elif not user.has_permission('core', group, command): _LOGGER.info("Permission denied for user %s/%s", username, protocol) raise _errors.CredentialError("No permission")
def execute(self, username, protocol=None): """Change password a Fabric user """ username, password, protocol = self._ask_credentials( username, ask_password=False) # Check if user exists if not get_user(username, protocol): raise _errors.CredentialError("No user {user}/{protocol}".format( user=username, protocol=protocol)) password = _get_password('New password: ') if password: try: _change_password(username, password, protocol, self.config, self.persister) except _errors.CredentialError as error: print "Changing password cancelled." _LOGGER.debug(str(error)) print error else: print "Password changed." else: raise _errors.CredentialError("Can not set empty password")
def _change_password(username, password, protocol, config, persister): """Change password of a Fabric user :param username: Username of Fabric user. :param password: Password to which we change. :param protocol: Protocol for this user. :param config: Fabric configuration. :param persister: A valid handle to the state store. :raise _errors.CredentialError: if any error occurs while updating data """ try: persister.begin() options = { "fetch": False, "params": (), "columns": True, } update = ("UPDATE users SET password = %s WHERE username = %s" " AND protocol = %s") hashed = _hash_password(username, password, protocol, config) options['params'] = (hashed, username, protocol) persister.exec_stmt(update, options) except Exception as error: # We rollback and re-raise persister.rollback() raise _errors.CredentialError("Error updating password: {0}".format( str(error))) persister.commit()
def _hash_password(username, password, protocol, config, realm=None): """Hash password base on protocol. If the OpenSSL is not installed, the function uses built-in functions from hashlib. :raises errors.CredentialError: if protocol is not implemented """ if not password: return if protocol in ('xmlrpc', ) and not realm and config: section = 'protocol.' + protocol realm = config.get(section, 'realm', vars=FABRIC_PROTOCOL_DEFAULTS[section]) if protocol == 'xmlrpc': return hashlib.md5( '{user}:{realm}:{secret}'.format(user=username, realm=FABRIC_REALM_XMLRPC, secret=password)).hexdigest() elif protocol == 'mysql': return hashlib.sha1( hashlib.sha1(password).digest()).hexdigest().upper() raise _errors.CredentialError( "Password hasing for protocol '{0}' is not implemented.".format( protocol))
def execute(self, username, protocol=None, force=False): """Delete a Fabric user """ username, password, protocol = self._ask_credentials( username, ask_password=False) # Check if user exists if not get_user(username, protocol, persister=self.persister): raise _errors.CredentialError("No user {user}/{protocol}".format( user=username, protocol=protocol)) if not self.options.force: result = confirm("Really remove user {user}/{protocol}?".format( user=username, protocol=protocol), default='n') if result is not True: return try: self.persister.begin() User.delete_user(username=username, protocol=protocol) except Exception: # We rollback and re-raise self.persister.rollback() print "Removing user cancelled." raise self.persister.commit() print "Fabric user deleted."
def execute(self, username, protocol=None, roles=None): """Add a new Fabric user""" username, password, protocol = self._ask_credentials( username, ask_password=False) # Check if user exists if get_user(username, protocol, persister=self.persister): raise _errors.CredentialError( "User {user}/{protocol} already exists".format( user=username, protocol=protocol)) password = _get_password('Password: '******',')] else: print "\nSelect role(s) for new user" _role_listing() role_list = _role_selection(persister=self.persister, choices=role_list) try: self.persister.begin() user_id = User.add_user(username, password, protocol) if not role_list: print( "You can always assign roles later using the " "'user addrole' command") else: for role_id in role_list: User.add_user_role(user_id, int(role_id)) except Exception: # Whatever happens, we rollback and re-raise self.persister.rollback() print "Adding user cancelled." raise self.persister.commit() print "Fabric user added."
def _role_selection(message=None, choices=None, persister=None): """Offers user to select roles on the console :param persister: A valid handle to the state store. :param message: Message shown just before prompt. :param choices: Do not ask, just process choices (string or sequence). :return: List of role IDs or role names. :rtype: list :raises errors.CredentialError: if invalid role was given """ if not persister: persister = _persistence.current_persister() if not choices: if not message: message = "\nEnter comma separated list of role IDs or names: " choices = raw_input(message) if not choices.strip(): return [] if isinstance(choices, str): choices = choices.split(',') options = { "raw": False, "fetch": False, "columns": True, } cur = persister.exec_stmt("SELECT role_id, name FROM roles", options) valid_role_ids = [] roles = {} for row in cur: roles[row.name] = row.role_id if str(row.role_id) not in valid_role_ids: valid_role_ids.extend([str(row.role_id), row.name]) try: if not all(rid.strip() in valid_role_ids for rid in choices): raise ValueError except ValueError: raise _errors.CredentialError("Found invalid role.") # Only return role IDs result = [] for rid in choices: try: result.append(int(rid)) except ValueError: # We got name result.append(roles[rid.strip()]) return result
def _get_password(prompt=None): """Get password from using STDIN :param prompt: String describing what input is required. :returns: Password as string. :rtype: str :raises CredentialError: if passwords did not match """ if not prompt: prompt = 'Password: '******'Repeat Password: ').strip() if password != repeat: raise _errors.CredentialError("Passwords did not match!") return password
def validate_protocol(protocol, allow_empty=False): """Validates a protocol :param protocol: The protocol we need to check. :param allow_empty: Whether to allow empty protocol. :returns: Stripped protocol or None when protocol was empty :rtype: str :raises ValueError: if protocol is not acceptable. """ protocol = protocol.strip() if not protocol and allow_empty: return None valid = r"[[email protected]]" match = re.match(r"^" + valid + "+$", protocol) if not match: raise _errors.CredentialError( "Invalid protocol (was not {0})".format(valid)) return protocol
def validate_username(username, allow_empty=False): """Validates a username :param username: The username we need to check. :param allow_empty: Whether to allow empty username. :returns: Stripped username or None when username was empty :rtype: str :raises ValueError: if username is not acceptable. """ username = username.strip() if not username and allow_empty: return None valid = r"[[email protected]]" match = re.match(r"^" + valid + "+$", username) if not match: raise _errors.CredentialError( "Invalid username (was not {0})".format(valid)) return username
def check_initial_setup(config, persister, check_only=False): """Check if admin user has password and if not sets it :param persister: A valid handle to the state store. """ # Fetch which protocols have no passwords set for user 'admin' protocols = [] for key in FABRIC_PROTOCOL_DEFAULTS.keys(): if key.startswith('protocol.'): tmp = key.split('.', 2)[1] if tmp not in protocols: user = get_user('admin', tmp, persister) if not user or not user.password: protocols.append(tmp) # Try setting password for 'admin' user from configuration file for protocol in tuple(protocols): # we change protocols, loop over copy section = 'protocol.' + protocol try: username = config.get(section, 'user') except _config.NoOptionError: username = '******' # If there is no password, we have to ask for one. try: password = config.get(section, 'password').strip() # No password, so we have to ask for one if password == '': break except _config.NoOptionError: # No password, so we have to ask for one continue persister.begin() try: if username != 'admin': _LOGGER.info("Adding user %s/%s", username, protocol) user_id = User.add_user(username, password, protocol, persister=persister) else: _LOGGER.info("Initial password for %s/%s set", username, protocol) _change_password(username, password, protocol, config, persister) except _errors.CredentialError as error: print "Setting password cancelled." _LOGGER.debug(str(error)) print error persister.rollback() else: persister.commit() msg = ( "Password set for {user}/{protocol} from configuration file." ).format(user=username, protocol=protocol) print msg _LOGGER.info(msg) if username != 'admin': print "Note: {user}/{protocol} has no roles set!".format( user=username, protocol=protocol) else: # No need to ask for password later for this protocol protocols.remove(protocol) if not protocols: # Passwords are set return if check_only and protocols: print( "\nPassword for admin user is empty. Please run\n\n" " shell> mysqlfabric user password admin\n\n" "Make sure the password is empty or commented in section\n" "[protocol.xmlrpc] of the configuration file before executing the\n" "above command.") raise _errors.CredentialError("Check empty admin password failed") print "Finishing initial setup" print "=======================" print "Password for admin user is not yet set." password = None while True: password = _get_password("Password for {user}/{protocol}: ".format( user='******', protocol='xmlrpc')) if password: break if password: for protocol in protocols: try: _change_password('admin', password, protocol, config, persister) except _errors.CredentialError as error: print "Setting password cancelled." _LOGGER.debug(str(error)) print error else: print "Password set." else: # Making sure password is set and there is an error message raise _errors.CredentialError( "Password for admin can not be empty. Use `user password` command." ) persister.commit()
def execute(self, username, protocol=None, roles=None): """Change roles for a Fabric user """ username, password, protocol = self._ask_credentials( username, ask_password=False) # Check if user exists user = get_user(username, protocol) if not user: raise _errors.CredentialError("No user {user}/{protocol}".format( user=username, protocol=protocol)) exit_text_removed = ("Roles for {user}/{protocol} removed.").format( user=username, protocol=protocol) exit_text_updated = ("Roles for {user}/{protocol} updated.").format( user=username, protocol=protocol) confirmed = False role_list = [] if not roles: options = { "fetch": False, "params": (user.user_id, ), "columns": True, } cur = self.persister.exec_stmt( "SELECT role_id FROM user_roles WHERE user_id = %s", options) current_roles = [row.role_id for row in cur] print "\nSelect new role(s) for user, replacing current roles." print "Current roles are marke with an X." _role_listing(selected=current_roles) role_list = _role_selection(persister=self.persister) if not role_list: confirm_text = ( "Remove all roles of user {user}/{protocol}?").format( user=username, protocol=protocol) exit_text = exit_text_removed default = 'n' else: confirm_text = ( "Replace roles of user {user}/{protocol}?").format( user=username, protocol=protocol) exit_text = exit_text_updated default = 'y' confirmed = confirm(confirm_text.format(username=user.username), default=default) else: # From command line option --roles confirmed = True if roles.strip() == "0": exit_text = exit_text_removed role_list = [] else: exit_text = exit_text_updated role_list = [role.strip() for role in roles.split(',')] role_list = _role_selection(persister=self.persister, choices=role_list) if confirmed: try: self.persister.begin() options = { "fetch": False, "params": (user.user_id, ), "columns": True, } self.persister.exec_stmt( "DELETE FROM user_roles WHERE user_id = %s", options) for role_id in role_list: User.add_user_role(user.user_id, int(role_id)) except Exception: # Whatever happens, we rollback and re-raise self.persister.rollback() print "Changing roles for user cancelled." raise else: self.persister.commit() print exit_text else: print "Changing roles cancelled."