Esempio n. 1
0
def disable_api(serial=None):
    """
    Disable a single token or all the tokens of a user.
    Disabled tokens can not be used to authenticate but can be enabled again.

    You can call the function like this:
        POST /token/disable?serial=<serial>
        POST /token/disable?user=<user>&realm=<realm>
        POST /token/disable/<serial>

    :param serial: the serial number of the single token to disable
    :type serial: basestring
    :param user: The login name of the user
    :type user: basestring
    :param realm: the realm name of the user
    :type realm: basestring
    :return: In case of success it returns the number of disabled
    tokens in "value".
    :rtype: json object
    """
    user = get_user_from_param(request.all_data, optional)
    if not serial:
        serial = getParam(request.all_data, "serial", optional)

    res = enable_token(serial, enable=False, user=user)
    g.audit_object.log({"success": res > 0})
    return send_result(res)
Esempio n. 2
0
def lost_api(serial=None):
    """
    Mark the specified token as lost and create a new temporary token.
    This new token gets the new serial number "lost<old-serial>" and
    a certain validity period and the PIN of the lost token.

    This method can be called by either the admin or the user on his own tokens.

    You can call the function like this:
        POST /token/lost/serial

    :param serial: the serial number of the lost token.
    :type serial: basestring
    :return: returns value=dictionary in case of success
    :rtype: bool
    """
    # check if a user is given, that the user matches the token owner.
    userobj = get_user_from_param(request.all_data)
    if userobj:
        toks = get_tokens(serial=serial, user=userobj)
        if len(toks) == 0:
            raise TokenAdminError("The user %s does not own the token %s" % (
                userobj, serial))

    options = {"g": g,
               "clientip": request.remote_addr}
    res = lost_token(serial, options=options)

    g.audit_object.log({"success": True})
    return send_result(res)
Esempio n. 3
0
def create(identifier=None):
    """
    This call creates or updates a RADIUS server definition.

    :param identifier: The unique name of the RADIUS server definition
    :param server: The FQDN or IP of the RADIUS server
    :param port: The port of the RADIUS server
    :param secret: The RADIUS secret of the RADIUS server
    :param description: A description for the definition
    """
    param = request.all_data
    identifier = identifier.replace(" ", "_")
    server = getParam(param, "server", required)
    port = int(getParam(param, "port", default=1812))
    secret = getParam(param, "secret", required)
    description = getParam(param, "description", default="")
    dictionary = getParam(param, "dictionary",
                          default="/etc/privacyidea/dictionary")

    r = add_radius(identifier, server, secret, port=port,
                   description=description, dictionary=dictionary)

    g.audit_object.log({'success': r > 0,
                        'info':  r})
    return send_result(r > 0)
Esempio n. 4
0
def reset_api(serial=None):
    """
    Reset the failcounter of a single token or of all tokens of a user.

    You can call the function like this:
        POST /token/reset?serial=<serial>
        POST /token/reset?user=<user>&realm=<realm>
        POST /token/reset/<serial>

    :param serial: the serial number of the single token to reset
    :type serial: basestring
    :param user: The login name of the user
    :type user: basestring
    :param realm: the realm name of the user
    :type realm: basestring
    :return: In case of success it returns "value"=True
    :rtype: json object
    """
    user = get_user_from_param(request.all_data, optional)
    if not serial:
        serial = getParam(request.all_data, "serial", optional)

    res = reset_token(serial, user=user)
    g.audit_object.log({"success": True})
    return send_result(res)
Esempio n. 5
0
def tokenrealm_api(serial=None):
    """
    Set the realms of a token.
    The token is identified by the unique serial number

    You can call the function like this:
        POST /token/realm?serial=<serial>&realms=<something>
        POST /token/realm/<serial>?realms=<hash>


    :param serial: the serial number of the single token to reset
    :type serial: basestring
    :param realms: The realms the token should be assigned to. Comma seperated
    :type realms: basestring
    :return: returns value=True in case of success
    :rtype: bool
    """
    realms = getParam(request.all_data, "realms", required)
    if type(realms) == list:
        realm_list = realms
    else:
        realm_list = [r.strip() for r in realms.split(",")]

    res = set_realms(serial, realms=realm_list)
    g.audit_object.log({"success": True})
    return send_result(res == 1)
Esempio n. 6
0
def create(identifier=None):
    """
    This call creates or updates an SMTP server definition.

    :param identifier: The unique name of the SMTP server definition
    :param server: The FQDN or IP of the mail server
    :param port: The port of the mail server
    :param username: The mail username for authentication at the SMTP server
    :param password: The password for authentication at the SMTP server
    :param tls: If the server should do TLS
    :param description: A description for the definition
    """
    param = request.all_data
    server = getParam(param, "server", required)
    port = int(getParam(param, "port", default=25))
    username = getParam(param, "username", default="")
    password = getParam(param, "password", default="")
    sender = getParam(param, "sender", default="")
    tls = bool(getParam(param, "tls"))
    description = getParam(param, "description", default="")

    r = add_smtpserver(
        identifier,
        server,
        port=port,
        username=username,
        password=password,
        tls=tls,
        description=description,
        sender=sender,
    )

    g.audit_object.log({"success": r > 0, "info": r})
    return send_result(r > 0)
Esempio n. 7
0
def get_applications():
    """
    returns a json list of the available applications
    """
    res = get_application_types()
    g.audit_object.log({"success": True})
    return send_result(res)
Esempio n. 8
0
def get_serial_by_otp_api(otp=None):
    """
    Get the serial number for a given OTP value.
    If the administrator has a token, he does not know to whom it belongs,
    he can type in the OTP value and gets the serial number of the token, that
    generates this very OTP value.

    :query otp: The given OTP value
    :query type: Limit the search to this token type
    :query unassigned: If set=1, only search in unassigned tokens
    :query assigned: If set=1, only search in assigned tokens
    :query serial: This can be a substring of serial numbers to search in.
    :query window: The number of OTP look ahead (default=10)
    :return: The serial number of the token found
    """
    ttype = getParam(request.all_data, "type")
    unassigned_param = getParam(request.all_data, "unassigned")
    assigned_param = getParam(request.all_data, "assigned")
    serial_substr = getParam(request.all_data, "serial")
    window = int(getParam(request.all_data, "window", default=10))

    serial_substr = serial_substr or ""

    assigned = None
    if unassigned_param:
        assigned = False
    if assigned_param:
        assigned = True

    tokenobj_list = get_tokens(tokentype=ttype, serial="*%s*" % serial_substr, assigned=assigned)
    serial = get_serial_by_otp(tokenobj_list, otp=otp, window=window)

    g.audit_object.log({"success": True, "info": "get %s by OTP" % serial})

    return send_result({"serial": serial})
Esempio n. 9
0
def test():
    """
    Test the email configuration
    :return:
    """
    param = request.all_data
    identifier = getParam(param, "identifier", required)
    server = getParam(param, "server", required)
    port = int(getParam(param, "port", default=25))
    username = getParam(param, "username", default="")
    password = getParam(param, "password", default="")
    sender = getParam(param, "sender", default="")
    tls = bool(getParam(param, "tls"))
    recipient = getParam(param, "recipient", required)

    s = SMTPServerDB(
        identifier=identifier, server=server, port=port, username=username, password=password, sender=sender, tls=tls
    )
    r = SMTPServer.test_email(
        s,
        recipient,
        "Test Email from privacyIDEA",
        "This is a test email from privacyIDEA. " "The configuration %s is working." % identifier,
    )

    g.audit_object.log({"success": r > 0, "info": r})
    return send_result(r > 0)
Esempio n. 10
0
def create_user_api():
    """
    Create a new user in the given resolver.

    **Example request**:

    .. sourcecode:: http

       POST /user
       user=new_user
       resolver=<resolvername>
       surname=...
       givenname=...
       email=...
       mobile=...
       phone=...
       password=...
       description=...

       Host: example.com
       Accept: application/json

    """
    # We can not use "get_user_from_param", since this checks the existence
    # of the user.
    attributes = _get_attributes_from_param(request.all_data)
    username = getParam(request.all_data, "user", optional=False)
    resolvername = getParam(request.all_data, "resolver", optional=False)
    r = create_user(resolvername, attributes)
    g.audit_object.log({"success": True,
                        "info": "%s: %s/%s" % (r, username, resolvername)})
    return send_result(r)
Esempio n. 11
0
def detach_token_api(serial, machineid, resolver, application):
    """
    Detach a token from a machine with a certain application.

    :param machineid: identify the machine by the machine ID and the resolver
        name
    :param resolver: identify the machine by the machine ID and the resolver name
    :param serial: identify the token by the serial number
    :param application: the name of the application like "luks" or "ssh".

    :return: json result with "result": true and the machine list in "value".

    **Example request**:

    .. sourcecode:: http

       DELETE /token HTTP/1.1
       Host: example.com
       Accept: application/json

       { "hostname": "puckel.example.com",
         "resolver": "machineresolver1",
         "application": "luks" }

    """
    r = detach_token(serial, application,
                     machine_id=machineid, resolver_name=resolver)

    g.audit_object.log({'success': True,
                        'info': "serial: %s, application: %s" % (serial,
                                                                 application)})

    return send_result(r)
Esempio n. 12
0
def set_resolver(resolver=None):
    """
    This creates a new machine resolver or updates an existing one.
    A resolver is uniquely identified by its name.

    If you update a resolver, you do not need to provide all parameters.
    Parameters you do not provide are left untouched.
    When updating a resolver you must not change the type!
    You do not need to specify the type, but if you specify a wrong type,
    it will produce an error.

    :param resolver: the name of the resolver.
    :type resolver: basestring
    :param type: the type of the resolver. Valid types are... "hosts"
    :type type: string
    :return: a json result with the value being the database id (>0)

    Additional parameters depend on the resolver type.

    hosts:
     * filename
    """
    param = request.all_data
    if resolver:
        # The resolver parameter was passed as a part of the URL
        param.update({"name": resolver})
    res = save_resolver(param)
    return send_result(res)
Esempio n. 13
0
def get_policy_defs(scope=None):
    """
    This is a helper function that returns the POSSIBLE policy
    definitions, that can
    be used to define your policies.

    :query scope: if given, the function will only return policy
                  definitions for the given scope.

    :return: The policy definitions of the allowed scope with the actions and
        action types. The top level key is the scope.
    :rtype: dict
    """
    pol = {}
    static_pol = get_static_policy_definitions()
    dynamic_pol = get_dynamic_policy_definitions()

    # combine static and dynamic policies
    keys = static_pol.keys() + dynamic_pol.keys()
    pol = {k: dict(static_pol.get(k, {}).items()
                   + dynamic_pol.get(k, {}).items()) for k in keys}

    if scope:
        pol = pol.get(scope)

    g.audit_object.log({"success": True,
                        'info': scope})
    return send_result(pol)
Esempio n. 14
0
def list_machinetokens_api():
    """
    Return a list of MachineTokens either for a given machine or for a given
    token.

    :param serial: Return the MachineTokens for a the given Token
    :param hostname: Identify the machine by the hostname
    :param machineid: Identify the machine by the machine ID and the resolver
        name
    :param resolver: Identify the machine by the machine ID and the resolver
        name
    :return:
    """
    hostname = getParam(request.all_data, "hostname")
    machineid = getParam(request.all_data, "machineid")
    resolver = getParam(request.all_data, "resolver")
    serial = getParam(request.all_data, "serial")
    application = getParam(request.all_data, "application")

    res = []

    if not hostname and not machineid and not resolver:
        # We return the list of the machines for the given serial
        res = list_token_machines(serial)
    else:
        res = list_machine_tokens(hostname=hostname, machine_id=machineid,
                                  resolver_name=resolver)

    g.audit_object.log({'success': True,
                        'info': "serial: %s, hostname: %s" % (serial,
                                                              hostname)})
    return send_result(res)
Esempio n. 15
0
def resync_api(serial=None):
    """
    Resync the OTP token by providing two consecutive OTP values.

    You can call the function like this:
        POST /token/resync?serial=<serial>&otp1=<otp1>&otp2=<otp2>
        POST /token/resync/<serial>?otp1=<otp1>&otp2=<otp2>

    :param serial: the serial number of the single token to reset
    :type serial: basestring
    :param otp1: First OTP value
    :type otp1: basestring
    :param otp2: Second OTP value
    :type otp2: basestring
    :return: In case of success it returns "value"=True
    :rtype: json object
    """
    if not serial:
        serial = getParam(request.all_data, "serial", required)
    otp1 = getParam(request.all_data, "otp1", required)
    otp2 = getParam(request.all_data, "otp2", required)

    res = resync_token(serial, otp1, otp2)
    g.audit_object.log({"success": True})
    return send_result(res)
Esempio n. 16
0
def revoke_api(serial=None):
    """
    Revoke a single token or all the tokens of a user.
    A revoked token will usually be locked. A locked token can not be used
    anymore.
    For certain token types additional actions might occur when revoking a
    token.

    :param serial: the serial number of the single token to revoke
    :type serial: basestring
    :param user: The login name of the user
    :type user: basestring
    :param realm: the realm name of the user
    :type realm: basestring
    :return: In case of success it returns the number of revoked
        tokens in "value".
    :rtype: json object
    """
    user = get_user_from_param(request.all_data, optional)
    if not serial:
        serial = getParam(request.all_data, "serial", optional)

    res = revoke_token(serial, user=user)
    g.audit_object.log({"success": res > 0})
    return send_result(res)
Esempio n. 17
0
def get_clients():
    """
    return a list of authenticated clients grouped (dictionary) by the
    clienttype.

    **Example request**:

    .. sourcecode:: http

       GET /client
       Host: example.com
       Accept: application/json

    **Example response**:

    .. sourcecode:: http

       HTTP/1.1 200 OK
       Content-Type: application/json

        {
          "id": 1,
          "jsonrpc": "2.0",
          "result": {
            "status": true,
            "value": {"PAM": [],
                      "SAML": [],
          },
          "version": "privacyIDEA unknown"
        }
    """
    clients = get_clientapplication()
    g.audit_object.log({'success': True})
    
    return send_result(clients)
Esempio n. 18
0
def set_config():
    """
    set a configuration key or a set of configuration entries

    parameter are generic keyname=value pairs.

    *remark: In case of key-value pairs the type information could be
             provided by an additional parameter with same keyname with the
             postfix ".type". Value could then be 'password' to trigger the
             storing of the value in an encrypted form

    :param key: configuration entry name
    :param value: configuration value
    :param type: type of the value: int or string/text or password
                 password will trigger to store the encrypted value
    :param description: additional information for this config entry

    * or
    :param key-value pairs: pair of &keyname=value pairs
    :return: a json result with a boolean "result": true
    """
    param = request.all_data
    result = {}
    for key in param:
        if key.split(".")[-1] not in ["type", "desc"]:
            # Only store base values, not type or desc
            value = getParam(param, key, optional)
            typ = getParam(param, key + ".type", optional)
            desc = getParam(param, key + ".desc", optional)
            res = set_privacyidea_config(key, value, typ, desc)
            result[key] = res
            g.audit_object.log({"success": True})
            g.audit_object.add_to_log({"info": "%s=%s, " % (key, value)})
    return send_result(result)
Esempio n. 19
0
def get_rights():
    """
    This returns the rights of the logged in user.
    :return:
    """
    enroll_types = g.policy_object.ui_get_enroll_tokentypes(request.remote_addr,
                                                            g.logged_in_user)
    return send_result(enroll_types)
Esempio n. 20
0
def get_caconnector_api(name=None):
    """
    returns a json list of the available applications
    """
    g.audit_object.log({"detail": "%s" % name})
    res = get_caconnector_list(filter_caconnector_name=name)
    g.audit_object.log({"success": True})
    return send_result(res)
Esempio n. 21
0
def delete_caconnector_api(name=None):
    """
    returns a json list of the available applications
    """
    g.audit_object.log({"detail": "{0!s}".format(name)})
    res = delete_caconnector(name)
    g.audit_object.log({"success": True})
    return send_result(res)
Esempio n. 22
0
def get_users():
    """
    list the users in a realm

    A normal user can call this endpoint and will get information about his
    own account.

    :param realm: a realm that contains several resolvers. Only show users
                  from this realm
    :param resolver: a distinct resolvername
    :param <searchexpr>: a search expression, that depends on the ResolverClass
    
    :return: json result with "result": true and the userlist in "value".

    **Example request**:

    .. sourcecode:: http

       GET /user?realm=realm1 HTTP/1.1
       Host: example.com
       Accept: application/json

    **Example response**:

    .. sourcecode:: http

       HTTP/1.1 200 OK
       Content-Type: application/json

        {
          "id": 1,
          "jsonrpc": "2.0",
          "result": {
            "status": true,
            "value": [
              {
                "description": "Cornelius K\u00f6lbel,,+49 151 2960 1417,+49 561 3166797,[email protected]",
                "email": "*****@*****.**",
                "givenname": "Cornelius",
                "mobile": "+49 151 2960 1417",
                "phone": "+49 561 3166797",
                "surname": "K\u00f6lbel",
                "userid": "1009",
                "username": "******",
                "resolver": "name-of-resolver"
              }
            ]
          },
          "version": "privacyIDEA unknown"
        }
    """
    realm = getParam(request.all_data, "realm")
    users = get_user_list(request.all_data)

    g.audit_object.log({'success': True,
                        'info': "realm: {0!s}".format(realm)})
    
    return send_result(users)
Esempio n. 23
0
def get_rights():
    """
    This returns the rights of the logged in user.

    :reqheader Authorization: The authorization token acquired by /auth request
    """
    enroll_types = g.policy_object.ui_get_enroll_tokentypes(g.client_ip,
                                                            g.logged_in_user)
    return send_result(enroll_types)
Esempio n. 24
0
def test_resolver():
    """
    :return: a json result with True, if the given values can create a
        working resolver and a description.
    """
    param = request.all_data
    rtype = getParam(param, "type", required)
    success, desc = pretestresolver(rtype, param)
    return send_result(success, details={"description": desc})
Esempio n. 25
0
def get_realms_api():
    """
    This call returns the list of all defined realms.
    It take no arguments.

    :return: a json result with a list of realms


    **Example request**:

    .. sourcecode:: http

       GET / HTTP/1.1
       Host: example.com
       Accept: application/json

    **Example response**:

    .. sourcecode:: http

        HTTP/1.1 200 OK
        Content-Type: application/json

        {
          "id": 1,
          "jsonrpc": "2.0",
          "result": {
            "status": true,
            "value": {
              "realm1_with_resolver": {
                "default": true,
                "resolver": [
                  {
                    "name": "reso1_with_realm",
                    "type": "passwdresolver"
                  }
                ]
              }
            }
          },
          "version": "privacyIDEA unknown"
        }
    """
    realms = get_realms()
    g.audit_object.log({"success": True})

    # If the admin is not allowed to see all realms,
    # (policy scope=system, action=read)
    # the realms, where he has no administrative rights need,
    # to be stripped.
    '''
        polPost = self.Policy.checkPolicyPost('system',
                                              'getRealms',
                                              {'realms': realms})
        res = polPost['realms']
    '''
    return send_result(realms)
Esempio n. 26
0
def disable_policy_api(name):
    """
    Disable a given policy by its name.
    :param name: The name of the policy
    :return: ID in the database
    """
    p = enable_policy(name, False)
    g.audit_object.log({"success": True})
    return send_result(p)
Esempio n. 27
0
def get_auth_items_api(application=None):
    """
    This fetches the authentication items for a given application and the
    given client machine.

    :param challenge: A challenge for which the authentication item is
        calculated. In case of the Yubikey this can be a challenge that produces
        a response. The authentication item is the combination of the challenge
        and the response.
    :type challenge: basestring
    :param hostname: The hostname of the machine
    :type hostname: basestring

    :return: dictionary with lists of authentication items

    **Example response**:

    .. sourcecode:: http

       HTTP/1.1 200 OK
       Content-Type: application/json

        {
          "id": 1,
          "jsonrpc": "2.0",
          "result": {
            "status": true,
            "value": { "ssh": [ { "username": "******",
                                  "sshkey": "...."
                                }
                              ],
                       "luks": [ { "slot": ".....",
                                   "challenge": "...",
                                   "response": "...",
                                   "partition": "..."
                               ]
                     }
          },
          "version": "privacyIDEA unknown"
        }
    """
    challenge = getParam(request.all_data, "challenge")
    hostname = getParam(request.all_data, "hostname", optional=False)
    # Get optional additional filter parameters
    filter_param = request.all_data
    for key in ["challenge", "hostname"]:
        if key in filter_param:
            del(filter_param[key])

    ret = get_auth_items(hostname, ip=request.remote_addr,
                         application=application, challenge=challenge,
                         filter_param=filter_param)
    g.audit_object.log({'success': True,
                        'info': "host: %s, application: %s" % (hostname,
                                                               application)})
    return send_result(ret)
Esempio n. 28
0
def delete_server(identifier=None):
    """
    This call deletes the specified SMTP server configuration

    :param identifier: The unique name of the SMTP server definition
    """
    r = delete_smtpserver(identifier)

    g.audit_object.log({"success": r > 0, "info": r})
    return send_result(r > 0)
Esempio n. 29
0
def save_caconnector_api(name=None):
    """
    returns a json list of the available applications
    """
    param = request.all_data
    param["caconnector"] = name
    g.audit_object.log({"detail": "{0!s}".format(name)})
    res = save_caconnector(param)
    g.audit_object.log({"success": True})
    return send_result(res)
Esempio n. 30
0
def get_resolvers():
    """
    returns a json list of all machine resolver.

    :param type: Only return resolvers of type (like "hosts"...)
    """
    typ = getParam(request.all_data, "type", optional)
    res = get_resolver_list(filter_resolver_type=typ)
    g.audit_object.log({"success": True})
    return send_result(res)
Esempio n. 31
0
def check_policy_api():
    """
    This function checks, if the given parameters would match a defined policy
    or not.

    :query user: the name of the user
    :query realm: the realm of the user or the realm the administrator
        want to do administrative tasks on.
    :query resolver: the resolver of a user
    :query scope: the scope of the policy
    :query action: the action that is done - if applicable
    :query IP_Address client: the client, from which this request would be
        issued

    :return: a json result with the keys allowed and policy in the value key
    :rtype: json

    :status 200: Policy created or modified.
    :status 401: Authentication failed

    **Example request**:

    .. sourcecode:: http

       GET /policy/check?user=admin&realm=r1&client=172.16.1.1 HTTP/1.1
       Host: example.com
       Accept: application/json

    **Example response**:

    .. sourcecode:: http

       HTTP/1.0 200 OK
       Content-Type: application/json

        {
          "id": 1,
          "jsonrpc": "2.0",
          "result": {
            "status": true,
            "value": {
              "pol_update_del": {
                "action": "enroll",
                "active": true,
                "client": "172.16.0.0/16",
                "name": "pol_update_del",
                "realm": "r1",
                "resolver": "test",
                "scope": "selfservice",
                "time": "",
                "user": "******"
              }
            }
          },
          "version": "privacyIDEA unknown"
        }

    """
    res = {}
    param = getLowerParams(request.all_data)

    user = getParam(param, "user", required)
    realm = getParam(param, "realm", required)
    scope = getParam(param, "scope", required)
    action = getParam(param, "action", required)
    client = getParam(param, "client", optional)
    resolver = getParam(param, "resolver", optional)

    P = g.policy_object
    policies = P.get_policies(user=user,
                              realm=realm,
                              resolver=resolver,
                              scope=scope,
                              action=action,
                              client=client,
                              active=True)
    if policies:
        res["allowed"] = True
        res["policy"] = policies
        policy_names = []
        for pol in policies:
            policy_names.append(pol.get("name"))
        g.audit_object.log(
            {'info': "allowed by policy {0!s}".format(policy_names)})
    else:
        res["allowed"] = False
        res["info"] = "No policies found"

    g.audit_object.log({
        "success":
        True,
        'action_detail':
        "action = %s, realm = %s, scope = "
        "%s" % (action, realm, scope)
    })

    return send_result(res)
Esempio n. 32
0
def get_auth_token():
    """
    This call verifies the credentials of the user and issues an
    authentication token, that is used for the later API calls. The
    authentication token has a validity, that is usually 1 hour.

    :jsonparam username: The username of the user who wants to authenticate to
        the API.
    :jsonparam password: The password/credentials of the user who wants to
        authenticate to the API.

    :return: A json response with an authentication token, that needs to be
        used in any further request.

    :status 200: in case of success
    :status 401: if authentication fails

    **Example Authentication Request**:

    .. sourcecode:: http

       POST /auth HTTP/1.1
       Host: example.com
       Accept: application/json

       username=admin
       password=topsecret

    **Example Authentication Response**:

    .. sourcecode:: http

       HTTP/1.0 200 OK
       Content-Length: 354
       Content-Type: application/json

       {
            "id": 1,
            "jsonrpc": "2.0",
            "result": {
                "status": true,
                "value": {
                    "token": "eyJhbGciOiJIUz....jdpn9kIjuGRnGejmbFbM"
                }
            },
            "version": "privacyIDEA unknown"
       }

    **Response for failed authentication**:

    .. sourcecode:: http

       HTTP/1.1 401 UNAUTHORIZED
       Content-Type: application/json
       Content-Length: 203

       {
          "id": 1,
          "jsonrpc": "2.0",
          "result": {
            "error": {
              "code": -401,
              "message": "missing Authorization header"
            },
            "status": false
          },
          "version": "privacyIDEA unknown",
          "config": {
            "logout_time": 30
          }
       }

    """
    validity = timedelta(hours=1)
    username = request.all_data.get("username")
    password = request.all_data.get("password")
    realm = request.all_data.get("realm")
    details = {}
    if realm:
        username = username + "@" + realm

    g.audit_object.log({"user": username})

    secret = current_app.secret_key
    superuser_realms = current_app.config.get("SUPERUSER_REALM", [])
    # This is the default role for the logged in user.
    # The role privileges may be risen to "admin"
    role = ROLE.USER
    # The way the user authenticated. This could be
    # "password" = The admin user DB or the user store
    # "pi" = The admin or the user is authenticated against privacyIDEA
    # "remote_user" = authenticated by webserver
    authtype = "password"
    if username is None:
        raise AuthError("Authentication failure",
                        "missing Username",
                        status=401)
    # Verify the password
    admin_auth = False
    user_auth = False

    loginname, realm = split_user(username)
    realm = realm or get_default_realm()

    # Check if the remote user is allowed
    if (request.remote_user == username) and is_remote_user_allowed(request):
        # Authenticated by the Web Server
        # Check if the username exists
        # 1. in local admins
        # 2. in a realm
        # 2a. is an admin realm
        authtype = "remote_user "
        if db_admin_exist(username):
            role = ROLE.ADMIN
            admin_auth = True
            g.audit_object.log({
                "success": True,
                "user": "",
                "administrator": username,
                "info": "internal admin"
            })
        else:
            # check, if the user exists
            user_obj = User(loginname, realm)
            if user_obj.exist():
                user_auth = True
                if user_obj.realm in superuser_realms:
                    role = ROLE.ADMIN
                    admin_auth = True

    elif verify_db_admin(username, password):
        role = ROLE.ADMIN
        admin_auth = True
        # This admin is not in the default realm!
        realm = ""
        g.audit_object.log({
            "success": True,
            "user": "",
            "administrator": username,
            "info": "internal admin"
        })

    else:
        # The user could not be identified against the admin database,
        # so we do the rest of the check
        options = {"g": g, "clientip": g.client_ip}
        for key, value in request.all_data.items():
            if value and key not in ["g", "clientip"]:
                options[key] = value
        user_obj = User(loginname, realm)
        user_auth, role, details = check_webui_user(
            user_obj,
            password,
            options=options,
            superuser_realms=superuser_realms)
        if role == ROLE.ADMIN:
            g.audit_object.log({"user": "", "administrator": username})

    if not admin_auth and not user_auth:
        raise AuthError("Authentication failure",
                        "Wrong credentials",
                        status=401,
                        details=details or {})
    else:
        g.audit_object.log({"success": True})

    # If the HSM is not ready, we need to create the nonce in another way!
    hsm = init_hsm()
    if hsm.is_ready:
        nonce = geturandom(hex=True)
        # Add the role to the JWT, so that we can verify it internally
        # Add the authtype to the JWT, so that we could use it for access
        # definitions
        rights = g.policy_object.ui_get_rights(role, realm, loginname,
                                               g.client_ip)
    else:
        import os
        import binascii
        nonce = binascii.hexlify(os.urandom(20))
        rights = []

    # What is the log level?
    log_level = current_app.config.get("PI_LOGLEVEL", 30)

    token = jwt.encode(
        {
            "username": loginname,
            "realm": realm,
            "nonce": nonce,
            "role": role,
            "authtype": authtype,
            "exp": datetime.utcnow() + validity,
            "rights": rights
        }, secret)

    # Add the role to the response, so that the WebUI can make decisions
    # based on this (only show selfservice, not the admin part)
    return send_result(
        {
            "token": token,
            "role": role,
            "username": loginname,
            "realm": realm,
            "log_level": log_level,
            "rights": rights
        },
        details=details)
Esempio n. 33
0
def init():
    """
    create a new token.

    :jsonparam otpkey: required: the secret key of the token
    :jsonparam genkey: set to =1, if key should be generated. We either
                   need otpkey or genkey
    :jsonparam keysize: the size (byte) of the key. Either 20 or 32. Default is 20
    :jsonparam serial: required: the serial number/identifier of the token
    :jsonparam description: A description for the token
    :jsonparam pin: the pin of the token. "OTP PIN"
    :jsonparam user: the login user name. This user gets the token assigned
    :jsonparam realm: the realm of the user.
    :jsonparam type: the type of the token
    :jsonparam tokenrealm: additional realms, the token should be put into
    :jsonparam otplen: length of the OTP value
    :jsonparam hashlib: used hashlib sha1, sha256 or sha512
    :jsonparam validity_period_start: The beginning of the validity period
    :jsonparam validity_period_end: The end of the validity period

    :return: a json result with a boolean "result": true

    **Example response**:

       .. sourcecode:: http

           HTTP/1.1 200 OK
           Content-Type: application/json

           {
              "detail": {
                "googleurl": {
                  "description": "URL for google Authenticator",
                  "img": "<img    width=250   src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAcIAAAHCAQAAAABUY/ToAAADsUlEQVR4nO2czY3bMBCF34QCfKSALcClyB2kpCAlpQOxlBQQgDwaoPBy4I+p9W4OSRaWF28OgizxgylgMJw/0oi/k/DlL0FApEiRIkWKFCnyeKRVmdrjNAFh3srTMuSS2qjLg2cr8pDkQpKMgF3SBITz1QA4YolVfQA4kiT35CNmK/JQZLM8aQaWH+3pEkEgTZlhBojksgGAAS7/83+K/ORkOF/NLtismiCfYXbOd+AxZivygCTXdCLCDJRLfTbhTo4wW5FHIJtyeAJIAJb4AobLBIP/ZQRAwMcyakxIPtd3ivw4EqObXJzody9t1EKS63N9p8iPI4sO3QTwGSSbA1Q0x+cWunWRDolsUjSnxvau6VB0xMIMrp4EPAnAkWsjpEMiu+ysD1mUZomuKk1/i6WtedIhkXupS1MEsMRmaVafh7dVfXwGV0D+kMj3yXDOsIsngXQiV59R0tZIE7jC0b4VA3WE2Yo8CtkTPy7b8sPA8HWbWML6dCKAqxG4GgADw+weOVuRRyTHuGztbk+PwdqQPIzTWibyDbJWVdOJQDLj9xkod4yOCK2gbzZvVpyip/xOkR9B4maCbnF8c53vHGuuLVaTHRLZpBgYgweAVP0hLPElA+mFtVrvf3W/aTM+brYij0j23o8JthAweNc1J5cCmSFNYDCAS5wfOVuRRyT7QpVL9F6XLN/zjhG4ZSAHj1trmcgmLcfoWoq6/B4LZLeqBxmVpxb5WobYfl8vaxfU7DSA4mdLh0S+TW5W2xXTiaWZ0WbALqiXmi5KU/n5tN8p8r+TzaqUH936MKNW6/2uIkvZIZF/IEleDfAZZnYi1zSB/DmVpa2YJZtVLxP5JmnfWCutty5qwNcFrWSsV2xGxs3+03+K/Cxk74WtTWflDr652L0XtoZuylOLvJNb9H7XPzQ0DOX9RTokcpAhAzRYpN4LO5TsI1rQLx0SOci4z7VcSuvQZgxWX1gfbfBX1ctEvhLupbZSe5bNQK0Jv/dTe9U6RL6WtoIBqDs33NA7Xdey3SYzrWUi99L8IfJW4cC4pYNjg+Ow/+O5vlPkx5OpnSsUzler2cbS29g8pmBmWH6elGMU+UqaFwS0NBBa9O45Rmhr26Mof0jkTt440MNlC9aOGQqzA8McaQs34xJfsv3rf4r8XOTduR+lezHN5fyh0sdY76qz/cDZijwwGcxqs0c9gNFx5w9t7e18hNmKPBRZ7NDtXKF6V1qp2e9qtZ7DkOf6TpEiRYoUKVKkyPfkNyq7YXtdjZCIAAAAAElFTkSuQmCC\"/>",
                  "value": "otpauth://hotp/mylabel?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ&counter=0"
                },
                "oathurl": {
                  "description": "URL for OATH token",
                  "img": "<img    width=250   src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAcIAAAHCAQAAAABUY/ToAAADfElEQVR4nO2cTYrjMBCFX40EvZRvkKPIN5gz9c3so/QBBqxlwObNQpIlp2cYaBI6zrxamDjyhywo6leyEV+T+ccXQUCkSJEiRYoUKfL5SCviy7+zmZWBAbARmwGpPjXeZU6RL0ZGkuQCAMkMCCTmqlJ8HwAb4UiSPJJfn1Pki5Fpty8AED/MEBeAU/JoA52pOuk6Rd6f9H/60xBWbwCMyG7Mg0j3mlPky5OOiB9v5AQACCQnONr4yDlFnpisdigQQAIM4WpE2oyAWy0umyfCku1QX5A81zpFPo5EHybDEXH566U+FUlyOtc6RT6OzHao2RfOgwMQVqBYJADz5WrFVN1jTpGvRRY7FLmCExwR8y3JKbAm84HkFFawieyQyCpFJRagaMniikqRK4C9KpSVa3GULxN5lGZp8n3kinrr2H5xCmsZlQ6JPEiLqbPzKh5sRefL4uJILq4MyJeJPEjzZb2jQnFopQmSH3FZw2SHRB6lC3bQeatDiI2wghOAaoykQyKb7L2OzQPpjZjNEUgDDNiMSAMAOFpchjvNKfK1yGqHlkNetofYxclVs5RzNfkykZ/J4rc+So+++S2zy1ofDVezMXmURtoZ1ynyEeRuh1xXSiwJPtCFRyUygupDIm+l5fa9Q+Na0rT8yCG3lw6JPEqtMZaCUNfmyPWhBajtMx46Iedap8jHkV2/DK0cDWBXqapczY0ptxd5kFZjLEqzlJi6C4WyHYJjHZAOieyk2aGsSNyjoF2l0Jsg9TpE/oVMHpgvK8wupRZkIwDMQy0S5QMfbVfsOdcp8v5kF1M3N9ZaGrX/sbf2g+yQyFtpPdW2/75pTtGX5tWCcnuRt9L1OtguLcFve9DazmrpkMheOn3Ju4aA4tX6gVopiurbi7yV3Lc3IJ+vh0VuHoBbAWyeSH41hF+fzzKea50iH012QdE8OPJ92MzG9HY4NJRDpqt9+9uKfEayffeDU/J7z3UzG8PVSlqfPMrlm99W5FOSsUY8Noarmdkb+T7UTSF7Wv8kbyvyqcguL+u23k/7cDvdmm9Vpxb5LzLbobErObbc/lFzijw3eZtvcR4WAtjKx2Lmn1djztBAWN5ZPX3X24p8RrI719HcWNnsEVoz1vWPyJeJ7KXYoTln7A4Wcz6/eQL7xxxyRr95IlwNskMiezF941ykSJEiRYoU+Z+TvwF49nApsKFZZAAAAABJRU5ErkJggg==\"/>",
                  "value": "oathtoken:///addToken?name=mylabel&lockdown=true&key=3132333435363738393031323334353637383930"
                },
                "otpkey": {
                  "description": "OTP seed",
                  "img": "<img    width=200   src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUoAAAFKAQAAAABTUiuoAAAB70lEQVR4nO2aTY6jQAyFPw9IWYI0B+ijwNHhKH0DWLZU6PXCVYSOZkF6xM/CXkQkfIsnWRU/22ViZ4x/9pIQaKCBBhpooEeilqPGrAWzdjGYy8/94QICfQftJEkTAIsBlYBKkqSf6DECAn0HnfMRkj4fnjfrATOrzxEQ6I6oX74bYGJuzxIQ6H9kqySqSjCfISDQX6CNpKE8mX18lT9GpXMEBLofHc3M7WA/19B9PgQsbgnPEBDonrCXyZMB/HMaFZOnu6DWz2aMZqaBZ79Vw9gu0W/dBsU7qm4CL16aKq9geonhcq2BlqR4jirRSYImoaF8eO8c2boeXR38YnRavIwJkNFUsg1xudZAy5ywreSFyqcabgxr8lE7XECgu8JPjpj/Ao2AJtXAYoIEYzsVi3i51kBz3Rq8O658RFhKVn4Rdesu6MYTemZoEm468kh+TejlWgNdjXoeMGVjOJXXnVJk6zboa1uFb7Wm1csTZ+tu6HN3TKcEYwvZIlLJ+sMFBPoO+twdjz7GXQy8Mf6Kqe7t0HV37FaDSp630R7Rb90WtR6ytxiaFPute6Gvu2OY6wRzC92EtguUy7UGWvqtzWgX8DtPZZ8cnvAuKNs7aH4v7ZnBPH6PWcZd0DInLPHjqSTvSAGBBhpooIEG+gb6DeDWV0l+Ofz2AAAAAElFTkSuQmCC\"/>",
                  "value": "seed://3132333435363738393031323334353637383930"
                },
                "serial": "OATH00096020"
              },
              "id": 1,
              "jsonrpc": "2.0",
              "result": {
                "status": true,
                "value": true
              },
              "version": "privacyIDEA unknown"
            }
    """
    response_details = {}
    tokenrealms = None
    param = request.all_data

    # check admin authorization
    # user_tnum = len(getTokens4UserOrSerial(user))
    # res = self.Policy.checkPolicyPre('admin', 'init', param, user=user,
    #                                 options={'token_num': user_tnum})

    # if no user is given, we put the token in all realms of the admin
    # if user.login == "":
    #    log.debug("setting tokenrealm %s" % res['realms'])
    #    tokenrealm = res['realms']

    user = get_user_from_param(param)
    tokenobject = init_token(param, user, tokenrealms=tokenrealms)

    if tokenobject:
        g.audit_object.log({"success": True})
        # The token was created successfully, so we add token specific
        # init details like the google URL to the response
        init_details = tokenobject.get_init_detail(param, user)
        response_details.update(init_details)

    g.audit_object.log({
        'user': user.login,
        'realm': user.realm,
        'serial': tokenobject.token.serial,
        'token_type': tokenobject.token.tokentype
    })

    # logTokenNum()

    # setting the random PIN
    # randomPINLength = self.Policy.getRandomOTPPINLength(user)
    # if randomPINLength > 0:
    #    newpin = self.Policy.getRandomPin(randomPINLength)
    #    log.debug("setting random pin for token with serial "
    #              "%s and user: %s" % (serial, user))
    #    setPin(newpin, None, serial)

    # finally we render the info as qr immage, if the qr parameter
    # is provided and if the token supports this
    # if 'qr' in param and tokenobject is not None:
    #    (rdata, hparam) = tokenobject.getQRImageData(response_detail)
    #    hparam.update(response_detail)
    #    hparam['qr'] = param.get('qr') or 'html'
    #    return sendQRImageResult(response, rdata, hparam)
    # else:
    #    return sendResult(response, ret, opt=response_detail)

    return send_result(True, details=response_details)
Esempio n. 34
0
def list_api():
    """
    Display the list of tokens. Using different parameters you can choose,
    which tokens you want to get and also in which format you want to get the
    information (*outform*).

    :query serial: Display the token data of this single token. You can do a
        not strict matching by specifying a serial like "*OATH*".
    :query type: Display only token of type. You ca do a non strict matching by
        specifying a tokentype like "*otp*", to file hotp and totp tokens.
    :query user: display tokens of this user
    :query tokenrealm: takes a realm, only the tokens in this realm will be
        displayed
    :query basestring description: Display token with this kind of description
    :query sortby: sort the output by column
    :query sortdir: asc/desc
    :query page: request a certain page
    :query assigned: Only return assigned (True) or not assigned (False) tokens
    :query pagesize: limit the number of returned tokens
    :query user_fields: additional user fields from the userid resolver of
        the owner (user)
    :query outform: if set to "csv", than the token list will be given in CSV

    :return: a json result with the data being a list of token dictionaries::

        { "data": [ { <token1> }, { <token2> } ]}

    :rtype: json
    """
    param = request.all_data
    user = get_user_from_param(param, optional)
    serial = getParam(param, "serial", optional)
    page = int(getParam(param, "page", optional, default=1))
    tokentype = getParam(param, "type", optional)
    description = getParam(param, "description", optional)
    sort = getParam(param, "sortby", optional, default="serial")
    sdir = getParam(param, "sortdir", optional, default="asc")
    psize = int(getParam(param, "pagesize", optional, default=15))
    realm = getParam(param, "tokenrealm", optional)
    userid = getParam(param, "userid", optional)
    resolver = getParam(param, "resolver", optional)
    ufields = getParam(param, "user_fields", optional)
    output_format = getParam(param, "outform", optional)
    assigned = getParam(param, "assigned", optional)
    if assigned:
        assigned = assigned.lower() == "true"

    user_fields = []
    if ufields:
        user_fields = [u.strip() for u in ufields.split(",")]

    # filterRealm determines, which realms the admin would be allowed to see
    filterRealm = ["*"]
    # TODO: Userfields

    # If the admin wants to see only one realm, then do it:
    if realm and (realm in filterRealm or '*' in filterRealm):
        filterRealm = [realm]
    g.audit_object.log({'info': "realm: {0!s}".format((filterRealm))})

    # get list of tokens as a dictionary
    tokens = get_tokens_paginate(serial=serial,
                                 realm=realm,
                                 page=page,
                                 user=user,
                                 assigned=assigned,
                                 psize=psize,
                                 sortby=sort,
                                 sortdir=sdir,
                                 tokentype=tokentype,
                                 resolver=resolver,
                                 description=description,
                                 userid=userid)
    g.audit_object.log({"success": True})
    if output_format == "csv":
        return send_csv_result(tokens)
    else:
        return send_result(tokens)
Esempio n. 35
0
def set_api(serial=None):
    """
    This API is only to be used by the admin!
    This can be used to set token specific attributes like

        * description
        * count_window
        * sync_window
        * count_auth_max
        * count_auth_success_max
        * hashlib,
        * max_failcount

    The token is identified by the unique serial number or by the token owner.
    In the later case all tokens of the owner will be modified.

    :jsonparam basestring serial: the serial number of the single token to reset
    :jsonparam basestring user: The username of the token owner
    :jsonparam basestring realm: The realm name of the token owner
    :return: returns the number of attributes set in "value"
    :rtype: json object
    """
    if not serial:
        serial = getParam(request.all_data, "serial", required)
    g.audit_object.log({"serial": serial})
    user = get_user_from_param(request.all_data)

    description = getParam(request.all_data, "description")
    count_window = getParam(request.all_data, "count_window")
    sync_window = getParam(request.all_data, "sync_window")
    hashlib = getParam(request.all_data, "hashlib")
    max_failcount = getParam(request.all_data, "max_failcount")
    count_auth_max = getParam(request.all_data, "count_auth_max")
    count_auth_success_max = getParam(request.all_data,
                                      "count_auth_success_max")
    validity_period_start = getParam(request.all_data, "validity_period_start")
    validity_period_end = getParam(request.all_data, "validity_period_end")

    res = 0

    if description is not None:
        g.audit_object.add_to_log(
            {'action_detail': "description=%r, "
             "" % description})
        res += set_description(serial, description, user=user)

    if count_window is not None:
        g.audit_object.add_to_log(
            {'action_detail': "count_window=%r, "
             "" % count_window})
        res += set_count_window(serial, count_window, user=user)

    if sync_window is not None:
        g.audit_object.add_to_log(
            {'action_detail': "sync_window=%r, "
             "" % sync_window})
        res += set_sync_window(serial, sync_window, user=user)

    if hashlib is not None:
        g.audit_object.add_to_log(
            {'action_detail': "hashlib=%r, "
             "" % hashlib})
        res += set_hashlib(serial, hashlib, user=user)

    if max_failcount is not None:
        g.audit_object.add_to_log(
            {'action_detail': "max_failcount=%r, "
             "" % max_failcount})
        res += set_max_failcount(serial, max_failcount, user=user)

    if count_auth_max is not None:
        g.audit_object.add_to_log(
            {'action_detail': "count_auth_max=%r, "
             "" % count_auth_max})
        res += set_count_auth(serial, count_auth_max, user=user, max=True)

    if count_auth_success_max is not None:
        g.audit_object.add_to_log({
            'action_detail':
            "count_auth_success_max={0!r}, ".format(count_auth_success_max)
        })
        res += set_count_auth(serial,
                              count_auth_success_max,
                              user=user,
                              max=True,
                              success=True)

    if validity_period_end is not None:
        g.audit_object.add_to_log({
            'action_detail':
            "validity_period_end={0!r}, ".format(validity_period_end)
        })
        res += set_validity_period_end(serial, user, validity_period_end)

    if validity_period_start is not None:
        g.audit_object.add_to_log({
            'action_detail':
            "validity_period_start={0!r}, ".format(validity_period_start)
        })
        res += set_validity_period_start(serial, user, validity_period_start)

    g.audit_object.log({"success": True})
    return send_result(res)
Esempio n. 36
0
def loadtokens_api(filename=None):
    """
    The call imports the given file containing token definitions.
    The file can be an OATH CSV file, an aladdin XML file or a Yubikey CSV file
    exported from the yubikey initialization tool.

    The function is called as a POST request with the file upload.

    :jsonparam filename: The name of the token file, that is imported
    :jsonparam type: The file type. Can be "aladdin-xml",
        "oathcsv" or "yubikeycsv".
    :jsonparam tokenrealms: comma separated list of tokens.
    :jsonparam psk: Pre Shared Key, when importing PSKC
    :return: The number of the imported tokens
    :rtype: int
    """
    if not filename:
        filename = getParam(request.all_data, "filename", required)
    known_types = [
        'aladdin-xml', 'oathcsv', "OATH CSV", 'yubikeycsv', 'Yubikey CSV',
        'pskc'
    ]
    file_type = getParam(request.all_data, "type", required)
    hashlib = getParam(request.all_data, "aladdin_hashlib")
    aes_psk = getParam(request.all_data, "psk")
    aes_password = getParam(request.all_data, "password")
    if aes_psk and len(aes_psk) != 32:
        raise TokenAdminError("The Pre Shared Key must be 128 Bit hex "
                              "encoded. It must be 32 characters long!")
    trealms = getParam(request.all_data, "tokenrealms") or ""
    tokenrealms = []
    if trealms:
        tokenrealms = trealms.split(",")

    TOKENS = {}
    token_file = request.files['file']
    file_contents = ""
    # In case of form post requests, it is a "instance" of FieldStorage
    # i.e. the Filename is selected in the browser and the data is
    # transferred
    # in an iframe. see: http://jquery.malsup.com/form/#sample4
    #
    if type(token_file) == FieldStorage:  # pragma: no cover
        log.debug("Field storage file: %s", token_file)
        file_contents = token_file.value
    elif type(token_file) == FileStorage:
        log.debug("Werkzeug File storage file: %s", token_file)
        file_contents = token_file.read()
    else:  # pragma: no cover
        file_contents = token_file

    if file_contents == "":
        log.error(
            "Error loading/importing token file. file {0!s} empty!".format(
                filename))
        raise ParameterError("Error loading token file. File empty!")

    if file_type not in known_types:
        log.error(
            "Unknown file type: >>{0!s}<<. We only know the types: {1!s}".
            format(file_type, ', '.join(known_types)))
        raise TokenAdminError("Unknown file type: >>%s<<. We only know the "
                              "types: %s" %
                              (file_type, ', '.join(known_types)))

    # Decrypt file, if necessary
    if file_contents.startswith("-----BEGIN PGP MESSAGE-----"):
        GPG = GPGImport(current_app.config)
        file_contents = GPG.decrypt(file_contents)

    # Parse the tokens from file and get dictionary
    if file_type == "aladdin-xml":
        TOKENS = parseSafeNetXML(file_contents)
    elif file_type in ["oathcsv", "OATH CSV"]:
        TOKENS = parseOATHcsv(file_contents)
    elif file_type in ["yubikeycsv", "Yubikey CSV"]:
        TOKENS = parseYubicoCSV(file_contents)
    elif file_type in ["pskc"]:
        TOKENS = parsePSKCdata(file_contents,
                               preshared_key_hex=aes_psk,
                               password=aes_password)

    # Now import the Tokens from the dictionary
    ret = ""
    for serial in TOKENS:
        log.debug("importing token {0!s}".format(TOKENS[serial]))

        log.info("initialize token. serial: {0!s}, realm: {1!s}".format(
            serial, tokenrealms))

        init_param = {
            'serial': serial,
            'type': TOKENS[serial]['type'],
            'description': TOKENS[serial].get("description", "imported"),
            'otpkey': TOKENS[serial]['otpkey'],
            'otplen': TOKENS[serial].get('otplen'),
            'timeStep': TOKENS[serial].get('timeStep'),
            'hashlib': TOKENS[serial].get('hashlib')
        }

        if hashlib and hashlib != "auto":
            init_param['hashlib'] = hashlib

        #if tokenrealm:
        #    self.Policy.checkPolicyPre('admin', 'loadtokens',
        #                   {'tokenrealm': tokenrealm })

        init_token(init_param, tokenrealms=tokenrealms)

    g.audit_object.log({
        'info':
        "{0!s}, {1!s} (imported: {2:d})".format(file_type, token_file,
                                                len(TOKENS)),
        'serial':
        ', '.join(TOKENS.keys())
    })
    # logTokenNum()

    return send_result(len(TOKENS))
Esempio n. 37
0
def register_post():
    """
    Register a new user in the realm/userresolver. To do so, the user
    resolver must be writeable like an SQLResolver.

    Registering a user in fact creates a new user and also creates the first
    token for the user. The following values are needed to register the user:

    * username (mandatory)
    * givenname (mandatory)
    * surname (mandatory)
    * email address (mandatory)
    * password (mandatory)
    * mobile phone (optional)
    * telephone (optional)

    The user receives a registration token via email to be able to login with
    his self chosen password and the registration token.

    :jsonparam username: The login name of the new user. Check if it already
        exists
    :jsonparam givenname: The givenname of the new user
    :jsonparam surname: The surname of the new user
    :jsonparam email: The email address of the new user
    :jsonparam password: The password of the new user. This is the resolver
        password of the new user.
    :jsonparam mobile: The mobile phone number
    :jsonparam phone: The phone number (land line) of the new user

    :return: a json result with a boolean "result": true
    """
    username = getParam(request.all_data, "username", required)
    surname = getParam(request.all_data, "surname", required)
    givenname = getParam(request.all_data, "givenname", required)
    email = getParam(request.all_data, "email", required)
    password = getParam(request.all_data, "password", required)
    mobile = getParam(request.all_data, "mobile")
    phone = getParam(request.all_data, "phone")
    options = {"g": g, "clientip": g.client_ip}
    g.audit_object.log({"info": username})
    # Add all params to the options
    for key, value in request.all_data.items():
        if value and key not in ["g", "clientip"]:
            options[key] = value

    # 0. check, if we can do the registration at all!
    smtpconfig = g.policy_object.get_action_values(ACTION.EMAILCONFIG,
                                                   scope=SCOPE.REGISTER,
                                                   unique=True)
    if not smtpconfig:
        raise RegistrationError("No SMTP server configuration specified!")

    # 1. determine, in which resolver/realm the user should be created
    realm = g.policy_object.get_action_values(ACTION.REALM,
                                              scope=SCOPE.REGISTER,
                                              unique=True)
    if not realm:
        # No policy for realm, so we use the default realm
        realm = get_default_realm
    else:
        # we use the first realm in the list
        realm = realm[0]
    resolvername = g.policy_object.get_action_values(ACTION.RESOLVER,
                                                     scope=SCOPE.REGISTER,
                                                     unique=True)
    if not resolvername:
        raise RegistrationError("No resolver specified to register in!")
    resolvername = resolvername[0]
    # Check if the user exists
    user = User(username, realm=realm, resolver=resolvername)
    if user.exist():
        raise RegistrationError("The username is already registered!")
    # Create user
    uid = create_user(
        resolvername, {
            "username": username,
            "email": email,
            "phone": phone,
            "mobile": mobile,
            "surname": surname,
            "givenname": givenname,
            "password": password
        })

    # 3. create a registration token for this user
    user = User(username, realm=realm, resolver=resolvername)
    token = init_token({"type": "registration"}, user=user)
    # 4. send the registration token to the users email
    registration_key = token.init_details.get("otpkey")

    smtpconfig = smtpconfig[0]
    # Send the registration key via email
    body = g.policy_object.get_action_values(ACTION.REGISTERBODY,
                                             scope=SCOPE.REGISTER,
                                             unique=True)
    body = body or DEFAULT_BODY
    email_sent = send_email_identifier(smtpconfig, email,
                                       "Your privacyIDEA registration",
                                       body.format(regkey=registration_key))
    if not email_sent:
        log.warning("Failed to send registration email to {0!s}".format(email))
        # delete registration token
        token.delete()
        # delete user
        user.delete()
        raise RegistrationError("Failed to send email!")

    log.debug("Registration email sent to {0!s}".format(email))

    g.audit_object.log({"success": email_sent})
    return send_result(email_sent)
Esempio n. 38
0
def check():
    """
    check the authentication for a user or a serial number.
    Either a ``serial`` or a ``user`` is required to authenticate.
    The PIN and OTP value is sent in the parameter ``pass``.
    In case of successful authentication it returns ``result->value: true``.

    :param serial: The serial number of the token, that tries to authenticate.
    :param user: The loginname/username of the user, who tries to authenticate.
    :param realm: The realm of the user, who tries to authenticate. If the
        realm is omitted, the user is looked up in the default realm.
    :param pass: The password, that consists of the OTP PIN and the OTP value.
    :param otponly: If set to 1, only the OTP value is verified. This is used
        in the management UI. Only used with the parameter serial.
    :param transaction_id: The transaction ID for a response to a challenge
        request
    :param state: The state ID for a response to a challenge request

    :return: a json result with a boolean "result": true

    **Example Validation Request**:

        .. sourcecode:: http

           POST /validate/check HTTP/1.1
           Host: example.com
           Accept: application/json

           user=user
           realm=realm1
           pass=s3cret123456

    **Example response** for a successful authentication:

       .. sourcecode:: http

           HTTP/1.1 200 OK
           Content-Type: application/json

            {
              "detail": {
                "message": "matching 1 tokens",
                "serial": "PISP0000AB00",
                "type": "spass"
              },
              "id": 1,
              "jsonrpc": "2.0",
              "result": {
                "status": true,
                "value": true
              },
              "version": "privacyIDEA unknown"
            }
    """
    #user = get_user_from_param(request.all_data)
    user = request.User
    serial = getParam(request.all_data, "serial")
    password = getParam(request.all_data, "pass", required)
    otp_only = getParam(request.all_data, "otponly")
    options = {"g": g, "clientip": g.client_ip}
    # Add all params to the options
    for key, value in request.all_data.items():
        if value and key not in ["g", "clientip"]:
            options[key] = value

    g.audit_object.log({
        "user": user.login,
        "resolver": user.resolver,
        "realm": user.realm
    })

    if serial:
        if not otp_only:
            result, details = check_serial_pass(serial,
                                                password,
                                                options=options)
        else:
            result, details = check_otp(serial, password)

    else:
        result, details = check_user_pass(user, password, options=options)

    g.audit_object.log({
        "info": details.get("message"),
        "success": result,
        "serial": serial or details.get("serial"),
        "tokentype": details.get("type")
    })
    return send_result(result, details=details)
Esempio n. 39
0
def samlcheck():
    """
    Authenticate the user and return the SAML user information.

    :param user: The loginname/username of the user, who tries to authenticate.
    :param realm: The realm of the user, who tries to authenticate. If the
        realm is omitted, the user is looked up in the default realm.
    :param pass: The password, that consists of the OTP PIN and the OTP value.

    :return: a json result with a boolean "result": true

    **Example response** for a successful authentication:

       .. sourcecode:: http

           HTTP/1.1 200 OK
           Content-Type: application/json

            {
              "detail": {
                "message": "matching 1 tokens",
                "serial": "PISP0000AB00",
                "type": "spass"
              },
              "id": 1,
              "jsonrpc": "2.0",
              "result": {
                "status": true,
                "value": {"attributes": {
                            "username": "******",
                            "realm": "themis",
                            "mobile": null,
                            "phone": null,
                            "myOwn": "/data/file/home/koelbel",
                            "resolver": "themis",
                            "surname": "Kölbel",
                            "givenname": "Cornelius",
                            "email": null},
                          "auth": true}
              },
              "version": "privacyIDEA unknown"
            }

    The response in value->attributes can contain additional attributes
    (like "myOwn") which you can define in the LDAP resolver in the attribute
    mapping.
    """
    user = get_user_from_param(request.all_data)
    password = getParam(request.all_data, "pass", required)
    options = {"g": g, "clientip": g.client_ip}
    # Add all params to the options
    for key, value in request.all_data.items():
        if value and key not in ["g", "clientip"]:
            options[key] = value

    auth, details = check_user_pass(user, password, options=options)
    ui = user.info
    result_obj = {"auth": auth, "attributes": {}}
    if return_saml_attributes():
        if auth or return_saml_attributes_on_fail():
            # privacyIDEA's own attribute map
            result_obj["attributes"] = {
                "username": ui.get("username"),
                "realm": user.realm,
                "resolver": user.resolver,
                "email": ui.get("email"),
                "surname": ui.get("surname"),
                "givenname": ui.get("givenname"),
                "mobile": ui.get("mobile"),
                "phone": ui.get("phone")
            }
            # additional attributes
            for k, v in ui.iteritems():
                result_obj["attributes"][k] = v

    g.audit_object.log({
        "info": details.get("message"),
        "success": auth,
        "serial": details.get("serial"),
        "tokentype": details.get("type"),
        "user": user.login,
        "resolver": user.resolver,
        "realm": user.realm
    })
    return send_result(result_obj, details=details)
Esempio n. 40
0
def trigger_challenge():
    """
    An administrator can call this endpoint if he has the right of
    ``triggerchallenge`` (scope: admin).
    He can pass a ``user`` name and or a ``serial`` number.
    privacyIDEA will trigger challenges for all native challenges response
    tokens, possessed by this user or only for the given serial number.

    The request needs to contain a valid PI-Authorization header.

    :param user: The loginname/username of the user, who tries to authenticate.
    :param realm: The realm of the user, who tries to authenticate. If the
        realm is omitted, the user is looked up in the default realm.
    :param serial: The serial number of the token.

    :return: a json result with a "result" of the number of matching
        challenge response tokens

    **Example response** for a successful triggering of challenge:

       .. sourcecode:: http

           {"jsonrpc": "2.0",
            "signature": "1939...146964",
            "detail": {"transaction_ids": ["03921966357577766962"],
                       "messages": ["Enter the OTP from the SMS:"],
                       "threadid": 140422378276608},
            "versionnumber": "unknown",
            "version": "privacyIDEA unknown",
            "result": {"status": true,
                       "value": 1},
            "time": 1482223663.517212,
            "id": 1}

    **Example response** for response, if the user has no challenge token:

       .. sourcecode:: http

           {"detail": {"messages": [],
                       "threadid": 140031212377856,
                       "transaction_ids": []},
            "id": 1,
            "jsonrpc": "2.0",
            "result": {"status": true,
                       "value": 0},
            "signature": "205530282...54508",
            "time": 1484303812.346576,
            "version": "privacyIDEA 2.17",
            "versionnumber": "2.17"}

    **Example response** for a failed triggering of a challenge. In this case
        the ``status`` will be ``false``.

       .. sourcecode:: http

           {"detail": null,
            "id": 1,
            "jsonrpc": "2.0",
            "result": {"error": {"code": 905,
                                 "message": "ERR905: The user can not be
                                 found in any resolver in this realm!"},
                       "status": false},
            "signature": "14468...081555",
            "time": 1484303933.72481,
            "version": "privacyIDEA 2.17"}

    """
    user = request.User
    serial = getParam(request.all_data, "serial")
    result_obj = 0
    details = {"messages": [], "transaction_ids": []}
    options = {"g": g, "clientip": g.client_ip, "user": user}

    token_objs = get_tokens(serial=serial, user=user)
    for token_obj in token_objs:
        if "challenge" in token_obj.mode:
            # If this is a challenge response token, we create a challenge
            success, return_message, transactionid, attributes = \
                token_obj.create_challenge(options=options)
            if attributes:
                details["attributes"] = attributes
            if success:
                result_obj += 1
                details.get("transaction_ids").append(transactionid)
                # This will write only the serial of the token that was processed last to the audit log
                g.audit_object.log({
                    "serial": token_obj.token.serial,
                })
            details.get("messages").append(return_message)

    g.audit_object.log({
        "user": user.login,
        "resolver": user.resolver,
        "realm": user.realm,
        "success": result_obj > 0,
        "info": "triggered {0!s} challenges".format(result_obj),
    })

    return send_result(result_obj, details=details)
Esempio n. 41
0
def get_policy(name=None, export=None):
    """
    this function is used to retrieve the policies that you
    defined.
    It can also be used to export the policy to a file.

    :query name: will only return the policy with the given name
    :query export: The filename needs to be specified as the
        third part of the URL like policy.cfg. It
        will then be exported to this file.
    :query realm: will return all policies in the given realm
    :query scope: will only return the policies within the given scope
    :query active: Set to true or false if you only want to display
        active or inactive policies.

    :return: a json result with the configuration of the specified policies
    :rtype: json

    :status 200: Policy created or modified.
    :status 401: Authentication failed

    **Example request**:

    In this example a policy "pol1" is created.

    .. sourcecode:: http

       GET /policy/pol1 HTTP/1.1
       Host: example.com
       Accept: application/json

    **Example response**:

    .. sourcecode:: http

       HTTP/1.0 200 OK
       Content-Type: application/json

        {
          "id": 1,
          "jsonrpc": "2.0",
          "result": {
            "status": true,
            "value": {
              "pol_update_del": {
                "action": "enroll",
                "active": true,
                "client": "1.1.1.1",
                "name": "pol_update_del",
                "realm": "r1",
                "resolver": "test",
                "scope": "selfservice",
                "time": "",
                "user": "******"
              }
            }
          },
          "version": "privacyIDEA unknown"
        }
    """
    param = getLowerParams(request.all_data)
    realm = getParam(param, "realm")
    scope = getParam(param, "scope")
    active = getParam(param, "active")

    P = g.policy_object
    if not export:
        log.debug(
            "retrieving policy name: {0!s}, realm: {1!s}, scope: {2!s}".format(
                name, realm, scope))

        pol = P.get_policies(name=name,
                             realm=realm,
                             scope=scope,
                             active=active,
                             all_times=True)
        ret = send_result(pol)
    else:
        # We want to export all policies
        pol = P.get_policies()
        response = make_response(export_policies(pol))
        response.headers["Content-Disposition"] = ("attachment; "
                                                   "filename=%s" % export)
        ret = response

    g.audit_object.log({
        "success":
        True,
        'info':
        "name = {0!s}, realm = {1!s}, scope = {2!s}".format(
            name, realm, scope)
    })
    return ret
Esempio n. 42
0
def set_policy_api(name=None):
    """
    Creates a new policy that defines access or behaviour of different
    actions in privacyIDEA

    :jsonparam basestring name: name of the policy
    :jsonparam scope: the scope of the policy like "admin", "system",
        "authentication" or "selfservice"
    :jsonparam adminrealm: Realm of the administrator. (only for admin scope)
    :jsonparam action: which action may be executed
    :jsonparam realm: For which realm this policy is valid
    :jsonparam resolver: This policy is valid for this resolver
    :jsonparam user: The policy is valid for these users.
        string with wild cards or list of strings
    :jsonparam time: on which time does this policy hold
    :jsonparam client: for which requesting client this should be
    :jsontype client: IP address with subnet

    :return: a json result with success or error

    :status 200: Policy created or modified.
    :status 401: Authentication failed

    **Example request**:

    In this example a policy "pol1" is created.

    .. sourcecode:: http

       POST /policy/pol1 HTTP/1.1
       Host: example.com
       Accept: application/json

       scope=admin
       realm=realm1
       action=enroll, disable

    **Example response**:

    .. sourcecode:: http

       HTTP/1.0 200 OK
       Content-Length: 354
       Content-Type: application/json

        {
          "id": 1,
          "jsonrpc": "2.0",
          "result": {
            "status": true,
            "value": {
              "setPolicy pol1": 1
            }
          },
          "version": "privacyIDEA unknown"
        }
    """
    res = {}
    param = request.all_data
    if not re.match('^[a-zA-Z0-9_.]*$', name):
        raise ParameterError(
            _("The name of the policy may only contain "
              "the characters a-zA-Z0-9_."))

    if name.lower() == "check":
        raise ParameterError(_("T'check' is an invalid policy name."))

    action = getParam(param, "action", required)
    scope = getParam(param, "scope", required)
    realm = getParam(param, "realm", required)
    resolver = getParam(param, "resolver", optional)
    user = getParam(param, "user", optional)
    time = getParam(param, "time", optional)
    client = getParam(param, "client", optional)
    active = getParam(param, "active", optional)
    admin_realm = getParam(param, "adminrealm", optional)

    g.audit_object.log({'action_detail': name, 'info': "{0!s}".format(param)})
    ret = set_policy(name=name,
                     scope=scope,
                     action=action,
                     realm=realm,
                     resolver=resolver,
                     user=user,
                     client=client,
                     time=time,
                     active=active or True,
                     adminrealm=admin_realm)
    log.debug("policy {0!s} successfully saved.".format(name))
    string = "setPolicy " + name
    res[string] = ret
    g.audit_object.log({"success": True})

    return send_result(res)
Esempio n. 43
0
def samlcheck():
    """
    Authenticate the user and return the SAML user information.

    :param user: The loginname/username of the user, who tries to authenticate.
    :param realm: The realm of the user, who tries to authenticate. If the
        realm is omitted, the user is looked up in the default realm.
    :param pass: The password, that consists of the OTP PIN and the OTP value.

    :return: a json result with a boolean "result": true

    **Example response** for a successful authentication:

       .. sourcecode:: http

           HTTP/1.1 200 OK
           Content-Type: application/json

            {
              "detail": {
                "message": "matching 1 tokens",
                "serial": "PISP0000AB00",
                "type": "spass"
              },
              "id": 1,
              "jsonrpc": "2.0",
              "result": {
                "status": true,
                "value": {"auth": true,
                          "username: <loginname>,
                          "realm": ....,
                          "surname": ....,
                          "givenname": .....,
                          "mobile": ....,
                          "phone": ....,
                          "email": ....
                }
              },
              "version": "privacyIDEA unknown"
            }
    """
    user = get_user_from_param(request.all_data)
    password = getParam(request.all_data, "pass", required)
    options = {"g": g, "clientip": request.remote_addr}
    # Add all params to the options
    for key, value in request.all_data.items():
        if value and key not in ["g", "clientip"]:
            options[key] = value

    auth, details = check_user_pass(user, password, options=options)
    ui = user.info
    result_obj = {"auth": auth, "attributes": {}}
    if return_saml_attributes():
        result_obj["attributes"] = {
            "username": ui.get("username"),
            "realm": user.realm,
            "resolver": user.resolver,
            "email": ui.get("email"),
            "surname": ui.get("surname"),
            "givenname": ui.get("givenname"),
            "mobile": ui.get("mobile"),
            "phone": ui.get("phone")
        }

    g.audit_object.log({
        "info": details.get("message"),
        "success": auth,
        "serial": details.get("serial"),
        "tokentype": details.get("type"),
        "user": user.login,
        "realm": user.realm
    })
    return send_result(result_obj, details=details)
Esempio n. 44
0
def import_policy_api(filename=None):
    """
    This function is used to import policies from a file.

    :jsonparam filename: The name of the file in the request

    :formparam file: The uploaded file contents

    :return: A json response with the number of imported policies.

    :status 200: Policy created or modified.
    :status 401: Authentication failed

    **Example request**:

    .. sourcecode:: http

       POST /policy/import/backup-policy.cfg HTTP/1.1
       Host: example.com
       Accept: application/json

    **Example response**:

    .. sourcecode:: http

       HTTP/1.0 200 OK
       Content-Type: application/json

        {
          "id": 1,
          "jsonrpc": "2.0",
          "result": {
            "status": true,
            "value": 2
          },
          "version": "privacyIDEA unknown"
        }


    """
    policy_file = request.files['file']
    file_contents = ""
    # In case of form post requests, it is a "instance" of FieldStorage
    # i.e. the Filename is selected in the browser and the data is
    # transferred
    # in an iframe. see: http://jquery.malsup.com/form/#sample4
    #
    if type(policy_file) == FieldStorage:  # pragma: no cover
        log.debug("Field storage file: %s", policy_file)
        file_contents = policy_file.value
    elif type(policy_file) == FileStorage:
        log.debug("Werkzeug File storage file: %s", policy_file)
        file_contents = policy_file.read()
    else:  # pragma: no cover
        file_contents = policy_file

    if file_contents == "":
        log.error(
            "Error loading/importing policy file. file {0!s} empty!".format(
                filename))
        raise ParameterError("Error loading policy. File empty!")

    policy_num = import_policies(file_contents=file_contents)
    g.audit_object.log({
        "success":
        True,
        'info':
        "imported {0:d} policies from file {1!s}".format(policy_num, filename)
    })

    return send_result(policy_num)
Esempio n. 45
0
def check():
    """
    check the authentication for a user or a serial number.
    Either a ``serial`` or a ``user`` is required to authenticate.
    The PIN and OTP value is sent in the parameter ``pass``.

    :param serial: The serial number of the token, that tries to authenticate.
    :param user: The loginname/username of the user, who tries to authenticate.
    :param realm: The realm of the user, who tries to authenticate. If the
        realm is omitted, the user is looked up in the default realm.
    :param pass: The password, that consists of the OTP PIN and the OTP value.
    :param transaction_id: The transaction ID for a response to a challenge
        request
    :param state: The state ID for a response to a challenge request

    :return: a json result with a boolean "result": true

    **Example response** for a successful authentication:

       .. sourcecode:: http

           HTTP/1.1 200 OK
           Content-Type: application/json

            {
              "detail": {
                "message": "matching 1 tokens",
                "serial": "PISP0000AB00",
                "type": "spass"
              },
              "id": 1,
              "jsonrpc": "2.0",
              "result": {
                "status": true,
                "value": true
              },
              "version": "privacyIDEA unknown"
            }
    """
    user = get_user_from_param(request.all_data)
    serial = getParam(request.all_data, "serial")
    password = getParam(request.all_data, "pass", required)
    options = {"g": g, "clientip": request.remote_addr}
    # Add all params to the options
    for key, value in request.all_data.items():
        if value and key not in ["g", "clientip"]:
            options[key] = value

    g.audit_object.log({"user": user.login, "realm": user.realm})

    if serial:
        result, details = check_serial_pass(serial, password, options=options)
    else:
        result, details = check_user_pass(user, password, options=options)

    g.audit_object.log({
        "info": details.get("message"),
        "success": result,
        "serial": serial or details.get("serial"),
        "tokentype": details.get("type")
    })
    return send_result(result, details=details)
Esempio n. 46
0
def list_machines_api():
    """
    List all machines that can be found in the machine resolvers.

    :param hostname: only show machines, that match this hostname as substring
    :param ip: only show machines, that exactly match this IP address
    :param id: filter for substring matching ids
    :param resolver: filter for substring matching resolvers
    :param any: filter for a substring either matching in "hostname", "ip"
        or "id"
    
    :return: json result with "result": true and the machine list in "value".

    **Example request**:

    .. sourcecode:: http

       GET /hostname?hostname=on HTTP/1.1
       Host: example.com
       Accept: application/json

    **Example response**:

    .. sourcecode:: http

       HTTP/1.1 200 OK
       Content-Type: application/json

        {
          "id": 1,
          "jsonrpc": "2.0",
          "result": {
            "status": true,
            "value": [
              {
                "id": "908asljdas90ad0",
                "hostname": [ "flavon.example.com", "test.example.com" ],
                "ip": "1.2.3.4",
                "resolver_name": "machineresolver1"
              },
              {
                "id": "1908209x48x2183",
                "hostname": [ "london.example.com" ],
                "ip": "2.4.5.6",
                "resolver_name": "machineresolver1"
              }
            ]
          },
          "version": "privacyIDEA unknown"
        }
    """
    hostname = getParam(request.all_data, "hostname")
    ip = getParam(request.all_data, "ip")
    if ip:
        try:
            ip = netaddr.IPAddress(ip)
        except netaddr.AddrFormatError:
            # This happens when filtering in the machine view
            ip = None
    id = getParam(request.all_data, "id")
    resolver = getParam(request.all_data, "resolver")

    any = getParam(request.all_data, "any")

    machines = get_machines(hostname=hostname, ip=ip, id=id, resolver=resolver,
                            any=any)
    # this returns a list of Machine Object. This is not JSON serialiable,
    # so we need to convert the Machine Object to dict
    machines = [mobject.get_dict() for mobject in machines]
    g.audit_object.log({'success': True,
                        'info': "hostname: {0!s}, ip: {1!s}".format(hostname, ip)})
    
    return send_result(machines)