server — Faraday 0.1 documentation

# Source code for server

    #!/usr/bin/python2.7
    # -*- coding: utf-8 -*-
    '''
    Faraday Penetration Test IDE
    Copyright (C) 2016  Infobyte LLC (http://www.infobytesec.com/)
    See the file 'doc/LICENSE' for the license information

    '''
    import requests
    import json
    from persistence.server.utils import force_unique
    from persistence.server.server_io_exceptions import (WrongObjectSignature,
                                                         CantCommunicateWithServerError,
                                                         ConflictInDatabase,
                                                         ResourceDoesNotExist,
                                                         Unauthorized,
                                                         MoreThanOneObjectFoundByID)

    from persistence.server.changes_stream import CouchChangesStream

    # NOTE: Change is you want to use this module by itself.
    # If FARADAY_UP is False, SERVER_URL must be a valid faraday server url
    FARADAY_UP = True
    SERVER_URL = "http://127.0.0.1:5984"

    def _conf():
        from config.configuration import getInstanceConfiguration
        CONF = getInstanceConfiguration()
        return CONF

    def _get_base_server_url():
        if FARADAY_UP:
            server_url = _conf().getCouchURI()
        else:
            server_url = SERVER_URL
        return server_url

    def _create_server_api_url():
        """Return the server's api url."""
        return "{0}/_api".format(_get_base_server_url())

    def _create_server_get_url(workspace_name, object_name=None):
        """Creates a url to get from the server. Takes the workspace name
        as a string, an object_name paramter which is the object you want to
        query as a string ('hosts', 'interfaces', etc) .

        object_name may be None if you want to get the workspace itself.

        Return the get_url as a string.
        """
        object_name = "/{0}".format(object_name) if object_name else ""
        get_url = '{0}/ws/{1}{2}'.format(_create_server_api_url(),
                                         workspace_name,
                                         object_name)
        return get_url

    def _create_server_post_url(workspace_name, object_id):
        server_api_url = _create_server_api_url()
        post_url = '{0}/ws/{1}/doc/{2}'.format(server_api_url, workspace_name, object_id)
        return post_url

    def _create_server_delete_url(workspace_name, object_id):
        return _create_server_post_url(workspace_name, object_id)

    # XXX: COUCH IT!
    def _create_couch_get_url(workspace_name, object_id):
        server_url = _get_base_server_url()
        return "{0}/{1}/{2}".format(server_url, workspace_name, object_id)

    # XXX: COUCH IT!
    def _create_couch_post_url(workspace_name, object_id):
        return _create_couch_get_url(workspace_name, object_id)

    # XXX: COUCH IT!
    def _create_couch_db_url(workspace_name):
        server_base_url = _get_base_server_url()
        db_url = '{0}/{1}'.format(server_base_url, workspace_name)
        return db_url

    def _create_server_db_url(workspace_name):
        server_api_url = _create_server_api_url()
        db_url = '{0}/ws/{1}'.format(server_api_url, workspace_name)
        return db_url

    def _unsafe_io_with_server(server_io_function, server_expected_response,
                               server_url, **payload):
        """A wrapper for functions which deals with I/O to or from the server.
        It calls the server_io_function with url server_url and the payload,
        raising an CantCommunicateWithServerError if the response wasn't
        server_expected_response or if there was a Connection Error.

        Return the response from the server.
        """
        try:
            answer = server_io_function(server_url, **payload)
            if answer.status_code == 409 and answer.json()['error'] == 'conflict':
                raise ConflictInDatabase(answer)
            if answer.status_code == 404:
                raise ResourceDoesNotExist(server_url)
            if answer.status_code == 403 or answer.status_code == 401:
                raise Unauthorized(answer)
            if answer.status_code != server_expected_response:
                raise requests.exceptions.RequestException(response=answer)
        except requests.exceptions.RequestException:
            raise CantCommunicateWithServerError(server_io_function, server_url, payload)
        return answer

    def _parse_json(response_object):
        """Takes a response object and return its response as a dictionary."""
        try:
            return response_object.json()
        except ValueError:
            return {}

    def _get(request_url, **params):
        """Get from the request_url. Takes an arbitrary number of parameters
        to customize the request_url if necessary.

        Will raise a CantCommunicateWithServerError if requests cant stablish
        connection to server or if response is not equal to 200.

        Return a dictionary with the information in the json.
        """
        return _parse_json(_unsafe_io_with_server(requests.get,
                                                  200,
                                                  request_url,
                                                  params=params))

    def _put(post_url, update=False, expected_response=201, **params):
        """Put to the post_url. If update is True, try to get the object
        revision first so as to update the object in Couch. You can
        customize the expected response (it should be 201, but Couchdbkit returns
        200, so...). Also take an arbitrary number of parameters to put into the
        post_url.

        Will raise a CantCommunicateWithServerError if requests cant stablish
        connection to server or if response is not equal to 201.

        Return a dictionary with the response from couchdb, which looks like this:
        {u'id': u'61', u'ok': True, u'rev': u'1-967a00dff5e02add41819138abb3284d'}
        """
        if update:
            last_rev = _get(post_url)['_rev']
            params['_rev'] = last_rev
        return _parse_json(_unsafe_io_with_server(requests.put,
                                                  expected_response,
                                                  post_url,
                                                  json=params))

    def _delete(delete_url, database=False):
        """Deletes the object on delete_url. If you're deleting a database,
        specify the database parameter to True"""
        params = {}
        if not database:
            last_rev = _get(delete_url)['_rev']
            params = {'rev': last_rev}
        return _parse_json(_unsafe_io_with_server(requests.delete,
                                                  200,
                                                  delete_url,
                                                  params=params))

    def _get_raw_hosts(workspace_name, **params):
        """Take a workspace_name and an arbitrary number of params and return
        a dictionary with the hosts table."""
        request_url = _create_server_get_url(workspace_name, 'hosts')
        return _get(request_url, **params)

    def _get_raw_vulns(workspace_name, **params):
        """Take a workspace_name and an arbitrary number of params and return
        a dictionary with the vulns table."""
        request_url = _create_server_get_url(workspace_name, 'vulns')
        return _get(request_url, **params)

    def _get_raw_interfaces(workspace_name, **params):
        """Take a workspace_name and an arbitrary number of params and return
        a dictionary with the interfaces table."""
        request_url = _create_server_get_url(workspace_name, 'interfaces')
        return _get(request_url, **params)

    def _get_raw_services(workspace_name, **params):
        """Take a workspace_name and an arbitrary number of params and return
        a dictionary with the services table."""
        request_url = _create_server_get_url(workspace_name, 'services')
        return _get(request_url, **params)

    def _get_raw_notes(workspace_name, **params):
        """Take a workspace name and an arbitrary number of params and
        return a dictionary with the notes table."""
        request_url = _create_server_get_url(workspace_name, 'notes')
        return _get(request_url, **params)

    def _get_raw_credentials(workspace_name, **params):
        """Take a workspace name and an arbitrary number of params and
        return a dictionary with the credentials table."""
        request_url = _create_server_get_url(workspace_name, 'credentials')
        return _get(request_url, **params)

    def _get_raw_commands(workspace_name, **params):
        request_url = _create_server_get_url(workspace_name, 'commands')
        return _get(request_url, **params)

    def _get_raw_workspace_summary(workspace_name):
        request_url = _create_server_get_url(workspace_name, 'summary')
        return _get(request_url)

    # XXX: COUCH IT!
    def _save_to_couch(workspace_name, faraday_object_id, **params):
        post_url = _create_couch_post_url(workspace_name, faraday_object_id)
        return _put(post_url, update=False, **params)

    # XXX: COUCH IT!
    def _update_in_couch(workspace_name, faraday_object_id, **params):
        post_url = _create_server_post_url(workspace_name, faraday_object_id)
        return _put(post_url, update=True, **params)

    def _save_to_server(workspace_name, faraday_object_id, **params):
        post_url = _create_server_post_url(workspace_name, faraday_object_id)
        return _put(post_url, update=False, expected_response=200, **params)

    def _update_in_server(workspace_name, faraday_object_id, **params):
        post_url = _create_server_post_url(workspace_name, faraday_object_id)
        return _put(post_url, update=True, expected_response=200, **params)

    def _save_db_to_server(db_name, **params):
        post_url = _create_server_db_url(db_name)
        return _put(post_url, expected_response=200, **params)

    # XXX: SEMI COUCH IT!
    def _delete_from_couch(workspace_name, faraday_object_id):
        delete_url = _create_server_delete_url(workspace_name, faraday_object_id)
        return _delete(delete_url)

    # XXX: COUCH IT!
    def _couch_changes(workspace_name, **params):
        return CouchChangesStream(workspace_name,
                                  _create_couch_db_url(workspace_name),
                                  **params)

    def _get_faraday_ready_dictionaries(workspace_name, faraday_object_name,
                                        faraday_object_row_name, full_table=True,
                                        **params):
        """Takes a workspace_name (str), a faraday_object_name (str),
        a faraday_object_row_name (str) and an arbitrary number of params.
        Return a list of dictionaries that hold the information for the objects
        in table faraday_object_name.

        The full_table paramether may be used to get the full dictionary instead
        of just the one inside the 'value' key which holds information about the
        object.

        Preconditions:
        faraday_object_name == 'host', 'vuln', 'interface', 'service', 'note'
        or 'credential'

        faraday_object_row_name must be the key to the dictionary which holds
        the information of the object per se in the table. most times this is 'rows'
        """
        object_to_func = {'hosts': _get_raw_hosts,
                          'vulns': _get_raw_vulns,
                          'interfaces': _get_raw_interfaces,
                          'services': _get_raw_services,
                          'notes': _get_raw_notes,
                          'credentials': _get_raw_credentials,
                          'commands': _get_raw_commands}

        appropiate_function = object_to_func[faraday_object_name]
        appropiate_dictionary = appropiate_function(workspace_name, **params)
        faraday_ready_dictionaries = []
        if appropiate_dictionary:
            for raw_dictionary in appropiate_dictionary[faraday_object_row_name]:
                if not full_table:
                    faraday_ready_dictionaries.append(raw_dictionary['value'])
                else:
                    faraday_ready_dictionaries.append(raw_dictionary)
        return faraday_ready_dictionaries

    def get_hosts(workspace_name, **params):
        """Get hosts from the server.

        Args:
            workspace_name (str): the workspace from which to get the hosts.
            **params: any of valid request parameters for CouchDB.

        Returns:
            A dictionary containing the hosts matching the query.
        """
        return _get_faraday_ready_dictionaries(workspace_name, 'hosts',
                                               'rows', **params)

    def get_all_vulns(workspace_name, **params):
        """Get vulns, both normal and web, from the server.

        Args:
            workspace_name (str): the workspace from which to get the vulns.
            **params: any of valid request parameters for CouchDB.

        Returns:
            A dictionary containing the vulns matching the query.
        """
        return _get_faraday_ready_dictionaries(workspace_name, 'vulns',
                                               'vulnerabilities', **params)

    def get_vulns(workspace_name, **params):
        """Get only normal vulns from the server.

        Args:
            workspace_name (str): the workspace from which to get the vulns.
            **params: any of valid request parameters for CouchDB.

        Returns:
            A dictionary containing the vulns matching the query.
        """
        return get_all_vulns(workspace_name, type='Vulnerability', **params)

    def get_web_vulns(workspace_name, **params):
        """Get only web vulns from the server.

        Args:
            workspace_name (str): the workspace from which to get the vulns.
            **params: any of valid request parameters for CouchDB.

        Returns:
            A dictionary containing the vulns matching the query.
        """
        return get_all_vulns(workspace_name, type="VulnerabilityWeb", **params)

    def get_interfaces(workspace_name, **params):
        """Get interfaces from the server.

        Args:
            workspace_name (str): the workspace from which to get the interfaces.
            **params: any of valid request parameters for CouchDB.

        Returns:
            A dictionary containing the interfaces matching the query.
        """
        return _get_faraday_ready_dictionaries(workspace_name, 'interfaces',
                                               'interfaces', **params)

    def get_services(workspace_name, **params):
        """Get services from the server.

        Args:
            workspace_name (str): the workspace from which to get the services.
            **params: any of valid request parameters for CouchDB.

        Returns:
            A dictionary containing the services matching the query.
        """
        return _get_faraday_ready_dictionaries(workspace_name, 'services',
                                               'services', **params)

    def get_credentials(workspace_name, **params):
        """Get credentials from the server.

        Args:
            workspace_name (str): the workspace from which to get the credentials.
            **params: any of valid request parameters for CouchDB.

        Returns:
            A dictionary containing the credentials matching the query.
        """
        return _get_faraday_ready_dictionaries(workspace_name, 'credentials',
                                               'rows', **params)

    def get_notes(workspace_name, **params):
        """Get notes from the server.

        Args:
            workspace_name (str): the workspace from which to get the notes.
            **params: any of valid request parameters for CouchDB.

        Returns:
            A dictionary containing the notes matching the query.
        """
        return _get_faraday_ready_dictionaries(workspace_name, 'notes',
                                               'rows', **params)

    def get_commands(workspace_name, **params):
        """Get commands from the server.

        Args:
            workspace_name (str): the workspace from which to get the commands.
            **params: any of valid request parameters for CouchDB.

        Returns:
            A dictionary containing the commands matching the query.
        """
        return _get_faraday_ready_dictionaries(workspace_name, 'commands',
                                               'commands', **params)

    def get_objects(workspace_name, object_signature, **params):
        """Get any type of object from the server, be it hosts, vulns, interfaces,
        services, credentials, commands or notes.

        Args:
            workspace_name (str): the workspace from which to get the commands.
            object_signature (str): the type of object to get. Must equal 'hosts',
                'vulns', 'interfaces', 'services', 'credentials', 'notes' or 'commands'
            **params: any of valid request parameters for CouchDB.

        Returns:
            A dictionary containing the commands matching the query.

        Raises:
            WrongObjectSignature: if the object_signature string didn't match
            a faraday object.
        """
        object_to_func = {'hosts': get_hosts,
                          'vulns': get_vulns,
                          'interfaces': get_interfaces,
                          'services': get_services,
                          'credentials': get_credentials,
                          'notes': get_notes,
                          'commands': get_commands}
        try:
            appropiate_function = object_to_func[object_signature]
        except KeyError:
            raise WrongObjectSignature(object_signature)

        return appropiate_function(workspace_name, **params)

    # cha cha cha chaaaanges!
    def get_changes_stream(workspace_name, since=0, heartbeat='1000', **extra_params):
        return _couch_changes(workspace_name, since=since, feed='continuous',
                              heartbeat=heartbeat, **extra_params)

    def get_workspaces_names():
        """Returns:
            A dictionary with a list with the workspaces names."""
        return _get("{0}/ws".format(_create_server_api_url()))

    # XXX: COUCH IT!
    def _clean_up_stupid_couch_response(response_string):
        """Couch likes to give invalid jsons as a response :). So nice."""
        interesting_part = "{".join(response_string.split("{")[1:])
        almost_there = interesting_part.split("}")[0:-1]
        ok_yeah = "}".join(almost_there)
        hopefully_valid_json = "{{{0}}}".format(ok_yeah)
        return json.loads(hopefully_valid_json)

    # XXX: COUCH IT!
    # COUCH IT LEVEL: REVOLUTIONS
    def get_object_before_last_revision(workspace_name, object_id):
        """Get an object before its last revision. Useful to get information about
        recently deleted objects.

        Warning:
            Error-pronce process. You should check for 'None' after usage,
            as that's the return value if any problem arose during execution.

        Args:
            workspace_name (str): the workspace where the object was
            object_id (str): the id of the object

        Returns:
            A dictionary with the object's information.
        """
        get_url = _create_couch_get_url(workspace_name, object_id)
        response = _unsafe_io_with_server(requests.get, 200, get_url,
                                          params={'revs': 'true', 'open_revs': 'all'})
        try:
            valid_json_response = _clean_up_stupid_couch_response(response.text)
        except ValueError:
            return None
        try:
            id_before_del = valid_json_response['_revisions']['ids'][1]
            new_number_for_rev = valid_json_response['_revisions']['start'] - 1
        except KeyError:  # one if never too safe when you call a function called "_clean_up_stupid_couch_response"
            return None

        rev_id_before_del = "{0}-{1}".format(new_number_for_rev, id_before_del)
        object_dict = _get(get_url, rev=rev_id_before_del)
        return object_dict

    def get_object(workspace_name, object_signature, object_id):
        """Get an unique object of arbitrary type.

        Args:
            workspace_name (str): the workspace where the object should be found.
            object_signature (str): must be either 'hosts', 'vulns', 'interfaces'
                'services', 'credentials', 'notes' or 'commands'.
            object_id (str): the id of the object

        Returns:
            A dictionary containing information about the object.

        Raises:
            MoreThanOneObjectFoundByID: if for some reason the object_id is shared
            by two or more objects. This should never happen. If it does,
            contact Infobyte LCC.
        """
        objects = get_objects(workspace_name, object_signature, couchid=object_id)
        return force_unique(objects)

    def get_host(workspace_name, host_id):
        """Get an unique host.

        Args:
            workspace_name (str): the workspace where the object should be found.
            host_id (str): the id of the host

        Returns:
            A dictionary containing information about the host.

        Raises:
            MoreThanOneObjectFoundByID: if for some reason the host id is shared
            by two or more objects. This should never happen. If it does,
            contact Infobyte LCC.
        """
        return force_unique(get_hosts(workspace_name, couchid=host_id))

    def get_vuln(workspace_name, vuln_id):
        """Get an unique vuln.

        Args:
            workspace_name (str): the workspace where the object should be found.
            vuln_id (str): the id of the vuln

        Returns:
            A dictionary containing information about the vuln.

        Raises:
            MoreThanOneObjectFoundByID: if for some reason the vuln id is shared
            by two or more objects. This should never happen. If it does,
            contact Infobyte LCC.
        """
        return force_unique(get_vulns(workspace_name, couchid=vuln_id))

    def get_web_vuln(workspace_name, vuln_id):
        """Get an unique web vuln.

        Args:
            workspace_name (str): the workspace where the object should be found.
            web vuln_id (str): the id of the web vuln

        Returns:
            A dictionary containing information about the web vuln.

        Raises:
            MoreThanOneObjectFoundByID: if for some reason the web vuln id is shared
            by two or more objects. This should never happen. If it does,
            contact Infobyte LCC.
        """
        return force_unique(get_web_vulns(workspace_name, couchid=vuln_id))

    def get_interface(workspace_name, interface_id):
        """Get an unique interface.

        Args:
            workspace_name (str): the workspace where the object should be found.
            interface_id (str): the id of the interface

        Returns:
            A dictionary containing information about the interface.

        Raises:
            MoreThanOneObjectFoundByID: if for some reason the interface id is shared
            by two or more objects. This should never happen. If it does,
            contact Infobyte LCC.
        """
        return force_unique(get_interfaces(workspace_name, couchid=interface_id))

    def get_service(workspace_name, service_id):
        """Get an unique service.

        Args:
            workspace_name (str): the workspace where the object should be found.
            service_id (str): the id of the service

        Returns:
            A dictionary containing information about the service.

        Raises:
            MoreThanOneObjectFoundByID: if for some reason the service id is shared
            by two or more objects. This should never happen. If it does,
            contact Infobyte LCC.
        """
        return force_unique(get_services(workspace_name, couchid=service_id))

    def get_note(workspace_name, note_id):
        """Get an unique note.

        Args:
            workspace_name (str): the workspace where the object should be found.
            note_id (str): the id of the note

        Returns:
            A dictionary containing information about the note.

        Raises:
            MoreThanOneObjectFoundByID: if for some reason the note id is shared
            by two or more objects. This should never happen. If it does,
            contact Infobyte LCC.
        """
        return force_unique(get_notes(workspace_name, couchid=note_id))

    def get_credential(workspace_name, credential_id):
        """Get an unique credential.

        Args:
            workspace_name (str): the workspace where the object should be found.
            credential_id (str): the id of the credential

        Returns:
            A dictionary containing information about the credential.

        Raises:
            MoreThanOneObjectFoundByID: if for some reason the credential id is shared
            by two or more objects. This should never happen. If it does,
            contact Infobyte LCC.
        """
        return force_unique(get_services(workspace_name, couchid=credential_id))

    def get_command(workspace_name, command_id):
        """Get an unique command.

        Args:
            workspace_name (str): the workspace where the object should be found.
            command_id (str): the id of the command

        Returns:
            A dictionary containing information about the command.

        Raises:
            MoreThanOneObjectFoundByID: if for some reason the command id is shared
            by two or more objects. This should never happen. If it does,
            contact Infobyte LCC.
        """
        return force_unique(get_commands(workspace_name, couchid=command_id))

    def get_workspace(workspace_name, **params):
        """Get an unique command.

        Args:
            command_name (str): the command where the object should be found.
            command_id (str): the id of the command

        Returns:
            A dictionary containing information about the command.

        Raises:
            MoreThanOneObjectFoundByID: if for some reason the command id is shared
            by two or more objects. This should never happen. If it does,
            contact Infobyte LCC.
        """
        request_url = _create_server_get_url(workspace_name)
        return _get(request_url, **params)

    def get_workspace_summary(workspace_name):
        """Get a collection of data about the workspace.

        Args:
            workspace_name (str): the workspace to get the stats from.

        Returns:
            A dictionary with the workspace's information
        """
        return _get_raw_workspace_summary(workspace_name)['stats']

    def get_workspace_numbers(workspace_name):
        """Get the number of hosts, interfaces, services and vulns in the workspace.

        Args:
            workspace_name (str): the name of the workspace to query

        Return:
            A tuple of 4 elements with the amounts of hosts, interfaces, services and vulns.
        """
        stats = _get_raw_workspace_summary(workspace_name)['stats']
        return stats['hosts'], stats['interfaces'], stats['services'], stats['total_vulns']

    def get_hosts_number(workspace_name, **params):
        """
        Args:
            workspace_name (str): the name of the workspace to query
            **params: any of the Couchdb request parameters

        Returns:
            The amount of hosts in the workspace as an integer.
        """
        return int(get_workspace_summary(workspace_name)['hosts'])

    def get_services_number(workspace_name, **params):
        """
        Args:
            workspace_name (str): the name of the workspace to query
            **params: any of the Couchdb request parameters

        Returns:
            The amount of services in the workspace as an integer.
        """
        return int(get_workspace_summary(workspace_name)['interfaces'])

    def get_interfaces_number(workspace_name, **params):
        """
        Args:
            workspace_name (str): the name of the workspace to query
            **params: any of the Couchdb request parameters

        Returns:
            The amount of interfaces in the workspace as an integer.
        """
        return int(get_workspace_summary(workspace_name)['interfaces'])

    def get_vulns_number(workspace_name, **params):
        """
        Args:
            workspace_name (str): the name of the workspace to query
            **params: any of the Couchdb request parameters

        Returns:
            The amount of vulns in the workspace as an integer.
        """
        return int(get_workspace_summary(workspace_name)['total_vulns'])

    def get_notes_number(workspace_name, **params):
        """
        Args:
            workspace_name (str): the name of the workspace to query
            **params: any of the Couchdb request parameters

        Returns:
            The amount of notes in the workspace as an integer.
        """
        return int(get_workspace_summary(workspace_name)['notes'])

    def get_credentials_number(workspace_name, **params):
        """
        Args:
            workspace_name (str): the name of the workspace to query
            **params: any of the Couchdb request parameters

        Returns:
            The amount of credentials in the workspace as an integer.
        """
        return int(_get_raw_credentials(workspace_name, **params))

    def get_commands_number(workspace_name, **params):
        """
        Args:
            workspace_name (str): the name of the workspace to query
            **params: any of the Couchdb request parameters

        Returns:
            The amount of commands in the workspace as an integer.
        """
        return int(_get_raw_commands(workspace_name, **params))

    def create_host(workspace_name, id, name, os, default_gateway,
                    description="", metadata=None, owned=False, owner="",
                    parent=None):
        """Create a host.

        Args:
            workspace_name (str): the name of the workspace where the host will be saved.
            id (str): the id of the host. Must be unique.
            name (str): the host's name
            os (str): the operative system of the host
            default_gateway (str): the host's default_gateway
            description (str): a description.
            metadata: a collection of metadata. If you don't know the metada. leave
                on None.
            owned (bool): is the host owned or not?
            owner (str): an owner for the host
            parent (Faraday Object): the host's parent. If you don't know this, leave
                on None.

        Returns:
            A dictionary with the server's response.
        """
        return _save_to_server(workspace_name,
                               id,
                               name=name, os=os,
                               default_gateway=default_gateway,
                               owned=owned,
                               metadata=metadata,
                               owner=owner,
                               parent=parent,
                               description=description,
                               type="Host")

    def update_host(workspace_name, id, name, os, default_gateway,
                    description="", metadata=None, owned=False, owner="",
                    parent=None):
        """Updates a host.

        Args:
            workspace_name (str): the name of the workspace where the host will be saved.
            id (str): the id of the host. Must be unique.
            name (str): the host's name
            os (str): the operative system of the host
            default_gateway (str): the host's default_gateway
            description (str): a description.
            metadata: a collection of metadata. If you don't know the metada. leave
                on None, it will be created automatically.
            owned (bool): is the host owned or not?
            owner (str): an owner for the host
            parent (Faraday Object): the host's parent. If you don't know this, leave
                on None.

        Returns:
            A dictionary with the server's response.
        """
        return _update_in_server(workspace_name,
                                 id,
                                 name=name, os=os,
                                 default_gateway=default_gateway,
                                 owned=owned,
                                 metadata=metadata,
                                 owner=owner,
                                 parent=parent,
                                 description=description,
                                 type="Host")

    # TODO: FIX. If you actually pass ipv4 or ipv6 as None, which are the defaults
    # values here, the server will complain. Review if this should be fixed on
    # the client or on the server.
    def create_interface(workspace_name, id, name, description, mac,
                         owned=False, owner="", hostnames=None, network_segment=None,
                         ipv4=None, ipv6=None, metadata=None):
        """Creates an interface.

        Warning:
            DO NOT leave ipv4 and ipv6 values on None, as the default indicated.
            This is a known bug and we're working to fix it. ipv4 and ipv6 need to
            be valid IP addresses, or, in case one of them is irrelevant, empty strings.

        Args:
            workspace_name (str): the name of the workspace where the interface will be saved.
            id (str): the id of the interface. Must be unique.
            name (str): the interface's name
            description (str): a description.
            mac (str) the mac address of the interface
            owned (bool): is the host owned or not?
            owner (str): an owner for the host
            hostnames ([str]): a list of hostnames
            network_segment (str): the network segment
            ipv4 (str): the ipv4 direction of the interface.
            ipv6 (str): the ipv6 direction of the interface.
            metadata: a collection of metadata. If you don't know the metada. leave
                on None, it will be created automatically.

        Returns:
            A dictionary with the server's response.
        """
        return _save_to_server(workspace_name,
                               id,
                               name=name,
                               description=description,
                               mac=mac,
                               owned=owned,
                               owner=owner,
                               hostnames=hostnames,
                               network_segment=network_segment,
                               ipv4=ipv4,
                               ipv6=ipv6,
                               type="Interface",
                               metadata=metadata)

    def update_interface(workspace_name, id, name, description, mac,
                         owned=False, owner="", hostnames=None, network_segment=None,
                         ipv4=None, ipv6=None, metadata=None):
        """Creates an interface.

        Warning:
            DO NOT leave ipv4 and ipv6 values on None, as the default indicated.
            This is a known bug and we're working to fix it. ipv4 and ipv6 need to
            be valid IP addresses, or, in case one of them is irrelevant, empty strings.

        Args:
            workspace_name (str): the name of the workspace where the interface will be saved.
            id (str): the id of the interface. Must be unique.
            name (str): the interface's name
            description (str): a description.
            mac (str) the mac address of the interface
            owned (bool): is the host owned or not?
            owner (str): an owner for the host
            hostnames ([str]): a list of hostnames
            network_segment (str): the network segment
            ipv4 (str): the ipv4 direction of the interface.
            ipv6 (str): the ipv6 direction of the interface.
            metadata: a collection of metadata. If you don't know the metada. leave
                on None, it will be created automatically.

        Returns:
            A dictionary with the server's response.
        """
        return _update_in_server(workspace_name,
                                 id,
                                 name=name,
                                 description=description,
                                 mac=mac,
                                 owned=owned,
                                 owner=owner,
                                 hostnames=hostnames,
                                 network_segment=network_segment,
                                 ipv4=ipv4,
                                 ipv6=ipv6,
                                 type="Interface",
                                 metadata=metadata)

    def create_service(workspace_name, id, name, description, ports,
                       owned=False, owner="", protocol="", status="", version="",
                       metadata=None):
        """Creates a service.

        Args:
            workspace_name (str): the name of the workspace where the service will be saved.
            id (str): the id of the service. Must be unique.
            name (str): the host's name
            description (str): a description.
            ports ([str]): a list of ports for the service.
            owned (bool): is the service owned or not?
            owner (str): an owner for the service
            protocol (str): the service's protocol
            status (str): the service's status
            version (str): the service's version
            metadata: a collection of metadata. If you don't know the metada. leave
                on None, it will be created automatically.

        Returns:
            A dictionary with the server's response.
        """
        return _save_to_server(workspace_name,
                               id,
                               name=name,
                               description=description,
                               ports=ports,
                               owned=owned,
                               owner=owner,
                               protocol=protocol,
                               status=status,
                               version=version,
                               type="Service",
                               metadata=metadata)

    def update_service(workspace_name, id, name, description, ports,
                       owned=False, owner="", protocol="", status="", version="",
                       metadata=None):
        """Creates a service.

        Args:
            workspace_name (str): the name of the workspace where the service will be saved.
            id (str): the id of the service. Must be unique.
            name (str): the service's name
            description (str): a description.
            ports ([str]): a list of ports for the service.
            owned (bool): is the host owned or not?
            owner (str): an owner for the service
            protocol (str): the service's protocol
            status (str): the service's status
            version (str): the service's version
            metadata: a collection of metadata. If you don't know the metada. leave
                on None, it will be created automatically.

        Returns:
            A dictionary with the server's response.
        """
        return _update_in_server(workspace_name,
                                 id,
                                 name=name,
                                 description=description,
                                 ports=ports,
                                 owned=owned,
                                 owner=owner,
                                 protocol=protocol,
                                 status=status,
                                 version=version,
                                 type="Service",
                                 metadata=metadata)

    def create_vuln(workspace_name, id, name, description, owned=None, owner="",
                    confirmed=False, data="", refs=None, severity="info", resolution="",
                    desc="", metadata=None, status=None):
        """Creates a vuln.

        Args:
            workspace_name (str): the name of the workspace where the vuln will be saved.
            id (str): the id of the vuln. Must be unique.
            name (str): the vuln's name
            description (str): a description.
            owned (bool): is the vuln owned or not?
            owner (str): an owner for the vuln
            confirmed (bool): is the vulnerability confirmed?
            data (str): any aditional data about the vuln
            refs ([str]): references for the vulnerability
            severity (str): a string indicating the vuln's severity. can be 'info',
                'low', 'med', 'high', 'critical'
            resolution (str): the vuln's possible resolution
            desc (str): a vuln's description.
            status (str): the service's status
            metadata: a collection of metadata. If you don't know the metada. leave
                on None, it will be created automatically.

        Returns:
            A dictionary with the server's response.
        """
        return _save_to_server(workspace_name,
                               id,
                               name=name,
                               description=description,
                               owned=owned,
                               owner=owner,
                               confirmed=confirmed,
                               data=data,
                               refs=refs,
                               severity=severity,
                               resolution=resolution,
                               desc=desc,
                               type="Vulnerability",
                               status=status,
                               metadata=metadata)

    def update_vuln(workspace_name, id, name, description, owned=None, owner="",
                    confirmed=False, data="", refs=None, severity="info", resolution="",
                    desc="", metadata=None, status=None):
        """Updates a vuln.

        Args:
            workspace_name (str): the name of the workspace where the host will be saved.
            id (str): the id of the host. Must be unique.
            name (str): the host's name
            description (str): a description.
            owned (bool): is the vuln owned or not?
            owner (str): an owner for the vuln
            confirmed (bool): is the vulnerability confirmed?
            data (str): any aditional data about the vuln
            refs ([str]): references for the vulnerability
            severity (str): a string indicating the vuln's severity. can be 'info',
                'low', 'med', 'high', 'critical'
            resolution (str): the vuln's possible resolution
            desc (str): a vuln's description.
            status (str): the service's status
            metadata: a collection of metadata. If you don't know the metada. leave
                on None, it will be created automatically.

        Returns:
            A dictionary with the server's response.
        """
        return _update_in_server(workspace_name,
                                 id,
                                 name=name,
                                 description=description,
                                 owned=owned,
                                 owner=owner,
                                 confirmed=confirmed,
                                 data=data,
                                 refs=refs,
                                 severity=severity,
                                 resolution=resolution,
                                 desc=desc,
                                 type="Vulnerability",
                                 status=status,
                                 metadata=metadata)

    def create_vuln_web(workspace_name, id, name, description, owned=None, owner="",
                        confirmed=False, data="", refs=None, severity="info", resolution="",
                        desc="", metadata=None, method=None, params="", path=None, pname=None,
                        query=None, request=None, response=None, category="", website=None,
                        status=None):
        """Creates a vuln web.

        Args:
            workspace_name (str): the name of the workspace where the vuln web will be saved.
            id (str): the id of the vuln web. Must be unique.
            name (str): the vuln web's name
            description (str): a description.
            owner (str): an owner for the host
            confirmed (bool): is the vulnerability confirmed?
            data (str): any aditional data about the vuln
            refs ([str]): references for the vulnerability
            severity (str): a string indicating the vuln's severity. can be 'info',
                'low', 'med', 'high', 'critical'
            resolution (str): the vuln's possible resolution
            desc (str): a vuln's description.
            metadata: a collection of metadata. If you don't know the metada. leave
                on None, it will be created automatically.
            method (str): the web vuln method
            params (str): the parameters for the web vuln
            path (str): the web vuln's path
            query (str): the web vuln's query
            request (str): the web vuln's request
            response (str): the web vuln's response
            category (str): a category for the web vuln's
            website (str): the website where the vuln was found
            status (str): the web vulns's status

        Returns:
            A dictionary with the server's response.
        """
        return _save_to_server(workspace_name,
                               id,
                               name=name,
                               description=description,
                               owned=owned,
                               owner=owner,
                               confirmed=confirmed,
                               data=data,
                               refs=refs,
                               severity=severity,
                               resolution=resolution,
                               desc=desc,
                               metadata=metadata,
                               method=method,
                               params=params,
                               path=path,
                               pname=pname,
                               query=query,
                               request=request,
                               response=response,
                               website=website,
                               category=category,
                               status=status,
                               type='VulnerabilityWeb')

    def update_vuln_web(workspace_name, id, name, description, owned=None, owner="",
                        confirmed=False, data="", refs=None, severity="info", resolution="",
                        desc="", metadata=None, method=None, params="", path=None, pname=None,
                        query=None, request=None, response=None, category="", website=None,
                        status=None):
        """Creates a vuln web.

        Args:
            workspace_name (str): the name of the workspace where the vuln web will be saved.
            id (str): the id of the vuln web. Must be unique.
            name (str): the vuln web's name
            description (str): a description.
            owner (str): an owner for the host
            confirmed (bool): is the vulnerability confirmed?
            data (str): any aditional data about the vuln
            refs ([str]): references for the vulnerability
            severity (str): a string indicating the vuln's severity. can be 'info',
                'low', 'med', 'high', 'critical'
            resolution (str): the vuln's possible resolution
            desc (str): a vuln's description.
            metadata: a collection of metadata. If you don't know the metada. leave
                on None, it will be created automatically.
            method (str): the web vuln method
            params (str): the parameters for the web vuln
            path (str): the web vuln's path
            query (str): the web vuln's query
            request (str): the web vuln's request
            response (str): the web vuln's response
            category (str): a category for the web vuln's
            website (str): the website where the vuln was found
            status (str): the web vulns's status

        Returns:
            A dictionary with the server's response.
        """
        return _update_in_server(workspace_name,
                                 id,
                                 name=name,
                                 description=description,
                                 owned=owned,
                                 owner=owner,
                                 confirmed=confirmed,
                                 data=data,
                                 refs=refs,
                                 severity=severity,
                                 resolution=resolution,
                                 desc=desc,
                                 metadata=metadata,
                                 method=method,
                                 params=params,
                                 path=path,
                                 pname=pname,
                                 query=query,
                                 request=request,
                                 response=response,
                                 website=website,
                                 category=category,
                                 status=status,
                                 type='VulnerabilityWeb')

    def create_note(workspace_name, id, name, text, owned=None, owner="",
                    description="", metadata=None):
        """Creates a note.

        Args:
            workspace_name (str): the name of the workspace where the vuln web will be saved.
            id (str): the id of the vuln web. Must be unique.
            name (str): the vuln web's name
            text (str): the note's text
            owned (bool): is the note owned?
            owner (str): the note's owner
            description (str): a description
            metadata: a collection of metadata. If you don't know the metada. leave
                on None, it will be created automatically.

        Returns:
            A dictionary with the server's response.
        """
        return _save_to_server(workspace_name,
                               id,
                               name=name,
                               description=description,
                               owned=owned,
                               owner=owner,
                               text=text,
                               type="Note",
                               metadata=metadata)

    def update_note(workspace_name, id, name, text, owned=None, owner="",
                    description="", metadata=None):
        """Updates a note.

        Args:
            workspace_name (str): the name of the workspace where the vuln web will be saved.
            id (str): the id of the vuln web. Must be unique.
            name (str): the vuln web's name
            text (str): the note's text
            owned (bool): is the note owned?
            owner (str): the note's owner
            description (str): a description
            metadata: a collection of metadata. If you don't know the metada. leave
                on None, it will be created automatically.

        Returns:
            A dictionary with the server's response.
        """
        return _update_in_server(workspace_name,
                                 id,
                                 name=name,
                                 description=description,
                                 owned=owned,
                                 owner=owner,
                                 text=text,
                                 type="Note",
                                 metadata=metadata)

    def create_credential(workspace_name, id, name, username, password,
                          owned=None, owner="", description="", metadata=None):
        """Save a credential to the server. Return the json with the
        server's response.
        """
        return _save_to_server(workspace_name,
                               id,
                               name=name,
                               description=description,
                               owned=owned,
                               owner=owner,
                               metadata=metadata,
                               username=username,
                               password=password,
                               type="Credential")

    def update_credential(workspace_name, id, name, username, password,
                          owned=None, owner="", description="", metadata=None):
        """Update a credential in the server. Return the json with the
        server's response.
        """
        return _update_in_server(workspace_name,
                                 id,
                                 name=name,
                                 description=description,
                                 owned=owned,
                                 owner=owner,
                                 metadata=metadata,
                                 username=username,
                                 password=password,
                                 type="Credential")

    def create_command(workspace_name, id, command, duration=None, hostname=None,
                       ip=None, itime=None, params=None, user=None):
        """Create a command in the server. Return the json with the
        server's response.
        """
        return _save_to_server(workspace_name,
                               id,
                               command=command,
                               duration=duration,
                               hostname=hostname,
                               ip=ip,
                               itime=itime,
                               params=params,
                               user=user,
                               workspace=workspace_name,
                               type="CommandRunInformation")

    def update_command(workspace_name, id, command, duration=None, hostname=None,
                       ip=None, itime=None, params=None, user=None):
        """Update a command in the server. Return the json with the
        server's response.
        """
        return _update_in_server(workspace_name,
                                 id,
                                 command=command,
                                 duration=duration,
                                 hostname=hostname,
                                 ip=ip,
                                 itime=itime,
                                 params=params,
                                 user=user,
                                 workspace=workspace_name,
                                 type="CommandRunInformation")

    def create_workspace(workspace_name, description, start_date, finish_date,
                         customer=None):
        """Create a workspace in the server. Return the json with the
        server's response.
        """
        return _save_db_to_server(workspace_name,
                                  name=workspace_name,
                                  description=description,
                                  customer=customer,
                                  sdate=start_date,
                                  fdate=finish_date,
                                  type="Workspace")

    def delete_host(workspace_name, host_id):
        """Delete host of id host_id from the database."""
        return _delete_from_couch(workspace_name, host_id)

    def delete_interface(workspace_name, interface_id):
        """Delete interface of id interface_id from the database."""
        return _delete_from_couch(workspace_name, interface_id)

    def delete_service(workspace_name, service_id):
        """Delete service of id service_id from the database."""
        return _delete_from_couch(workspace_name, service_id)

    def delete_vuln(workspace_name, vuln_id):
        """Delete vuln of id vuln_id from the database."""
        return _delete_from_couch(workspace_name, vuln_id)

    def delete_note(workspace_name, note_id):
        """Delete note of id note_id from the database."""
        return _delete_from_couch(workspace_name, note_id)

    def delete_credential(workspace_name, credential_id):
        """Delete credential of id credential_id from the database."""
        return _delete_from_couch(workspace_name, credential_id)

    def delete_command(workspace_name, command_id):
        """Delete command of id command_id from the database."""
        return _delete_from_couch(workspace_name, command_id)

    def delete_workspace(workspace_name):
        """Delete the couch database of id workspace_name"""
        db_url = _create_server_db_url(workspace_name)
        return _delete(db_url, database=True)

    def is_server_up():
        """Return True if we can stablish a connection with the server,
        False otherwise.
        """
        try:
            _get("{0}/info".format(_create_server_api_url()))
            is_server_up = True
        except:
            is_server_up = False
        return is_server_up

    def test_server_url(url_to_test):
        """Return True if the url_to_test is indeed a valid Faraday Server URL.
        False otherwise.
        """
        try:
            _get("{0}/_api/info".format(url_to_test))
            test_okey = True
        except:
            test_okey = False
        return test_okey