Пример #1
0
    def test_12_resolver_priority(self):
        # Test the priority of resolvers.
        # we create resolvers with the same user in it. Depending on the
        # priority we either get the one or the other user.
        save_resolver({"resolver": "double1",
                       "type": "passwdresolver",
                       "fileName": PWFILE})
        save_resolver({"resolver": "double2",
                       "type": "passwdresolver",
                       "fileName": PWFILE})
        save_resolver({"resolver": "double3",
                       "type": "passwdresolver",
                       "fileName": PWFILE})

        (added, failed) = set_realm("double",
                                    ["double1", "double2", "double3"],
                                    priority={"double1": 2,
                                              "double2": 1,
                                              "double3": 3})
        self.assertEqual(len(failed), 0)
        self.assertEqual(len(added), 3)

        user = get_user_from_param({"user": "******", "realm": "double"})
        self.assertEqual(user.resolver, "double2")

        (added, failed) = set_realm("double",
                                    ["double1", "double2", "double3"],
                                    priority={"double1": 3,
                                              "double2": 2,
                                              "double3": 1})
        self.assertEqual(len(failed), 0)
        self.assertEqual(len(added), 3)

        user = get_user_from_param({"user": "******", "realm": "double"})
        self.assertEqual(user.resolver, "double3")
Пример #2
0
 def test_09_get_user_from_param(self):
     user = get_user_from_param({"user": "******"})
     self.assertTrue(user.realm == self.realm1, user)
     self.assertTrue(user.resolver == self.resolvername1, user)
     
     user = get_user_from_param({"realm": self.realm1})
     self.assertTrue(user.realm == self.realm1, user)
     self.assertTrue(user.login == "", user)
     self.assertTrue(user.resolver == "", user.resolver)
     
     user = get_user_from_param({"user": "******",
                                 "resolver": self.resolvername1})
     self.assertTrue(user.realm == self.realm1, user)
     
     # create a realm, where cornelius is in two resolvers!
     rid = save_resolver({"resolver": self.resolvername3,
                            "type": "passwdresolver",
                            "fileName": PWFILE2})
     self.assertTrue(rid > 0, rid)
            
     (added, failed) = set_realm(self.realm2,
                                 [self.resolvername1,
                                  self.resolvername3])
     self.assertTrue(len(failed) == 0)
     self.assertTrue(len(added) == 2)
     
     # get user cornelius, who is in two resolvers!
     param = {"user": "******",
              "realm": self.realm2}
     user = get_user_from_param(param)
     self.assertEqual("{0!s}".format(user), "<cornelius.resolver1@realm2>")
Пример #3
0
    def update(self, param):
        """
        This method is called during the initialization process.
        :param param: parameters from the token init
        :type param: dict
        :return: None
        """
        TokenClass.update(self, param)

        request = getParam(param, "request", optional)
        spkac = getParam(param, "spkac", optional)
        certificate = getParam(param, "certificate", optional)
        generate = getParam(param, "genkey", optional)
        if request or generate:
            # If we do not upload a user certificate, then we need a CA do
            # sign the uploaded request or generated certificate.
            ca = getParam(param, "ca", required)
            self.add_tokeninfo("CA", ca)
            cacon = get_caconnector_object(ca)
        if request:
            # During the initialization process, we need to create the
            # certificate
            x509object = cacon.sign_request(request,
                                            options={"spkac": spkac})
            certificate = crypto.dump_certificate(crypto.FILETYPE_PEM,
                                                  x509object)
        elif generate:
            # Create the certificate on behalf of another user.
            # Now we need to create the key pair,
            # the request
            # and the certificate
            # We need the user for whom the certificate should be created
            user = get_user_from_param(param, optionalOrRequired=required)

            keysize = getParam(param, "keysize", optional, 2048)
            key = crypto.PKey()
            key.generate_key(crypto.TYPE_RSA, keysize)
            req = crypto.X509Req()
            req.get_subject().CN = user.login
            # Add email to subject
            if user.info.get("email"):
                req.get_subject().emailAddress = user.info.get("email")
            req.get_subject().organizationalUnitName = user.realm
            # TODO: Add Country, Organization, Email
            # req.get_subject().countryName = 'xxx'
            # req.get_subject().stateOrProvinceName = 'xxx'
            # req.get_subject().localityName = 'xxx'
            # req.get_subject().organizationName = 'xxx'
            req.set_pubkey(key)
            req.sign(key, "sha256")
            x509object = cacon.sign_request(crypto.dump_certificate_request(
                crypto.FILETYPE_PEM, req))
            certificate = crypto.dump_certificate(crypto.FILETYPE_PEM,
                                                  x509object)
            # Save the private key to the encrypted key field of the token
            s = crypto.dump_privatekey(crypto.FILETYPE_PEM, key)
            self.add_tokeninfo("privatekey", s, value_type="password")

        if certificate:
            self.add_tokeninfo("certificate", certificate)
Пример #4
0
def check_max_token_user(request=None, action=None):
    """
    Pre Policy
    This checks the maximum token per user policy.
    Check ACTION.MAXTOKENUSER

    This decorator can wrap:
        /token/init  (with a realm and user)
        /token/assign

    :param req:
    :param action:
    :return: True otherwise raises an Exception
    """
    ERROR = "The number of tokens for this user is limited!"
    params = request.all_data
    user_object = get_user_from_param(params)
    if user_object.login:
        policy_object = g.policy_object
        limit_list = policy_object.get_action_values(ACTION.MAXTOKENUSER,
                                                     scope=SCOPE.ENROLL,
                                                     realm=user_object.realm,
                                                     user=user_object.login,
                                                     client=request.remote_addr)
        if len(limit_list) > 0:
            # we need to check how many tokens the user already has assigned!
            tokenobject_list = get_tokens(user=user_object)
            already_assigned_tokens = len(tokenobject_list)
            if already_assigned_tokens >= int(max(limit_list)):
                raise PolicyError(ERROR)
    return True
Пример #5
0
def init_tokenlabel(request=None, action=None):
    """
    This policy function is to be used as a decorator in the API init function.
    It adds the tokenlabel definition to the params like this:
    params : { "tokenlabel": "<u>@<r>" }

    It uses the policy SCOPE.ENROLL, ACTION.TOKENLABEL to set the tokenlabel
    of Smartphone tokens during enrollment and this fill the details of the
    response.
    """
    params = request.all_data
    policy_object = g.policy_object
    user_object = get_user_from_param(params)
    # get the serials from a policy definition
    label_pols = policy_object.get_action_values(action=ACTION.TOKENLABEL,
                                                 scope=SCOPE.ENROLL,
                                                 user=user_object.login,
                                                 realm=user_object.realm,
                                                 client=request.remote_addr,
                                                 unique=True)

    if len(label_pols) == 1:
        # The policy was set, so we need to set the tokenlabel in the request.
        request.all_data["tokenlabel"] = label_pols[0]

    return True
Пример #6
0
def encrypt_pin(request=None, action=None):
    """
    This policy function is to be used as a decorator for several API functions.
    E.g. token/assign, token/setpin, token/init
    If the policy is set to define the PIN to be encrypted,
    the request.all_data is modified like this:
    encryptpin = True

    It uses the policy SCOPE.ENROLL, ACTION.ENCRYPTPIN
    """
    params = request.all_data
    policy_object = g.policy_object
    user_object = get_user_from_param(params)
    # get the length of the random PIN from the policies
    pin_pols = policy_object.get_policies(action=ACTION.ENCRYPTPIN,
                                          scope=SCOPE.ENROLL,
                                          user=user_object.login,
                                          realm=user_object.realm,
                                          client=request.remote_addr,
                                          active=True)

    if len(pin_pols) > 0:
        request.all_data["encryptpin"] = "True"
    else:
        if "encryptpin" in request.all_data:
            del request.all_data["encryptpin"]

    return True
Пример #7
0
def check_anonymous_user(request=None, action=None):
    """
    This decorator function takes the request and verifies the given action
    for the SCOPE USER without an authenticated user but the user from the
    parameters.

    This is used with password_reset

    :param request:
    :param action:
    :return: True otherwise raises an Exception
    """
    ERROR = "User actions are defined, but this action is not allowed!"
    params = request.all_data
    policy_object = g.policy_object
    scope = SCOPE.USER
    user_obj = get_user_from_param(params)
    username = user_obj.login
    realm = user_obj.realm

    action = policy_object.get_policies(action=action,
                                        user=username,
                                        realm=realm,
                                        scope=scope,
                                        client=request.remote_addr,
                                        adminrealm=None,
                                        active=True)
    action_at_all = policy_object.get_policies(scope=scope,
                                               active=True)
    if action_at_all and len(action) == 0:
        raise PolicyError(ERROR)
    return True
Пример #8
0
def before_request():
    """
    This is executed before the request
    """
    update_config_object()
    request.all_data = get_all_params(request.values, request.data)
    request.User = get_user_from_param(request.all_data)
    privacyidea_server = current_app.config.get("PI_AUDIT_SERVERNAME") or \
                         request.host
    # Create a policy_object, that reads the database audit settings
    # and contains the complete policy definition during the request.
    # This audit_object can be used in the postpolicy and prepolicy and it
    # can be passed to the innerpolicies.

    g.policy_object = PolicyClass()

    g.audit_object = getAudit(current_app.config)
    g.event_config = EventConfiguration()
    # access_route contains the ip addresses of all clients, hops and proxies.
    g.client_ip = get_client_ip(request, get_from_config(SYSCONF.OVERRIDECLIENT))
    g.audit_object.log({"success": False,
                        "action_detail": "",
                        "client": g.client_ip,
                        "client_user_agent": request.user_agent.browser,
                        "privacyidea_server": privacyidea_server,
                        "action": "{0!s} {1!s}".format(request.method, request.url_rule),
                        "info": ""})
Пример #9
0
def autoassign(request, response):
    """
    This decorator decorates the function /validate/check.
    Depending on ACTION.AUTOASSIGN it checks if the user has no token and if
    the given OTP-value matches a token in the users realm, that is not yet
    assigned to any user.

    If a token can be found, it assigns the token to the user also taking
    into account ACTION.MAXTOKENUSER and ACTION.MAXTOKENREALM.
    :return:
    """
    content = json.loads(response.data)
    # check, if the authentication was successful, then we need to do nothing
    if content.get("result").get("value") is False:
        user_obj = get_user_from_param(request.all_data)
        password = request.all_data.get("pass", "")
        if user_obj.login and user_obj.realm:
            # If there is no user in the request (because it is a serial
            # authentication request) we immediately bail out
            # check if the policy is defined
            policy_object = g.policy_object

            autoassign_bool = policy_object.get_policies(
                action=ACTION.AUTOASSIGN,
                scope=SCOPE.ENROLL,
                user=user_obj.login,
                realm=user_obj.realm,
                client=request.remote_addr,
                active=True,
            )

            if len(autoassign_bool) >= 1:
                # check if the user has no token
                if get_tokens(user=user_obj, count=True) == 0:
                    # Check is the token would match
                    # get all unassigned tokens in the realm and look for a matching OTP:
                    realm_tokens = get_tokens(realm=user_obj.realm, assigned=False)
                    for token_obj in realm_tokens:
                        (res, pin, otp) = token_obj.split_pin_pass(password)
                        # TODO: What do we want to do with the PIN?
                        # Check it against userstore?
                        if token_obj.check_otp(otp) >= 0:
                            # we found a matching token
                            #    check MAXTOKENUSER and MAXTOKENREALM
                            check_max_token_user(request=request)
                            check_max_token_realm(request=request)
                            #    Assign token
                            assign_token(serial=token_obj.token.serial, user=user_obj, pin=pin)
                            # Set the response to true
                            content.get("result")["value"] = True
                            # Set the serial number
                            if not content.get("detail"):
                                content["detail"] = {}
                            content.get("detail")["serial"] = token_obj.token.serial
                            content.get("detail")["type"] = token_obj.type
                            content.get("detail")["message"] = "Token " "assigned to " "user via " "Autoassignment"
                            response.data = json.dumps(content)
                            break

    return response
Пример #10
0
def token(ttype=None):
    """
    This is a special token function. Each token type can define an
    additional API call, that does not need authentication on the REST API
    level.

    :return: Token Type dependent
    """
    tokenc = get_token_class(ttype)
    res = tokenc.api_endpoint(request, g)
    serial = getParam(request.all_data, "serial")
    user = get_user_from_param(request.all_data)
    g.audit_object.log({"success": 1,
                        "user": user.login,
                        "realm": user.realm,
                        "serial": serial,
                        "token_type": ttype})
    if res[0] == "json":
        return jsonify(res[1])
    elif res[0] in ["html", "plain"]:
        return Response(res[1], mimetype="text/{0!s}".format(res[0]))
    elif len(res) == 2:
        return Response(json.dumps(res[1]),
                        mimetype="application/{0!s}".format(res[0]))
    else:
        return Response(res[1], mimetype="application/octet-binary",
                        headers=res[2])
Пример #11
0
def twostep_enrollment_parameters(request=None, action=None):
    """
    If the ``2stepinit`` parameter is set to true, this policy function
    reads additional configuration from policies and adds it
    to ``request.all_data``, that is:

     * ``{type}_2step_serversize`` is written to ``2step_serversize``
     * ``{type}_2step_clientsize`` is written to ``2step_clientsize`
     * ``{type}_2step_difficulty`` is written to ``2step_difficulty``

    If no policy matches, the value passed by the user is kept.

    This policy function is used to decorate the ``/token/init`` endpoint.
    """
    policy_object = g.policy_object
    user_object = get_user_from_param(request.all_data)
    serial = getParam(request.all_data, "serial", optional)
    token_type = getParam(request.all_data, "type", optional, "hotp")
    if serial:
        tokensobject_list = get_tokens(serial=serial)
        if len(tokensobject_list) == 1:
            token_type = tokensobject_list[0].token.tokentype
    token_type = token_type.lower()
    role = g.logged_in_user.get("role")
    # Differentiate between an admin enrolling a token for the
    # user and a user self-enrolling a token.
    if role == ROLE.ADMIN:
        adminrealm = g.logged_in_user.get("realm")
    else:
        adminrealm = None
    realm = user_object.realm
    # In any case, the policy's user attribute is matched against the
    # currently logged-in user (which may be the admin or the
    # self-enrolling user).
    user = g.logged_in_user.get("username")
    # Tokentypes have separate twostep actions
    if is_true(getParam(request.all_data, "2stepinit", optional)):
        parameters = ("2step_serversize", "2step_clientsize", "2step_difficulty")
        for parameter in parameters:
            action = u"{}_{}".format(token_type, parameter)
            action_values = policy_object.get_action_values(action=action,
                                                            scope=SCOPE.ENROLL,
                                                            unique=True,
                                                            user=user,
                                                            realm=realm,
                                                            client=g.client_ip,
                                                            adminrealm=adminrealm)
            if action_values:
                request.all_data[parameter] = action_values[0]
Пример #12
0
def get_recover_code():
    """
    This method requests a recover code for a user. The recover code it sent
    via email to the user.

    :queryparam user: username of the user
    :queryparam realm: realm of the user
    :queryparam email: email of the user
    :return: JSON with value=True or value=False
    """
    param = request.all_data
    user_obj = get_user_from_param(param, required)
    email = getParam(param, "email", required)
    r = create_recoverycode(user_obj, email, base_url=request.base_url)
    g.audit_object.log({"success": r,
                        "info": u"{0!s}".format(user_obj)})
    return send_result(r)
Пример #13
0
    def do(self, action, options=None):
        """
        This method executes the defined action in the given event.

        :param action:
        :param environment:
        :param options:
        :return:
        """
        ret = True
        g = options.get("g")
        request = options.get("request")
        logged_in_user = g.logged_in_user
        user = get_user_from_param(request.all_data)
        if action.lower() == "sendmail" and logged_in_user.get("role") == \
                ROLE.ADMIN and not user.is_empty() and user.login:
            emailconfig = options.get("emailconfig")
            if not emailconfig:
                log.error("Missing parameter 'emailconfig'")
                raise ParameterError("Missing parameter 'emailconfig'")
            useremail = user.info.get("email")
            subject = options.get("subject") or "An action was performed on " \
                                                "your token."
            body = options.get("body") or DEFAULT_BODY
            body = body.format(
                admin=logged_in_user.get("username"),
                realm=logged_in_user.get("realm"),
                action=request.path,
                serial=g.audit_object.audit_data.get("serial"),
                url=request.url_root,
                user=user.info.get("givenname")
                )
            try:
                ret = send_email_identifier(emailconfig,
                                            recipient=useremail,
                                            subject=subject, body=body)
            except Exception as exx:
                log.error("Failed to send email: {0!s}".format(exx))
                ret = False
            if ret:
                log.info("Sent a notification email to user {0}".format(user))
            else:
                log.warning("Failed to send a notification email to user "
                            "{0}".format(user))

        return ret
Пример #14
0
def token(ttype=None):
    """
    This is a special token function. Each token type can define an
    additional API call, that does not need authentication on the REST API
    level.

    :return: Token Type dependent
    """
    tokenc = get_token_class(ttype)
    res = tokenc.api_endpoint(request.all_data)
    serial = getParam(request.all_data, "serial")
    user = get_user_from_param(request.all_data)
    g.audit_object.log({"success": 1, "user": user, "serial": serial, "tokentype": ttype})
    if res[0] == "json":
        return jsonify(res[1])
    elif res[0] == "text":
        return Response(res[1])
Пример #15
0
def init_random_pin(request=None, action=None):
    """
    This policy function is to be used as a decorator in the API init function.
    If the policy is set accordingly it adds a random PIN to the
    request.all_data like.

    It uses the policy SCOPE.ENROLL, ACTION.OTPPINRANDOM to set a random OTP
    PIN during Token enrollment
    """
    params = request.all_data
    policy_object = g.policy_object
    user_object = get_user_from_param(params)
    # get the length of the random PIN from the policies
    pin_pols = policy_object.get_action_values(action=ACTION.OTPPINRANDOM,
                                               scope=SCOPE.ENROLL,
                                               user=user_object.login,
                                               realm=user_object.realm,
                                               client=request.remote_addr,
                                               unique=True)

    if len(pin_pols) == 1:
        log.debug("Creating random OTP PIN with length %s" % pin_pols[0])
        request.all_data["pin"] = generate_password(size=int(pin_pols[0]))

        # handle the PIN
        handle_pols = policy_object.get_action_values(
            action=ACTION.PINHANDLING, scope=SCOPE.ENROLL,
            user=user_object.login, realm=user_object.realm,
            client=request.remote_addr)
        # We can have more than one pin handler policy. So we can process the
        #  PIN in several ways!
        for handle_pol in handle_pols:
            log.debug("Handle the random PIN with the class %s" % handle_pol)
            packageName = ".".join(handle_pol.split(".")[:-1])
            className = handle_pol.split(".")[-1:][0]
            mod = __import__(packageName, globals(), locals(), [className])
            pin_handler_class = getattr(mod, className)
            pin_handler = pin_handler_class()
            # Send the PIN
            pin_handler.send(request.all_data["pin"],
                             request.all_data.get("serial", "N/A"),
                             user_object,
                             tokentype=request.all_data.get("type", "hotp"),
                             logged_in_user=g.logged_in_user)

    return True
Пример #16
0
def set_realm(request=None, action=None):
    """
    Pre Policy
    This pre condition gets the current realm and verifies if the realm
    should be rewritten due to the policy definition.
    I takes the realm from the request and - if a policy matches - replaces
    this realm with the realm defined in the policy

    Check ACTION.SETREALM

    This decorator should wrap
        /validate/check

    :param request: The request that is intercepted during the API call
    :type request: Request Object
    :param action: An optional Action
    :type action: basestring
    :returns: Always true. Modified the parameter request
    """
    user_object = get_user_from_param(request.all_data)
    # At the moment a realm parameter with no user parameter returns a user
    # object like "@realm". If this is changed one day, we need to also fetch
    #  the realm
    if user_object:
        realm = user_object.realm
    else:  # pragma: no cover
        realm = request.all_data.get("realm")

    policy_object = g.policy_object
    new_realm = policy_object.get_action_values(ACTION.SETREALM,
                                                scope=SCOPE.AUTHZ,
                                                realm=realm,
                                                client=request.remote_addr)
    # reduce the entries to unique entries
    new_realm = list(set(new_realm))
    if len(new_realm) > 1:
        raise PolicyError("I do not know, to which realm I should set the "
                          "new realm. Conflicting policies exist.")
    elif len(new_realm) == 1:
        # There is one specific realm, which we set in the request
        request.all_data["realm"] = new_realm[0]

    return True
Пример #17
0
def api_key_required(request=None, action=None):
    """
    This is a decorator for check_user_pass and check_serial_pass.
    It checks, if a policy scope=auth, action=apikeyrequired is set.
    If so, the validate request will only performed, if a JWT token is passed
    with role=validate.
    """
    ERROR = "The policy requires an API key to authenticate, " \
            "but no key was passed."
    params = request.all_data
    policy_object = g.policy_object
    user_object = get_user_from_param(params)

    # Get the policies
    action = policy_object.get_policies(action=ACTION.APIKEY,
                                        user=user_object.login,
                                        realm=user_object.realm,
                                        scope=SCOPE.AUTHZ,
                                        client=request.remote_addr,
                                        active=True)
    # Do we have a policy?
    if len(action) > 0:
        # check if we were passed a correct JWT
        # Get the Authorization token from the header
        auth_token = request.headers.get('PI-Authorization', None)
        if not auth_token:
            auth_token = request.headers.get('Authorization', None)
        try:
            r = jwt.decode(auth_token, current_app.secret_key)
            g.logged_in_user = {"username": r.get("username", ""),
                                "realm": r.get("realm", ""),
                                "role": r.get("role", "")}
        except AttributeError:
            raise PolicyError("No valid API key was passed.")

        role = g.logged_in_user.get("role")
        if role != ROLE.VALIDATE:
            raise PolicyError("A correct JWT was passed, but it was no API "
                              "key.")

    # If everything went fine, we call the original function
    return True
Пример #18
0
def mangle(request=None, action=None):
    """
    This pre condition checks if either of the parameters pass, user or realm
    in a validate/check request should be rewritten based on an
    authentication policy with action "mangle".
    See :ref:`policy_mangle` for an example.

    Check ACTION.MANGLE

    This decorator should wrap
        /validate/check

    :param request: The request that is intercepted during the API call
    :type request: Request Object
    :param action: An optional Action
    :type action: basestring
    :returns: Always true. Modified the parameter request
    """
    user_object = get_user_from_param(request.all_data)

    policy_object = g.policy_object
    mangle_pols = policy_object.get_action_values(ACTION.MANGLE,
                                                  scope=SCOPE.AUTH,
                                                  realm=user_object.realm,
                                                  user=user_object.login,
                                                  client=request.remote_addr)
    # reduce the entries to unique entries
    mangle_pols = list(set(mangle_pols))
    # We can have several mangle policies! One for user, one for realm and
    # one for pass. So we do no checking here.
    for mangle_pol_action in mangle_pols:
        # mangle_pol_action looks like this:
        # keyword/search/replace/. Where "keyword" can be "user", "pass" or
        # "realm".
        mangle_key, search, replace, _rest = mangle_pol_action.split("/", 3)
        mangle_value = request.all_data.get(mangle_key)
        if mangle_value:
            log.debug("mangling authentication data: %s" % mangle_key)
            request.all_data[mangle_key] = re.sub(search, replace,
                                                  mangle_value)
    return True
Пример #19
0
def reset_password():
    """
    reset the password with a given recovery code.
    The recovery code was sent by get_recover_code and is bound to a certain
    user.

    :jsonparam recoverycode: The recoverycode sent the the user
    :jsonparam password: The new password of the user

    :return: a json result with a boolean "result": true
    """
    r = False
    user_obj = get_user_from_param(request.all_data, required)
    recoverycode = getParam(request.all_data, "recoverycode", required)
    password = getParam(request.all_data, "password", required)
    if check_recoverycode(user_obj, recoverycode):
        # set password
        r = user_obj.update_user_info({"password": password})
        g.audit_object.log({"success": r,
                            "info": u"{0!s}".format(user_obj)})
    return send_result(r)
Пример #20
0
    def update(self, param):
        """
        This method is called during the initialization process.

        :param param: parameters from the token init
        :type param: dict
        :return: None
        """
        # We should only initialize such a token, when the user is
        # immediately given in the init process, since the token on the
        # smartphone needs to contain a userId.
        user_object = get_user_from_param(param, required)
        self.set_user(user_object)

        ocrasuite = get_from_config("tiqr.ocrasuite") or OCRA_DEFAULT_SUITE
        OCRASuite(ocrasuite)
        self.add_tokeninfo("ocrasuite", ocrasuite)
        TokenClass.update(self, param)
        # We have to set the realms here, since the token DB object does not
        # have an ID before TokenClass.update.
        self.set_realms([user_object.realm])
Пример #21
0
    def update(self, param):
        """
        This method is called during the initialization process.

        :param param: parameters from the token init
        :type param: dict
        :return: None
        """
        user_object = get_user_from_param(param, optional)
        if user_object:
            self.set_user(user_object)

        ocrasuite = getParam(param, "ocrasuite", default=OCRA_DEFAULT_SUITE)
        OCRASuite(ocrasuite)
        self.add_tokeninfo("ocrasuite", ocrasuite)
        TokenClass.update(self, param)

        if user_object:
            # We have to set the realms here, since the token DB object does not
            # have an ID before TokenClass.update.
            self.set_realms([user_object.realm])
Пример #22
0
def check_max_token_realm(request=None, action=None):
    """
    Pre Policy
    This checks the maximum token per realm.
    Check ACTION.MAXTOKENREALM

    This decorator can wrap:
        /token/init  (with a realm and user)
        /token/assign
        /token/tokenrealms

    :param req: The request that is intercepted during the API call
    :type req: Request Object
    :param action: An optional Action
    :type action: basestring
    :return: True otherwise raises an Exception
    """
    ERROR = "The number of tokens in this realm is limited!"
    params = request.all_data
    user_object = get_user_from_param(params)
    if user_object:
        realm = user_object.realm
    else:  # pragma: no cover
        realm = params.get("realm")

    if realm:
        policy_object = g.policy_object
        limit_list = policy_object.get_action_values(ACTION.MAXTOKENREALM,
                                                     scope=SCOPE.ENROLL,
                                                     realm=realm,
                                                     client=request.remote_addr)
        if len(limit_list) > 0:
            # we need to check how many tokens the user already has assigned!
            tokenobject_list = get_tokens(realm=realm)
            already_assigned_tokens = len(tokenobject_list)
            if already_assigned_tokens >= int(max(limit_list)):
                raise PolicyError(ERROR)
    return True
Пример #23
0
    def update(self, param):
        """
        This method is called during the initialization process.
        :param param: parameters from the token init
        :type param: dict
        :return: None
        """
        TokenClass.update(self, param)

        request = getParam(param, "request", optional)
        spkac = getParam(param, "spkac", optional)
        certificate = getParam(param, "certificate", optional)
        generate = getParam(param, "genkey", optional)
        template_name = getParam(param, "template", optional)
        if request or generate:
            # If we do not upload a user certificate, then we need a CA do
            # sign the uploaded request or generated certificate.
            ca = getParam(param, "ca", required)
            self.add_tokeninfo("CA", ca)
            cacon = get_caconnector_object(ca)
        if request:
            # During the initialization process, we need to create the
            # certificate
            x509object = cacon.sign_request(request,
                                            options={
                                                "spkac": spkac,
                                                "template": template_name
                                            })
            certificate = crypto.dump_certificate(crypto.FILETYPE_PEM,
                                                  x509object)
        elif generate:
            # Create the certificate on behalf of another user.
            # Now we need to create the key pair,
            # the request
            # and the certificate
            # We need the user for whom the certificate should be created
            user = get_user_from_param(param, optionalOrRequired=required)

            keysize = getParam(param, "keysize", optional, 2048)
            key = crypto.PKey()
            key.generate_key(crypto.TYPE_RSA, keysize)
            req = crypto.X509Req()
            req.get_subject().CN = user.login
            # Add email to subject
            if user.info.get("email"):
                req.get_subject().emailAddress = user.info.get("email")
            req.get_subject().organizationalUnitName = user.realm
            # TODO: Add Country, Organization, Email
            # req.get_subject().countryName = 'xxx'
            # req.get_subject().stateOrProvinceName = 'xxx'
            # req.get_subject().localityName = 'xxx'
            # req.get_subject().organizationName = 'xxx'
            req.set_pubkey(key)
            req.sign(key, "sha256")
            csr = to_unicode(
                crypto.dump_certificate_request(crypto.FILETYPE_PEM, req))
            x509object = cacon.sign_request(
                csr, options={"template": template_name})
            certificate = crypto.dump_certificate(crypto.FILETYPE_PEM,
                                                  x509object)
            # Save the private key to the encrypted key field of the token
            s = crypto.dump_privatekey(crypto.FILETYPE_PEM, key)
            self.add_tokeninfo("privatekey", s, value_type="password")

        if "pin" in param:
            self.set_pin(param.get("pin"), encrypt=True)

        if certificate:
            self.add_tokeninfo("certificate", certificate)
Пример #24
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.items():
                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)
Пример #25
0
def autoassign(request, response):
    """
    This decorator decorates the function /validate/check.
    Depending on ACTION.AUTOASSIGN it checks if the user has no token and if
    the given OTP-value matches a token in the users realm, that is not yet
    assigned to any user.

    If a token can be found, it assigns the token to the user also taking
    into account ACTION.MAXTOKENUSER and ACTION.MAXTOKENREALM.
    :return:
    """
    content = json.loads(response.data)
    # check, if the authentication was successful, then we need to do nothing
    if content.get("result").get("value") is False:
        user_obj = get_user_from_param(request.all_data)
        password = request.all_data.get("pass", "")
        if user_obj.login and user_obj.realm:
            # If there is no user in the request (because it is a serial
            # authentication request) we immediately bail out
            # check if the policy is defined
            policy_object = g.policy_object

            autoassign_values = policy_object.\
                get_action_values(action=ACTION.AUTOASSIGN,
                                  scope=SCOPE.ENROLL,
                                  user=user_obj.login,
                                  realm=user_obj.realm,
                                  client=g.client_ip)

            if len(autoassign_values) > 1:
                raise PolicyError("Contradicting Autoassign policies.")
            # check if the user has no token
            if autoassign_values and get_tokens(user=user_obj, count=True) == 0:
                # Check is the token would match
                # get all unassigned tokens in the realm and look for
                # a matching OTP:
                realm_tokens = get_tokens(realm=user_obj.realm,
                                          assigned=False)

                for token_obj in realm_tokens:
                    (res, pin, otp) = token_obj.split_pin_pass(password)
                    if res:
                        pin_check = True
                        if autoassign_values[0] == \
                                AUTOASSIGNVALUE.USERSTORE:
                            # If the autoassign policy is set to userstore,
                            # we need to check against the userstore.
                            pin_check = user_obj.check_password(pin)
                        if pin_check:
                            otp_check = token_obj.check_otp(otp)
                            if otp_check >= 0:
                                # we found a matching token
                                #    check MAXTOKENUSER and MAXTOKENREALM
                                check_max_token_user(request=request)
                                check_max_token_realm(request=request)
                                #    Assign token
                                assign_token(serial=token_obj.token.serial,
                                             user=user_obj, pin=pin)
                                # Set the response to true
                                content.get("result")["value"] = True
                                # Set the serial number
                                if not content.get("detail"):
                                    content["detail"] = {}
                                content.get("detail")["serial"] = \
                                    token_obj.token.serial
                                content.get("detail")["otplen"] = \
                                    token_obj.token.otplen
                                content.get("detail")["type"] = token_obj.type
                                content.get("detail")["message"] = "Token " \
                                                                   "assigned to " \
                                                                   "user via " \
                                                                   "Autoassignment"
                                response.data = json.dumps(content)

                                g.audit_object.log(
                                    {"success": True,
                                     "action_info":
                                         "Token assigned via auto assignment",
                                     "serial": token_obj.token.serial})
                                break

    return response
Пример #26
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.get_user_info()
    result_obj = {"auth": auth,
                  "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)
Пример #27
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)
Пример #28
0
    def do(self, action, options=None):
        """
        This method executes the defined action in the given event.

        :param action:
        :param options:
        :return:
        """
        ret = True
        g = options.get("g")
        request = options.get("request")
        try:
            logged_in_user = g.logged_in_user
        except Exception:
            logged_in_user = {}
        user = get_user_from_param(request.all_data)
        serial = request.all_data.get("serial")
        if user.is_empty() and serial:
            # maybe the user is empty, but a serial was passed.
            # Then we determine the user by the serial
            user = get_token_owner(serial)
        if not user.is_empty() and user.login and logged_in_user.get("role") ==\
                ROLE.ADMIN:
            body = options.get("body") or DEFAULT_BODY
            body = body.format(
                admin=logged_in_user.get("username"),
                realm=logged_in_user.get("realm"),
                action=request.path,
                serial=g.audit_object.audit_data.get("serial"),
                url=request.url_root,
                user=user.info.get("givenname")
            )

            if action.lower() == "sendmail":
                emailconfig = options.get("emailconfig")
                useremail = user.info.get("email")
                subject = options.get("subject") or "An action was performed on " \
                                                    "your token."
                try:
                    ret = send_email_identifier(emailconfig,
                                                recipient=useremail,
                                                subject=subject, body=body)
                except Exception as exx:
                    log.error("Failed to send email: {0!s}".format(exx))
                    ret = False
                if ret:
                    log.info("Sent a notification email to user {0}".format(user))
                else:
                    log.warning("Failed to send a notification email to user "
                                "{0}".format(user))

            elif action.lower() == "sendsms":
                smsconfig = options.get("smsconfig")
                userphone = user.info.get("mobile")
                try:
                    ret = send_sms_identifier(smsconfig, userphone, body)
                except Exception as exx:
                    log.error("Failed to send sms: {0!s}".format(exx))
                    ret = False
                if ret:
                    log.info("Sent a notification sms to user {0}".format(user))
                else:
                    log.warning("Failed to send a notification email to user "
                                "{0}".format(user))

        return ret
Пример #29
0
def check_otp_pin(request=None, action=None):
    """
    This policy function checks if the OTP PIN that is about to be set
    follows the OTP PIN policies ACTION.OTPPINMAXLEN, ACTION.OTPPINMINLEN and
    ACTION.OTPPINCONTENTS in the SCOPE.USER. It is used to decorate the API
    functions.

    The pin is investigated in the params as pin = params.get("pin")

    In case the given OTP PIN does not match the requirements an exception is
    raised.
    """
    # This policy is only used for USER roles at the moment:
    if g.logged_in_user.get("role") == "user":
        params = request.all_data
        pin = params.get("otppin", "") or params.get("pin", "")
        serial = params.get("serial")
        if serial:
            # if this is a token, that does not use a pin, we ignore this check
            # And immediately return true
            tokensobject_list = get_tokens(serial=serial)
            if len(tokensobject_list) == 1:
                if tokensobject_list[0].using_pin is False:
                    return True
        policy_object = g.policy_object
        user_object = get_user_from_param(params)
        # get the policies for minimum length, maximum length and PIN contents
        pol_minlen = policy_object.get_action_values(action=ACTION.OTPPINMINLEN,
                                                     scope=SCOPE.USER,
                                                     user=user_object.login,
                                                     realm=user_object.realm,
                                                     client=request.remote_addr,
                                                     unique=True)
        pol_maxlen = policy_object.get_action_values(action=ACTION.OTPPINMAXLEN,
                                                     scope=SCOPE.USER,
                                                     user=user_object.login,
                                                     realm=user_object.realm,
                                                     client=request.remote_addr,
                                                     unique=True)
        pol_contents = policy_object.get_action_values(action=ACTION.OTPPINCONTENTS,
                                                       scope=SCOPE.USER,
                                                       user=user_object.login,
                                                       realm=user_object.realm,
                                                       client=request.remote_addr,
                                                       unique=True)

        if len(pol_minlen) == 1:
            # check the minimum length requirement
            if len(pin) < int(pol_minlen[0]):
                raise PolicyError("The minimum OTP PIN length is %s" %
                                  pol_minlen[0])

        if len(pol_maxlen) == 1:
            # check the maximum length requirement
            if len(pin) > int(pol_maxlen[0]):
                raise PolicyError("The maximum OTP PIN length is %s" %
                                  pol_minlen[0])

        if len(pol_contents) == 1:
            # check the contents requirement
            chars = "[a-zA-Z]"  # c
            digits = "[0-9]"    # n
            special = "[.:,;-_<>+*!/()=?$§%&#~\^]"  # s
            no_others = False
            grouping = False

            if pol_contents[0] == "-":
                no_others = True
                pol_contents = pol_contents[1:]
            elif pol_contents[0] == "+":
                grouping = True
                pol_contents = pol_contents[1:]
            #  TODO implement grouping and substraction
            if "c" in pol_contents[0] and not re.search(chars, pin):
                raise PolicyError("Missing character in PIN: %s" % chars)
            if "n" in pol_contents[0] and not re.search(digits, pin):
                raise PolicyError("Missing character in PIN: %s" % digits)
            if "s" in pol_contents[0] and not re.search(special, pin):
                raise PolicyError("Missing character in PIN: %s" % special)

    return True
Пример #30
0
def twostep_enrollment_activation(request=None, action=None):
    """
    This policy function enables the two-step enrollment process according
    to the configured policies.
    It is used to decorate the ``/token/init`` endpoint.

    If a ``<type>_2step`` policy matches, the ``2stepinit`` parameter is handled according to the policy.
    If no policy matches, the ``2stepinit`` parameter is removed from the request data.
    """
    policy_object = g.policy_object
    user_object = get_user_from_param(request.all_data)
    serial = getParam(request.all_data, "serial", optional)
    token_type = getParam(request.all_data, "type", optional, "hotp")
    token_exists = False
    if serial:
        tokensobject_list = get_tokens(serial=serial)
        if len(tokensobject_list) == 1:
            token_type = tokensobject_list[0].token.tokentype
            token_exists = True
    token_type = token_type.lower()
    role = g.logged_in_user.get("role")
    # Differentiate between an admin enrolling a token for the
    # user and a user self-enrolling a token.
    if role == ROLE.ADMIN:
        scope = SCOPE.ADMIN
        adminrealm = g.logged_in_user.get("realm")
    else:
        scope = SCOPE.USER
        adminrealm = None
    realm = user_object.realm
    # In any case, the policy's user attribute is matched against the
    # currently logged-in user (which may be the admin or the
    # self-enrolling user).
    user = g.logged_in_user.get("username")
    # Tokentypes have separate twostep actions
    action = "{}_2step".format(token_type)
    twostep_enabled_pols = policy_object.get_action_values(action=action,
                                                           scope=scope,
                                                           unique=True,
                                                           user=user,
                                                           realm=realm,
                                                           client=g.client_ip,
                                                           adminrealm=adminrealm)
    if twostep_enabled_pols:
        enabled_setting = twostep_enabled_pols[0]
        if enabled_setting == "allow":
            # The user is allowed to pass 2stepinit=1
            pass
        elif enabled_setting == "force":
            # We force 2stepinit to be 1 (if the token does not exist yet)
            if not token_exists:
                request.all_data["2stepinit"] = 1
        else:
            raise PolicyError("Unknown 2step policy setting: {}".format(enabled_setting))
    else:
        # If no policy matches, the user is not allowed
        # to pass 2stepinit
        # Force two-step initialization to be None
        if "2stepinit" in request.all_data:
            del request.all_data["2stepinit"]
    return True