Exemple #1
0
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")
Exemple #2
0
    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")
Exemple #3
0
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()
Exemple #4
0
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))
Exemple #5
0
    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."
Exemple #6
0
    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
Exemple #8
0
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
Exemple #9
0
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
Exemple #10
0
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
Exemple #11
0
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()
Exemple #12
0
    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."