示例#1
0
    def test_16_remove_token(self):
        self.assertRaises(ParameterError, remove_token)

        count1 = get_tokens(count=True)
        tokenobject = init_token({"type": "hotp",
                                  "otpkey": "1234567890123456",
                                  "realm": self.realm1})
        count2 = get_tokens(count=True)
        self.assertTrue(count2 == count1 + 1, count2)
        # check for the token association
        token_id = tokenobject.token.id
        realm_assoc = TokenRealm.query.filter(TokenRealm.token_id == \
            token_id).count()
        self.assertTrue(realm_assoc == 1, realm_assoc)
        # Add a challenge for this token
        challenge = Challenge(tokenobject.get_serial(), transaction_id="918273")
        challenge.save()
        chall_count = Challenge.query.filter(Challenge.serial ==
                                             tokenobject.get_serial()).count()
        self.assertTrue(chall_count == 1, chall_count)

        # remove the token
        count_remove = remove_token(serial=tokenobject.get_serial())
        self.assertTrue(count_remove == 1, count_remove)
        self.assertTrue(get_tokens(count=True) == count1)
        # check for the realm association
        realm_assoc = TokenRealm.query.filter(TokenRealm.token_id == \
            token_id).count()
        self.assertTrue(realm_assoc == 0, realm_assoc)
        # check if the challenge is removed
        chall_count = Challenge.query.filter(Challenge.serial ==
                                             tokenobject.get_serial()).count()
        self.assertTrue(chall_count == 0, chall_count)
示例#2
0
    def test_03_check_user(self):
        # get the original counter
        tokenobject_list = get_tokens(serial=self.serials[0])
        hotp_tokenobject = tokenobject_list[0]
        count_1 = hotp_tokenobject.token.count

        # test successful authentication
        with self.app.test_request_context(
            "/validate/check", method="POST", data={"user": "******", "pass": "******"}
        ):
            res = self.app.full_dispatch_request()
            self.assertTrue(res.status_code == 200, res)
            result = json.loads(res.data).get("result")
            self.assertTrue(result.get("status") is True, result)
            self.assertTrue(result.get("value") is True, result)

        # Check that the counter is increased!
        tokenobject_list = get_tokens(serial=self.serials[0])
        hotp_tokenobject = tokenobject_list[0]
        count_2 = hotp_tokenobject.token.count
        self.assertTrue(
            count_2 > count_1, (hotp_tokenobject.token.serial, hotp_tokenobject.token.count, count_1, count_2)
        )

        # test authentication fails with the same OTP
        with self.app.test_request_context(
            "/validate/check", method="POST", data={"user": "******", "pass": "******"}
        ):
            res = self.app.full_dispatch_request()
            self.assertTrue(res.status_code == 200, res)
            result = json.loads(res.data).get("result")
            self.assertTrue(result.get("status") is True, result)
            self.assertTrue(result.get("value") is False, result)
示例#3
0
    def test_43_encryptpin(self):
        serial = "ENC01"
        # encrypt pin on init
        init_token({"serial": serial,
                    "genkey": 1,
                    "pin": "Hallo",
                    "encryptpin": True})
        tokenobj = get_tokens(serial=serial)[0]
        self.assertEqual(tokenobj.token.pin_hash[0:2], "@@")

        # set a hashed pin
        set_pin(serial, "test", encrypt_pin=False)
        tokenobj = get_tokens(serial=serial)[0]
        self.assertTrue(tokenobj.token.pin_hash[0:2] != "@@")

        # set an encrypted PIN
        set_pin(serial, "test", encrypt_pin=True)
        tokenobj = get_tokens(serial=serial)[0]
        self.assertEqual(tokenobj.token.pin_hash[0:2], "@@")

        # assign the token with a PIN
        assign_token(serial, User(login="******", realm=self.realm1),
                     pin="WellWell", encrypt_pin=True)
        # check if pinhash starts with "@@" to indicate the encryption
        tokenobj = get_tokens(serial=serial)[0]
        self.assertEqual(tokenobj.token.pin_hash[0:2], "@@")
示例#4
0
    def test_03_user_enroll_token(self):
        self.authenticate_selfserive_user()
        with self.app.test_request_context('/token/init',
                                           method='POST', data={"genkey": 1},
                                           headers={'Authorization':
                                                        self.at_user}):
            res = self.app.full_dispatch_request()
            self.assertTrue(res.status_code == 200, res)
            response = json.loads(res.data)
            self.assertTrue(response.get("result").get("value"),
                            response.get("result"))
            serial = response.get("detail").get("serial")
            self.assertTrue("OATH" in serial, serial)

        # Check, who is the owner of the new token!
        tokenobject = get_tokens(serial=serial)[0]
        self.assertTrue(tokenobject.token.user_id == "1004",
                        tokenobject.token.user_id)
        self.assertTrue(tokenobject.token.resolver == "resolver1",
                        tokenobject.token.resolver == "resolver1")

        # user can delete his own token
        with self.app.test_request_context('/token/%s' % serial,
                                           method='DELETE',
                                           headers={'Authorization':
                                                        self.at_user}):
            res = self.app.full_dispatch_request()
            self.assertTrue(res.status_code == 200, res)
            response = json.loads(res.data)
            self.assertTrue(response.get("result").get("value"),
                            response.get("result"))
        # check if there is no token left
        tokenobject_list = get_tokens(serial=serial)
        self.assertTrue(len(tokenobject_list) == 0, len(tokenobject_list))
    def test_03_add_delete_option(self):
        mt = attach_token(self.serial, "luks", hostname="gandalf")
        self.assertEqual(mt.token.serial, self.serial)
        self.assertEqual(mt.token.machine_list[0].machine_id, "192.168.0.1")

        r = add_option(serial=self.serial, application="luks",
                       hostname="gandalf", options={"option1": "value1",
                                                    "option2": "valü2"})
        self.assertEqual(r, 2)

        # The options are accessible via the Token!!!
        tok = get_tokens(serial=self.serial)[0]
        option_list = tok.token.machine_list[0].option_list
        self.assertEqual(len(option_list), 2)
        for option in option_list:
            if option.mt_key == "option1":
                self.assertEqual(option.mt_value, "value1")
            elif option.mt_key == "option2":
                self.assertEqual(option.mt_value, u"valü2")
            else:
                self.fail("Unspecified Option! {0!s}".format(option.mt_key))

        r = delete_option(serial=self.serial, application="luks",
                          hostname="gandalf", key="option1")
        self.assertEqual(r, 1)

        # The options are accessible via the Token!!!
        tok = get_tokens(serial=self.serial)[0]
        option_list = tok.token.machine_list[0].option_list
        self.assertEqual(len(option_list), 1)
示例#6
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
    def test_04_check_max_token_user(self):
        g.logged_in_user = {"username": "******",
                            "role": "admin"}
        builder = EnvironBuilder(method='POST',
                                 data={'serial': "OATH123456"},
                                 headers={})
        env = builder.get_environ()
        # Set the remote address so that we can filter for it
        env["REMOTE_ADDR"] = "10.0.0.1"
        g.client_ip = env["REMOTE_ADDR"]
        req = Request(env)
        req.all_data = {"user": "******",
                        "realm": self.realm1}

        # Set a policy, that allows two tokens per user
        set_policy(name="pol1",
                   scope=SCOPE.ENROLL,
                   action="{0!s}={1!s}".format(ACTION.MAXTOKENUSER, 2))
        g.policy_object = PolicyClass()
        # The user has one token, everything is fine.
        self.setUp_user_realms()
        tokenobject = init_token({"serial": "NEW001", "type": "hotp",
                                  "otpkey": "1234567890123456"},
                                  user=User(login="******",
                                            realm=self.realm1))
        tokenobject_list = get_tokens(user=User(login="******",
                                           realm=self.realm1))
        self.assertTrue(len(tokenobject_list) == 1)
        self.assertTrue(check_max_token_user(req))

        # Now the user gets his second token
        tokenobject = init_token({"serial": "NEW002", "type": "hotp",
                                  "otpkey": "1234567890123456"},
                                  user=User(login="******",
                                            realm=self.realm1))
        tokenobject_list = get_tokens(user=User(login="******",
                                           realm=self.realm1))
        self.assertTrue(len(tokenobject_list) == 2)

        # The user has two tokens. The check that will run in this case,
        # before the user would be assigned the 3rd token, will raise a
        # PolicyError
        self.assertRaises(PolicyError,
                          check_max_token_user, req)


        # The check for a token, that has no username in it, must not
        # succeed. I.e. in the realm new tokens must be enrollable.
        req.all_data = {}
        self.assertTrue(check_max_token_user(req))

        req.all_data = {"realm": self.realm1}
        self.assertTrue(check_max_token_user(req))

        # finally delete policy
        delete_policy("pol1")
        remove_token("NEW001")
        remove_token("NEW002")
示例#8
0
    def check_yubikey_pass(passw):
        """
        if the Token has set a PIN the user must also enter the PIN for
        authentication!

        This checks the output of a yubikey in AES mode without providing
        the serial number.
        The first 12 (of 44) or 16 of 48) characters are the tokenid, which is
        stored in the tokeninfo yubikey.tokenid or the prefix yubikey.prefix.

        :param passw: The password that consist of the static yubikey prefix and
            the otp
        :type passw: string

        :return: True/False and the User-Object of the token owner
        :rtype: dict
        """
        opt = {}
        res = False

        token_list = []

        # strip the yubico OTP and the PIN
        prefix = passw[:-32][-16:]

        from privacyidea.lib.token import get_tokens
        from privacyidea.lib.token import check_token_list

        # See if the prefix matches the serial number
        if prefix[:2] != "vv" and prefix[:2] != "cc":
            try:
                # Keep the backward compatibility
                serialnum = "UBAM" + modhex_decode(prefix)
                for i in range(1, 3):
                    s = "{0!s}_{1!s}".format(serialnum, i)
                    toks = get_tokens(serial=s, tokentype='yubikey')
                    token_list.extend(toks)
            except TypeError as exx:  # pragma: no cover
                log.error("Failed to convert serialnumber: {0!r}".format(exx))

        # Now, we see, if the prefix matches the new version
        if not token_list:
            # If we did not find the token via the serial number, we also
            # search for the yubikey.prefix in the tokeninfo.
            token_candidate_list = get_tokens(tokentype='yubikey',
                                              tokeninfo={"yubikey.prefix":
                                                             prefix})
            token_list.extend(token_candidate_list)

        if not token_list:
            opt['action_detail'] = ("The prefix {0!s} could not be found!".format(
                                    prefix))
            return res, opt

        (res, opt) = check_token_list(token_list, passw, allow_reset_all_tokens=True)
        return res, opt
示例#9
0
    def test_45_check_realm_pass(self):
        # create a bunch of tokens in the realm

        # disabled token
        serial = "inactive"
        init_token({"serial": serial,
                    "otpkey": self.otpkey,
                    "pin": serial}, User("cornelius", self.realm1))
        enable_token(serial, False)

        # not assigned token
        serial = "not_assigned"
        init_token({"serial": serial,
                    "otpkey": self.otpkey,
                    "pin": serial}, tokenrealms=[self.realm1])

        # a normal token
        serial = "assigned"
        init_token({"serial": serial,
                    "otpkey": self.otpkey,
                    "pin": serial}, User("cornelius", self.realm1))

        # check if the tokens were created accordingly
        tokens = get_tokens(realm=self.realm1, tokentype="hotp",
                            assigned=False, serial="not_assigned")
        self.assertEqual(len(tokens), 1)

        tokens = get_tokens(realm=self.realm1, tokentype="hotp",
                            active=False, serial="inactive")
        self.assertEqual(len(tokens), 1)

        tokens = get_tokens(realm=self.realm1, tokentype="hotp",
                            active=True, assigned=True, serial="assigned")
        self.assertEqual(len(tokens), 1)

        # an inactive token does not match
        r = check_realm_pass(self.realm1, "inactive" + "287082")
        self.assertEqual(r[0], False)
        # The remaining tokens are checked, but the pin does not match,
        # so we get "wrong otp pin"
        self.assertEqual(r[1].get("message"), "wrong otp pin")

        # an unassigned token does not match
        r = check_realm_pass(self.realm1, "unassigned" + "287082")
        self.assertEqual(r[0], False)
        # The remaining tokens are checked, but the pin does not match,
        # so we get "wrong otp pin"
        self.assertEqual(r[1].get("message"), "wrong otp pin")

        # a token assigned to a user does match
        r = check_realm_pass(self.realm1, "assigned" + "287082")
        # One token in the realm matches the pin and the OTP value
        self.assertEqual(r[0], True)
        # The remaining tokens are checked, but the pin does not match,
        # so we get "wrong otp pin"
        self.assertEqual(r[1].get("message"), "matching 1 tokens")
示例#10
0
    def test_02_get_tokens(self):
        # get All tokens
        tokenobject_list = get_tokens()
        # Check if these are valid tokentypes
        self.assertTrue(len(tokenobject_list) > 0, tokenobject_list)
        for token_object in tokenobject_list:
            self.assertTrue(token_object.type in get_token_types(),
                            token_object.type)

        # get assigned tokens
        tokenobject_list = get_tokens(assigned=True)
        self.assertTrue(len(tokenobject_list) == 0, tokenobject_list)
        # get unassigned tokens
        tokenobject_list = get_tokens(assigned=False)
        self.assertTrue(len(tokenobject_list) > 0, tokenobject_list)
        # pass the wrong parameter
        # This will ignore the filter!
        tokenobject_list = get_tokens(assigned="True")
        self.assertTrue(len(tokenobject_list) > 0, tokenobject_list)

        # get tokens of type HOTP
        tokenobject_list = get_tokens(tokentype="hotp")
        self.assertTrue(len(tokenobject_list) == 0, tokenobject_list)
        # get tokens of type TOTP
        tokenobject_list = get_tokens(tokentype="totp")
        self.assertTrue(len(tokenobject_list) > 0, tokenobject_list)

        # Search for tokens in realm
        db_token = Token("hotptoken",
                         tokentype="hotp",
                         userid=1000,
                         resolver=self.resolvername1,
                         realm=self.realm1)
        db_token.update_otpkey(self.otpkey)
        db_token.save()
        tokenobject_list = get_tokens(realm=self.realm1)
        self.assertTrue(len(tokenobject_list) == 1, tokenobject_list)
        self.assertTrue(tokenobject_list[0].type == "hotp",
                        tokenobject_list[0].type)

        # get tokens for a given serial number
        tokenobject_list = get_tokens(serial="hotptoken")
        self.assertTrue(len(tokenobject_list) == 1, tokenobject_list)
        # ...but not in an unassigned state!
        tokenobject_list = get_tokens(serial="hotptoken", assigned=False)
        self.assertTrue(len(tokenobject_list) == 0, tokenobject_list)
        # get the tokens for the given user
        tokenobject_list = get_tokens(user=User(login="******",
                                                realm=self.realm1))
        self.assertTrue(len(tokenobject_list) == 1, tokenobject_list)
示例#11
0
    def check_serial_wrapper(*args, **kwds):
        tokenobject_list_from = get_tokens(serial=args[0])
        tokenobject_list_to = get_tokens(serial=args[1])
        if len(tokenobject_list_from) != 1:
            log.error("not a unique token to copy from found")
            raise(TokenAdminError("No unique token to copy from found",
                                   id=1016))
        if len(tokenobject_list_to) != 1:
            log.error("not a unique token to copy to found")
            raise(TokenAdminError("No unique token to copy to found",
                                   id=1017))

        f_result = func(*args, **kwds)
        return f_result
示例#12
0
    def test_05_check_max_token_realm(self):
        g.logged_in_user = {"username": "******",
                            "role": "admin"}
        builder = EnvironBuilder(method='POST',
                                 data={'serial': "OATH123456"},
                                 headers={})
        env = builder.get_environ()
        # Set the remote address so that we can filter for it
        env["REMOTE_ADDR"] = "10.0.0.1"
        g.client_ip = env["REMOTE_ADDR"]
        req = Request(env)
        req.all_data = {"realm": self.realm1}

        # Set a policy, that allows two tokens per realm
        set_policy(name="pol1",
                   scope=SCOPE.ENROLL,
                   action="max_token_per_realm=2",
                   realm=self.realm1)
        g.policy_object = PolicyClass()
        self.setUp_user_realms()
        # Add the first token into the realm
        tokenobject = init_token({"serial": "NEW001", "type": "hotp",
                                  "otpkey": "1234567890123456"})
        set_realms("NEW001", [self.realm1])
        # check the realm, only one token is in it the policy condition will
        # pass
        tokenobject_list = get_tokens(realm=self.realm1)
        self.assertTrue(len(tokenobject_list) == 1)
        self.assertTrue(check_max_token_realm(req))

        # add a second token to the realm
        tokenobject = init_token({"serial": "NEW002", "type": "hotp",
                                  "otpkey": "1234567890123456"})
        set_realms("NEW002", [self.realm1])
        tokenobject_list = get_tokens(realm=self.realm1)
        self.assertTrue(len(tokenobject_list) == 2)

        # request with a user object, not with a realm
        req.all_data = {"user": "******".format(self.realm1)}

        # Now a new policy check will fail, since there are already two
        # tokens in the realm
        self.assertRaises(PolicyError,
                          check_max_token_realm, req)

        # finally delete policy
        delete_policy("pol1")
        remove_token("NEW001")
        remove_token("NEW002")
示例#13
0
def subscription_status():
    """
    Return the status of the subscription

    0: Token count <= 50
    1: Token count > 50, no subscription at all
    2: subscription expired
    3: subscription OK

    :return: subscription state
    """
    token_count = get_tokens(assigned=True, active=True, count=True)
    if token_count <= APPLICATIONS.get("privacyidea", 50):
        return 0

    subscriptions = get_subscription("privacyidea")
    if len(subscriptions) == 0:
        return 1

    try:
        check_subscription("privacyidea")
    except SubscriptionError as exx:
        log.warning(u"{0}".format(exx))
        return 2

    return 3
示例#14
0
def check_subscription(application):
    """
    This checks if the subscription for the given application is valid.
    In case of a failure an Exception is raised.

    :param application: the name of the application to check
    :return: bool
    """
    if application.lower() in APPLICATIONS.keys():
        subscriptions = get_subscription(application) or get_subscription(
            application.lower())
        if len(subscriptions) == 0:
            # get the number of active assigned tokens
            num_tokens = get_tokens(assigned=True, active=True, count=True)
            if num_tokens > APPLICATIONS.get(application.lower()) \
                    and raise_exception_probability():
                raise SubscriptionError(description="No subscription for your client.",
                                        application=application)
        else:
            subscription = subscriptions[0]
            expire_date = subscription.get("date_till")
            if expire_date < datetime.datetime.now():
                # subscription has expired
                if raise_exception_probability(subscription):
                    raise SubscriptionError(description="Your subscription "
                                                        "expired.",
                                            application=application)
            else:
                # subscription is still valid, so check the signature.
                check_signature(subscription)

    return True
示例#15
0
    def test_12_get_token_by_otp(self):
        tokenobject = get_token_by_otp(get_tokens(), otp="755224")
        self.assertTrue(tokenobject.token.serial == "hotptoken", tokenobject)

        serial = get_serial_by_otp(get_tokens(), otp="287082")
        self.assertTrue(serial == "hotptoken", serial)

        # create a second HOTP token, so that we have two tokens,
        # that generate the same OTP value
        db_token = Token("token2",
                         tokentype="hotp")
        db_token.update_otpkey(self.otpkey)
        db_token.save()
        self.assertRaises(TokenAdminError, get_serial_by_otp,
                          get_tokens(), "287922")
        db_token.delete()
示例#16
0
    def test_02_validate_check(self):
        # is the token still assigned?
        tokenbject_list = get_tokens(serial=self.serials[0])
        tokenobject = tokenbject_list[0]
        self.assertTrue(tokenobject.token.user_id == "1000", tokenobject.token.user_id)

        """                  Truncated
           Count    Hexadecimal    Decimal        HOTP
           0        4c93cf18       1284755224     755224
           1        41397eea       1094287082     287082
           2         82fef30        137359152     359152
           3        66ef7655       1726969429     969429
           4        61c5938a       1640338314     338314
           5        33c083d4        868254676     254676
           6        7256c032       1918287922     287922
           7         4e5b397         82162583     162583
           8        2823443f        673399871     399871
           9        2679dc69        645520489     520489
        """
        # test for missing parameter user
        with self.app.test_request_context("/validate/check", method="POST", data={"pass": "******"}):
            res = self.app.full_dispatch_request()
            self.assertTrue(res.status_code == 400, res)

        # test for missing parameter serial
        with self.app.test_request_context("/validate/check", method="POST", data={"pass": "******"}):
            res = self.app.full_dispatch_request()
            self.assertTrue(res.status_code == 400, res)

        # test for missing parameter "pass"
        with self.app.test_request_context("/validate/check", method="POST", data={"serial": "123456"}):
            res = self.app.full_dispatch_request()
            self.assertTrue(res.status_code == 400, res)
示例#17
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
    def test_06_passthru(self):
        user = User("cornelius", realm="r1")
        passw = "test"
        options = {}
        # A user with no tokens will fail to authenticate
        self.assertEqual(get_tokens(user=user, count=True), 0)
        rv = auth_user_passthru(check_user_pass, user, passw, options)
        self.assertFalse(rv[0])
        self.assertEqual(rv[1].get("message"), "The user has no tokens assigned")

        # Now we set a PASSTHRU policy, so that the user may authenticate
        # against his userstore
        set_policy(name="pol1", scope=SCOPE.AUTH, action=ACTION.PASSTHRU)
        g = FakeFlaskG()
        g.policy_object = PolicyClass()
        options = {"g": g}
        rv = auth_user_passthru(check_user_pass, user, passw, options=options)
        self.assertTrue(rv[0])
        self.assertEqual(
            rv[1].get("message"), u"The user authenticated against his userstore " u"according to policy 'pol1'."
        )

        # Now assign a token to the user. If the user has a token and the
        # passthru policy is set, the user must not be able to authenticate
        # with his userstore password.
        init_token({"serial": "PTHRU", "type": "spass", "pin": "Hallo"}, user=user)
        rv = auth_user_passthru(check_user_pass, user, passw, options=options)
        self.assertFalse(rv[0])
        self.assertEqual(rv[1].get("message"), "wrong otp pin")

        remove_token("PTHRU")
        delete_policy("pol1")
示例#19
0
    def get_authentication_item(cls,
                                token_type,
                                serial,
                                challenge=None, options=None,
                                filter_param=None):
        """
        :param token_type: the type of the token. At the moment
                           we only support "HOTP" token. Supporting time
                           based tokens is difficult, since we would have to
                           return a looooong list of OTP values.
                           Supporting "yubikey" token (AES) would be
                           possible, too.
        :param serial:     the serial number of the token.
        :param challenge:  This can contain the password (otp pin + otp
        value) so that we can put the OTP PIN into the hashed response.
        :type challenge: basestring
        :return auth_item: A list of hashed OTP values
        """
        ret = {}
        options = options or {}
        password = challenge
        otppin = ""
        if token_type.lower() == "hotp":
            count = int(options.get("count", 100))
            rounds = int(options.get("rounds", ROUNDS))
            # get the token
            toks = get_tokens(serial=serial)
            if len(toks) == 1:
                token_obj = toks[0]
                if password:
                    _r, otppin, _otpval = token_obj.split_pin_pass(password)
                (res, err, otp_dict) = token_obj.get_multi_otp(count=count)
                otps = otp_dict.get("otp")
                for key in otps.keys():
                    # Return the hash of OTP PIN and OTP values
                    otps[key] = passlib.hash.\
                        pbkdf2_sha512.encrypt(otppin + otps.get(key),
                                                rounds=rounds,
                                                salt_size=10)
                # We do not disable the token, so if all offline OTP values
                # are used, the token can be used the authenticate online again.
                # token_obj.enable(False)
                # increase the counter by the consumed values and
                # also store it in tokeninfo.
                token_obj.inc_otp_counter(counter=count)
                token_obj.add_tokeninfo(key="offline_counter",
                                        value=count)
                ret["response"] = otps
                user_object = token_obj.get_user()
                if user_object:
                    uInfo = user_object.get_user_info()
                    if "username" in uInfo:
                        ret["username"] = uInfo.get("username")

        else:
            log.info("Token %r, type %r is not supported by"
                     "OFFLINE application module" % (serial, token_type))

        return ret
    def test_04_create_token_on_server(self):
        self.setUp_user_realms()
        cwd = os.getcwd()
        # setup ca connector
        r = save_caconnector({"cakey": CAKEY,
                              "cacert": CACERT,
                              "type": "local",
                              "caconnector": "localCA",
                              "openssl.cnf": OPENSSLCNF,
                              "CSRDir": "",
                              "CertificateDir": "",
                              "WorkingDir": cwd + "/" + WORKINGDIR})

        db_token = Token(self.serial3, tokentype="certificate")
        db_token.save()
        token = CertificateTokenClass(db_token)

        # missing user
        self.assertRaises(ParameterError,
                          token.update, {"ca": "localCA","genkey": 1})

        token.update({"ca": "localCA", "genkey": 1,
                      "user": "******"})

        self.assertEqual(token.token.serial, self.serial3)
        self.assertEqual(token.token.tokentype, "certificate")
        self.assertEqual(token.type, "certificate")

        detail = token.get_init_detail()
        certificate = detail.get("certificate")
        # At each testrun, the certificate might get another serial number!
        x509obj = crypto.load_certificate(crypto.FILETYPE_PEM, certificate)
        self.assertEqual("{0!r}".format(x509obj.get_issuer()),
                         "<X509Name object '/C=DE/ST=Hessen"
                         "/O=privacyidea/CN=CA001'>")
        self.assertEqual("{0!r}".format(x509obj.get_subject()),
                         "<X509Name object '/OU=realm1/CN=cornelius/[email protected]'>")

        # Test, if the certificate is also completely stored in the tokeninfo
        # and if we can retrieve it from the tokeninfo
        token = get_tokens(serial=self.serial3)[0]
        certificate = token.get_tokeninfo("certificate")
        x509obj = crypto.load_certificate(crypto.FILETYPE_PEM, certificate)
        self.assertEqual("{0!r}".format(x509obj.get_issuer()),
                         "<X509Name object '/C=DE/ST=Hessen"
                         "/O=privacyidea/CN=CA001'>")
        self.assertEqual("{0!r}".format(x509obj.get_subject()),
                         "<X509Name object '/OU=realm1/CN=cornelius/[email protected]'>")

        privatekey = token.get_tokeninfo("privatekey")
        self.assertTrue(privatekey.startswith("-----BEGIN PRIVATE KEY-----"))

        # check for pkcs12
        self.assertTrue(detail.get("pkcs12"))

        # revoke the token
        r = token.revoke()
        self.assertEqual(r, int_to_hex(x509obj.get_serial_number()))
示例#21
0
    def test_12_copy_token(self):
        self._create_temp_token("FROM001")
        self._create_temp_token("TO001")
        with self.app.test_request_context('/token/assign',
                                            method="POST",
                                            data={"serial": "FROM001",
                                                  "user": "******",
                                                  "realm": self.realm1},
                                            headers={'Authorization': self.at}):
            res = self.app.full_dispatch_request()
            self.assertTrue(res.status_code == 200, res)
            result = json.loads(res.data).get("result")
            value = result.get("value")
            self.assertTrue(value is True, result)

        with self.app.test_request_context('/token/setpin',
                                            method="POST",
                                            data={"serial": "FROM001",
                                                  "otppin": "test"},
                                            headers={'Authorization': self.at}):
            res = self.app.full_dispatch_request()
            self.assertTrue(res.status_code == 200, res)
            result = json.loads(res.data).get("result")
            value = result.get("value")
            self.assertTrue(value == 1, result)

        # copy the PIN
        with self.app.test_request_context('/token/copypin',
                                            method="POST",
                                            data={"from": "FROM001",
                                                  "to": "TO001"},
                                            headers={'Authorization': self.at}):
            res = self.app.full_dispatch_request()
            self.assertTrue(res.status_code == 200, res)
            result = json.loads(res.data).get("result")
            value = result.get("value")
            self.assertTrue(value is True, result)

        # copy the user
        with self.app.test_request_context('/token/copyuser',
                                            method="POST",
                                            data={"from": "FROM001",
                                                  "to": "TO001"},
                                            headers={'Authorization': self.at}):
            res = self.app.full_dispatch_request()
            self.assertTrue(res.status_code == 200, res)
            result = json.loads(res.data).get("result")
            value = result.get("value")
            self.assertTrue(value is True, result)

        # check in the database
        tokenobject_list = get_tokens(serial="TO001")
        token = tokenobject_list[0]
        # check the user
        self.assertTrue(token.token.user_id == "1000", token.token)
        # check if the TO001 has a pin
        self.assertTrue(len(token.token.pin_hash) == 64,
                        len(token.token.pin_hash))
示例#22
0
    def test_06_user_can_assign_token(self):
        self.authenticate_selfserive_user()
        # The foreign token ist not assigned yet, so he can assign it
        with self.app.test_request_context('/token/assign',
                                           data={"serial": self.foreign_serial},
                                           method='POST',
                                           headers={'Authorization':
                                                        self.at_user}):
            res = self.app.full_dispatch_request()
            self.assertTrue(res.status_code == 200, res)
            response = json.loads(res.data)
            self.assertTrue(response.get("result").get("value"),
                            response.get("result"))

        tokenobject = get_tokens(serial=self.foreign_serial)[0]
        self.assertTrue(tokenobject.token.user_id == "1004",
                         tokenobject.token.user_id)

        # User can unassign token
        with self.app.test_request_context('/token/unassign',
                                           data={"serial": self.foreign_serial},
                                           method='POST',
                                           headers={'Authorization':
                                                        self.at_user}):
            res = self.app.full_dispatch_request()
            self.assertTrue(res.status_code == 200, res)
            response = json.loads(res.data)
            self.assertTrue(response.get("result").get("value"),
                            response.get("result"))

        tokenobject = get_tokens(serial=self.foreign_serial)[0]
        self.assertTrue(tokenobject.token.user_id == "",
                         tokenobject.token.user_id)


        # User can not unassign token, which does not belong to him
        with self.app.test_request_context('/token/unassign',
                                           data={"serial": self.foreign_serial},
                                           method='POST',
                                           headers={'Authorization':
                                                        self.at_user}):
            res = self.app.full_dispatch_request()
            self.assertTrue(res.status_code == 400, res)
示例#23
0
    def test_15_init_token(self):
        count = get_tokens(count=True)
        self.assertTrue(count == 4, count)
        tokenobject = init_token({"serial": "NEW001", "type": "hotp",
                                  "otpkey": "1234567890123456"},
                                 user=User(login="******",
                                           realm=self.realm1))
        self.assertTrue(tokenobject.token.tokentype == "hotp",
                        tokenobject.token)
        # Now there is one more token in the database
        count = get_tokens(count=True)
        self.assertTrue(count == 5, count)

        # try to create unknown tokentype
        self.assertRaises(TokenAdminError, init_token, {"otpkey": "1234",
                                                        "type": "never_know"})

        # try to create the same token with another type
        self.assertRaises(TokenAdminError, init_token, {"otpkey": "1234",
                                                        "serial": "NEW001",
                                                        "type": "totp"})

        # update the existing token
        self.assertTrue(tokenobject.token.otplen == 6, tokenobject.token.otplen)
        tokenobject = init_token({"serial": "NEW001", "type": "hotp",
                                  "otpkey": "1234567890123456",
                                  "otplen": 8},
                                 user=User(login="******",
                                           realm=self.realm1))
        self.assertTrue(tokenobject.token.otplen == 8, tokenobject.token.otplen)

        # add additional realms
        tokenobject = init_token({"serial": "NEW002", "type": "hotp",
                                  "otpkey": "1234567890123456",
                                  "realm": self.realm1})
        self.assertTrue(self.realm1 in tokenobject.token.get_realms(),
                        tokenobject.token.get_realms())

        tokenobject = init_token({"serial": "NEW003", "type": "hotp",
                                  "otpkey": "1234567890123456"},
                                 tokenrealms=[self.realm1])
        self.assertTrue(self.realm1 in tokenobject.token.get_realms(),
                        tokenobject.token.get_realms())
示例#24
0
    def test_02_detach_token(self):
        detach_token(self.serial, "luks", hostname="gandalf")

        # look at token, if we do not see the machine
        tok = get_tokens(serial=self.serial)[0]
        machine_list = tok.token.machine_list
        self.assertEqual(len(machine_list), 0)
        # problem detaching token with incomplete machine definition (missing
        #  resolver)
        self.assertRaises(Exception, detach_token, self.serial, "luks",
                          machine_id="192.168.0.1")
示例#25
0
def auth_user_passthru(wrapped_function, user_object, passw, options=None):
    """
    This decorator checks the policy settings of ACTION.PASSTHRU.
    If the authentication against the userstore is not successful,
    the wrapped function is called.

    The wrapped function is usually token.check_user_pass, which takes the
    arguments (user, passw, options={})

    :param wrapped_function:
    :param user_object:
    :param passw:
    :param options: Dict containing values for "g" and "clientip"
    :return: Tuple of True/False and reply-dictionary
    """
    from privacyidea.lib.token import get_tokens
    options = options or {}
    g = options.get("g")
    if get_tokens(user=user_object, count=True) == 0 and g:
        # We only go to passthru, if the user has no tokens!
        clientip = options.get("clientip")
        policy_object = g.policy_object
        pass_thru = policy_object.get_policies(action=ACTION.PASSTHRU,
                                               scope=SCOPE.AUTH,
                                               realm=user_object.realm,
                                               resolver=user_object.resolver,
                                               user=user_object.login,
                                               client=clientip,
                                               active=True,
                                               sort_by_priority=True)
        # Ensure that there are no conflicting action values within the same priority
        policy_object.check_for_conflicts(pass_thru, "passthru")
        if pass_thru:
            pass_thru_action = pass_thru[0].get("action").get("passthru")
            policy_name = pass_thru[0].get("name")
            if pass_thru_action in ["userstore", True]:
                # Now we need to check the userstore password
                if user_object.check_password(passw):
                    g.audit_object.add_policy([p.get("name") for p in pass_thru])
                    return True, {"message": u"against userstore due to '{!s}'".format(
                                      policy_name)}
            else:
                # We are doing RADIUS passthru
                log.info("Forwarding the authentication request to the radius "
                         "server %s" % pass_thru_action)
                radius = get_radius(pass_thru_action)
                r = radius.request(radius.config, user_object.login, passw)
                if r:
                    g.audit_object.add_policy([p.get("name") for p in pass_thru])
                    return True, {'message': u"against RADIUS server {!s} due to '{!s}'".format(
                                      pass_thru_action, policy_name)}

    # If nothing else returned, we return the wrapped function
    return wrapped_function(user_object, passw, options)
示例#26
0
def auth_user_passthru(wrapped_function, user_object, passw, options=None):
    """
    This decorator checks the policy settings of ACTION.PASSTHRU.
    If the authentication against the userstore is not successful,
    the wrapped function is called.

    The wrapped function is usually token.check_user_pass, which takes the
    arguments (user, passw, options={})

    :param wrapped_function:
    :param user_object:
    :param passw:
    :param options: Dict containing values for "g" and "clientip"
    :return: Tuple of True/False and reply-dictionary
    """

    from privacyidea.lib.token import get_tokens
    options = options or {}
    g = options.get("g")
    if g:
        clientip = options.get("clientip")
        policy_object = g.policy_object
        pass_thru = policy_object.get_policies(action=ACTION.PASSTHRU,
                                               scope=SCOPE.AUTH,
                                               realm=user_object.realm,
                                               resolver=user_object.resolver,
                                               user=user_object.login,
                                               client=clientip, active=True)
        if len(pass_thru) > 1:
            raise PolicyError("Contradicting passthru policies.")
        if pass_thru and get_tokens(user=user_object, count=True) == 0:
            # If the user has NO Token, authenticate against the user store
            # Now we need to check the userstore password
            pass_thru_action = pass_thru[0].get("action").get("passthru")
            policy_name = pass_thru[0].get("name")
            if pass_thru_action in ["userstore", True]:
                if user_object.check_password(passw):
                    return True, {"message": "The user authenticated against "
                                             "his userstore according to "
                                             "policy '%s'." % policy_name}
            else:
                # We are doing RADIUS passthru
                log.info("Forwarding the authentication request to the radius "
                         "server %s" % pass_thru_action)
                radius = get_radius(pass_thru_action)
                r = radius.request(radius.config, user_object.login, passw)
                if r:
                    return True, {'message': "The user authenticated against "
                                             "the RADIUS server %s according "
                                             "to policy '%s'." %
                                             (pass_thru_action, policy_name)}

    # If nothing else returned, we return the wrapped function
    return wrapped_function(user_object, passw, options)
示例#27
0
    def test_06_offline_auth(self):
        # Test that a machine definition will return offline hashes
        self.setUp_user_realms()
        serial = "offline01"
        tokenobject = init_token({"serial": serial, "type": "hotp",
                                  "otpkey": "3132333435363738393031"
                                            "323334353637383930",
                                  "pin": "offline",
                                  "user": "******"})

        # Set the Machine and MachineToken
        resolver1 = save_resolver({"name": "reso1",
                                   "type": "hosts",
                                   "filename": HOSTSFILE})

        mt = attach_token(serial, "offline", hostname="gandalf")
        self.assertEqual(mt.token.serial, serial)
        self.assertEqual(mt.token.machine_list[0].machine_id, "192.168.0.1")

        # The request with an OTP value and a PIN of a user, who has not
        # token assigned
        builder = EnvironBuilder(method='POST',
                                 data={},
                                 headers={})
        env = builder.get_environ()
        env["REMOTE_ADDR"] = "192.168.0.1"
        g.client_ip = env["REMOTE_ADDR"]
        req = Request(env)
        req.all_data = {"user": "******",
                        "pass": "******"}

        res = {"jsonrpc": "2.0",
               "result": {"status": True,
                          "value": True},
               "version": "privacyIDEA test",
               "detail": {"serial": serial},
               "id": 1}
        resp = Response(json.dumps(res))

        new_response = offline_info(req, resp)
        jresult = json.loads(new_response.data)
        self.assertTrue(jresult.get("result").get("value"), jresult)
        self.assertEqual(jresult.get("detail").get("serial"), serial)

        # Check the hashvalues in the offline tree
        auth_items = jresult.get("auth_items")
        self.assertEqual(len(auth_items), 1)
        response = auth_items.get("offline")[0].get("response")
        self.assertEqual(len(response), 100)
        # check if the counter of the token was increased to 100
        tokenobject = get_tokens(serial=serial)[0]
        self.assertEqual(tokenobject.token.count, 101)
        delete_policy("pol2")
示例#28
0
    def test_11_load_tokens(self):
        # Load OATH CSV
        with self.app.test_request_context('/token/load/import.oath',
                                            method="POST",
                                            data={"type": "oathcsv",
                                                  "file": (IMPORTFILE,
                                                           "import.oath")},
                                            headers={'Authorization': self.at}):
            res = self.app.full_dispatch_request()
            self.assertTrue(res.status_code == 200, res)
            result = json.loads(res.data).get("result")
            value = result.get("value")
            self.assertTrue(value == 3, result)

        # Load yubico.csv
        with self.app.test_request_context('/token/load/yubico.csv',
                                            method="POST",
                                            data={"type": "yubikeycsv",
                                                  "tokenrealms": self.realm1,
                                                  "file": (YUBICOFILE,
                                                           "yubico.csv")},
                                            headers={'Authorization': self.at}):
            res = self.app.full_dispatch_request()
            self.assertTrue(res.status_code == 200, res)
            result = json.loads(res.data).get("result")
            value = result.get("value")
            self.assertTrue(value == 3, result)

        # check if the token was put into self.realm1
        tokenobject_list = get_tokens(serial="UBOM508327_X")
        self.assertEqual(len(tokenobject_list), 1)
        token = tokenobject_list[0]
        self.assertEqual(token.token.realm_list[0].realm.name, self.realm1)

        # Try to load empty file
        with self.app.test_request_context('/token/load/empty.oath',
                                            method="POST",
                                            data={"type": "oathcsv",
                                                  "file": (IMPORTFILE2,
                                                           "empty.oath")},
                                            headers={'Authorization': self.at}):
            res = self.app.full_dispatch_request()
            self.assertTrue(res.status_code == 400, res)

        # Try to load unknown file type
        with self.app.test_request_context('/token/load/import.oath',
                                            method="POST",
                                            data={"type": "unknown",
                                                  "file": (IMPORTFILE,
                                                           "import.oath")},
                                            headers={'Authorization': self.at}):
            res = self.app.full_dispatch_request()
            self.assertTrue(res.status_code == 400, res)
    def test_16_passthru_assign(self):
        user = User("cornelius", realm="r1")
        passw = "{0!s}test".format(self.valid_otp_values[1])
        options = {}
        # remove all tokens of cornelius
        remove_token(user=user)

        # create unassigned tokens in realm r1
        init_token({"type": "hotp",
                    "otpkey": "00"*20,
                    "serial": "TOKFAIL"}, tokenrealms=["r1"])
        init_token({"type": "hotp",
                    "otpkey": self.otpkey,
                    "serial": "TOKMATCH"}, tokenrealms=["r1"])

        # A user with no tokens will fail to authenticate
        self.assertEqual(get_tokens(user=user, count=True), 0)
        rv = auth_user_passthru(check_user_pass, user, passw, options)
        self.assertFalse(rv[0])
        self.assertEqual(rv[1].get("message"),
                         "The user has no tokens assigned")

        # Now add a PASSTHRU policy to a RADIUS config
        radiusmock.setdata(success=True)
        set_policy(name="pol1",
                   scope=SCOPE.AUTH,
                   action="{0!s}=radiusconfig1".format(ACTION.PASSTHRU))
        r = add_radius("radiusconfig1", "1.2.3.4", "testing123",
                       dictionary=DICT_FILE)
        self.assertTrue(r > 0)
        set_policy(name="pol2",
                   scope=SCOPE.AUTH,
                   action="{0!s}=6:pin:1234".format(ACTION.PASSTHRU_ASSIGN))

        g = FakeFlaskG()
        g.policy_object = PolicyClass()
        g.audit_object = FakeAudit()
        options = {"g": g}

        rv = auth_user_passthru(check_user_pass, user, passw, options=options)
        self.assertTrue(rv[0])
        self.assertTrue(u"against RADIUS server radiusconfig1 due to 'pol1'" in rv[1].get("message"))
        self.assertTrue(u"autoassigned TOKMATCH" in rv[1].get("message"))

        # Check if the token is assigned and can authenticate
        r = check_user_pass(User("cornelius", "r1"), "test{0!s}".format(self.valid_otp_values[2]))
        self.assertTrue(r[0])
        self.assertEqual(r[1].get("serial"), "TOKMATCH")

        remove_token("TOKFAIL")
        remove_token("TOKMATCH")
        delete_policy("pol1")
        delete_policy("pol2")
示例#30
0
    def test_36_check_user_pass(self):
        hotp_tokenobject = get_tokens(serial="hotptoken")[0]
        user = User("shadow", realm=self.realm1)
        r, reply = check_user_pass(user, "passwordasdf")
        self.assertFalse(r)
        self.assertTrue(reply.get("message") == 'The user has no tokens '
                                                'assigned', "%s" % reply)

        user = User("cornelius", realm=self.realm1)
        r, reply = check_user_pass(user, "hotppin868912")
        self.assertTrue(r)
        r, reply = check_user_pass(user, "hotppin736127")
def auth_lastauth(wrapped_function, user_or_serial, passw, options=None):
    """
    This decorator checks the policy settings of ACTION.LASTAUTH
    If the last authentication stored in tokeninfo last_auth_success of a
    token is exceeded, the authentication is denied.

    The wrapped function is usually token.check_user_pass, which takes the
    arguments (user, passw, options={}) OR
    token.check_serial_pass with the arguments (user, passw, options={})

    :param wrapped_function: either check_user_pass or check_serial_pass
    :param user_or_serial: either the User user_or_serial or a serial
    :param passw:
    :param options: Dict containing values for "g" and "clientip"
    :return: Tuple of True/False and reply-dictionary
    """
    # First we call the wrapped function
    res, reply_dict = wrapped_function(user_or_serial, passw, options)

    options = options or {}
    g = options.get("g")
    if g and res:
        clientip = options.get("clientip")
        policy_object = g.policy_object

        # in case of a serial:
        realm = None
        login = None
        serial = user_or_serial
        try:
            # Assume we have a user
            realm = user_or_serial.realm
            resolver = user_or_serial.resolver
            login = user_or_serial.login
            serial = reply_dict.get("serial")
        except Exception:
            # in case of a serial:
            realm = None
            resolver = None
            login = None
            serial = user_or_serial

        # In case of a passthru policy we have no serial in the response
        # So we may only continue, if we have a serial.
        if serial:
            from privacyidea.lib.token import get_tokens
            try:
                token = get_tokens(serial=serial)[0]
            except IndexError:
                # In the special case of a registration token,
                # the token does not exist anymore. So we immediately return
                return res, reply_dict

            last_auth_dict = policy_object.get_action_values(
                action=ACTION.LASTAUTH,
                scope=SCOPE.AUTHZ,
                realm=realm,
                resolver=resolver,
                user=login,
                client=clientip, unique=True)

            if len(last_auth_dict) == 1:
                res = token.check_last_auth_newer(list(last_auth_dict)[0])
                if not res:
                    reply_dict["message"] = "The last successful " \
                                            "authentication was %s. " \
                                            "It is to long ago." % \
                                            token.get_tokeninfo(ACTION.LASTAUTH)
                    g.audit_object.add_policy(next(iter(last_auth_dict.values())))

            # set the last successful authentication, if res still true
            if res:
                token.add_tokeninfo(ACTION.LASTAUTH,
                                    datetime.datetime.now(tzlocal()))

    return res, reply_dict
def config_lost_token(wrapped_function, *args, **kwds):
    """
    Decorator to decorate the lib.token.lost_token function.
    Depending on ACTION.LOSTTOKENVALID, ACTION.LOSTTOKENPWCONTENTS,
    ACTION.LOSTTOKENPWLEN it sets the check_otp parameter, to signal
    how the lostToken should be generated.

    :param wrapped_function: Usually the function lost_token()
    :param args: argument "serial" as the old serial number
    :param kwds: keyword arguments like "validity", "contents", "pw_len"
    kwds["options"] contains the flask g

    :return: calls the original function with the modified "validity",
    "contents" and "pw_len" argument
    """
    # if called in any other way, options may be None
    #  or it might have no element "g".
    from privacyidea.lib.token import get_tokens
    options = kwds.get("options") or {}
    g = options.get("g")
    if g:
        # We need the old serial number, to determine the user - if it exist.
        serial = args[0]
        toks = get_tokens(serial=serial)
        if len(toks) == 1:
            username = None
            realm = None
            resolver = None
            user_object = toks[0].user
            if user_object:
                username = user_object.login
                realm = user_object.realm
                resolver = user_object.resolver
            clientip = options.get("clientip")
            # get the policy
            policy_object = g.policy_object
            contents_dict = policy_object.get_action_values(
                ACTION.LOSTTOKENPWCONTENTS,
                scope=SCOPE.ENROLL,
                realm=realm,
                resolver=resolver,
                user=username,
                client=clientip,
                unique=True,
                audit_data=g.audit_object.audit_data)
            validity_dict = policy_object.get_action_values(
                ACTION.LOSTTOKENVALID,
                scope=SCOPE.ENROLL,
                realm=realm,
                resolver=resolver,
                user=username,
                client=clientip,
                unique=True,
                audit_data=g.audit_object.audit_data)
            pw_len_dict = policy_object.get_action_values(
                ACTION.LOSTTOKENPWLEN,
                scope=SCOPE.ENROLL,
                realm=realm,
                resolver=resolver,
                user=username,
                client=clientip,
                unique=True,
                audit_data=g.audit_object.audit_data)

            if contents_dict:
                kwds["contents"] = list(contents_dict)[0]

            if validity_dict:
                kwds["validity"] = int(list(validity_dict)[0])

            if pw_len_dict:
                kwds["pw_len"] = int(list(pw_len_dict)[0])

    return wrapped_function(*args, **kwds)
示例#33
0
    def test_04_set_options(self):
        serial = "S1"
        with self.app.test_request_context('/machine/tokenoption',
                                           method='POST',
                                           data={
                                               "hostname": "gandalf",
                                               "serial": serial,
                                               "application": "luks",
                                               "partition": "/dev/sdb1"
                                           },
                                           headers={'Authorization': self.at}):
            res = self.app.full_dispatch_request()
            self.assertTrue(res.status_code == 200, res)
            result = json.loads(res.data).get("result")
            self.assertEqual(result["status"], True)
            self.assertTrue(result["value"] >= 1)

        # check if the options were set.
        token_obj = get_tokens(serial=serial)[0]
        self.assertEqual(token_obj.token.machine_list[0].application, "luks")
        self.assertEqual(
            token_obj.token.machine_list[0].option_list[1].mt_value,
            "/dev/sdb1")

        # delete slot!
        with self.app.test_request_context('/machine/tokenoption',
                                           method='POST',
                                           data={
                                               "hostname": "gandalf",
                                               "serial": serial,
                                               "application": "luks",
                                               "slot": ""
                                           },
                                           headers={'Authorization': self.at}):
            res = self.app.full_dispatch_request()
            self.assertTrue(res.status_code == 200, res)
            result = json.loads(res.data).get("result")
            self.assertEqual(result["status"], True)
            self.assertTrue(result["value"] >= 1)

        # check if the options were set.
        token_obj = get_tokens(serial=serial)[0]
        self.assertEqual(token_obj.token.machine_list[0].application, "luks")
        # As we deleted the slot, the partition now is the only entry in the
        # list
        self.assertEqual(
            token_obj.token.machine_list[0].option_list[0].mt_value,
            "/dev/sdb1")

        # Overwrite option
        with self.app.test_request_context('/machine/tokenoption',
                                           method='POST',
                                           data={
                                               "hostname": "gandalf",
                                               "serial": serial,
                                               "application": "luks",
                                               "partition": "/dev/sda1"
                                           },
                                           headers={'Authorization': self.at}):
            res = self.app.full_dispatch_request()
            self.assertTrue(res.status_code == 200, res)
            result = json.loads(res.data).get("result")
            self.assertEqual(result["status"], True)
            self.assertTrue(result["value"] >= 1)

        # check if the options were set.
        token_obj = get_tokens(serial=serial)[0]
        self.assertEqual(token_obj.token.machine_list[0].application, "luks")
        # As we deleted the slot, the partition now is the only entry in the
        # list
        self.assertEqual(
            token_obj.token.machine_list[0].option_list[0].mt_value,
            "/dev/sda1")
示例#34
0
    def do(self, action, options=None):
        """
        This method executes the defined action in the given event.

        :param action:
        :param options: Contains the flask parameters g, request, response
            and the handler_def configuration
        :type options: dict
        :return:
        """
        ret = True
        g = options.get("g")
        request = options.get("request")
        response = options.get("response")
        content = self._get_response_content(response)
        handler_def = options.get("handler_def")
        handler_options = handler_def.get("options", {})
        notify_type = handler_options.get("To", NOTIFY_TYPE.TOKENOWNER)
        reply_to_type = handler_options.get("reply_to")
        try:
            logged_in_user = g.logged_in_user
        except Exception:
            logged_in_user = {}

        tokenowner = self._get_tokenowner(request)
        log.debug(u"Executing event for action {0!r}, user {1!r}, "
                  u"logged_in_user {2!r}".format(action, tokenowner,
                                                 logged_in_user))

        # Determine recipient
        recipient = None
        reply_to = None

        if reply_to_type == NOTIFY_TYPE.NO_REPLY_TO:
            reply_to = ""

        elif reply_to_type == NOTIFY_TYPE.TOKENOWNER and not tokenowner.is_empty():
            reply_to = tokenowner.info.get("email")

        elif reply_to_type == NOTIFY_TYPE.INTERNAL_ADMIN:
            username = handler_options.get("reply_to " + NOTIFY_TYPE.INTERNAL_ADMIN)
            internal_admin = get_db_admin(username)
            reply_to = internal_admin.email if internal_admin else ""

        elif reply_to_type == NOTIFY_TYPE.ADMIN_REALM:
            # Adds all email addresses from a specific admin realm to the reply-to-header
            admin_realm = handler_options.get("reply_to " + NOTIFY_TYPE.ADMIN_REALM)
            attr = is_attribute_at_all()
            ulist = get_user_list({"realm": admin_realm}, custom_attributes=attr)
            # create a list of all user-emails, if the user has an email
            emails = [u.get("email") for u in ulist if u.get("email")]
            reply_to = ",".join(emails)

        elif reply_to_type == NOTIFY_TYPE.LOGGED_IN_USER:
            # Add email address from the logged in user into the reply-to header
            if logged_in_user.get("username") and not logged_in_user.get(
                    "realm"):
                # internal admins have no realm
                internal_admin = get_db_admin(logged_in_user.get("username"))
                if internal_admin:
                    reply_to = internal_admin.email if internal_admin else ""

            else:
                # Try to find the user in the specified realm
                user_obj = User(logged_in_user.get("username"),
                                logged_in_user.get("realm"))
                if user_obj:
                    reply_to = user_obj.info.get("email") if user_obj else ""

        elif reply_to_type == NOTIFY_TYPE.EMAIL:
            email = handler_options.get("reply_to " + NOTIFY_TYPE.EMAIL, "").split(",")
            reply_to = email[0]

        else:
            log.warning("Was not able to determine the email for the reply-to "
                        "header: {0!s}".format(handler_def))

        if notify_type == NOTIFY_TYPE.TOKENOWNER and not tokenowner.is_empty():
            recipient = {
                "givenname": tokenowner.info.get("givenname"),
                "surname": tokenowner.info.get("surname"),
                "username": tokenowner.login,
                "userrealm": tokenowner.realm,
                "email": tokenowner.info.get("email"),
                "mobile": tokenowner.info.get("mobile")
            }
        elif notify_type == NOTIFY_TYPE.INTERNAL_ADMIN:
            username = handler_options.get("To " + NOTIFY_TYPE.INTERNAL_ADMIN)
            internal_admin = get_db_admin(username)
            recipient = {
                "givenname": username,
                "email": internal_admin.email if internal_admin else ""
            }
        elif notify_type == NOTIFY_TYPE.ADMIN_REALM:
            # Send emails to all the users in the specified admin realm
            admin_realm = handler_options.get("To " + NOTIFY_TYPE.ADMIN_REALM)
            attr = is_attribute_at_all()
            ulist = get_user_list({"realm": admin_realm}, custom_attributes=attr)
            # create a list of all user-emails, if the user has an email
            emails = [u.get("email") for u in ulist if u.get("email")]
            recipient = {
                "givenname": "admin of realm {0!s}".format(admin_realm),
                "email": emails
            }
        elif notify_type == NOTIFY_TYPE.LOGGED_IN_USER:
            # Send notification to the logged in user
            if logged_in_user.get("username") and not logged_in_user.get(
                    "realm"):
                # internal admins have no realm
                internal_admin = get_db_admin(logged_in_user.get("username"))
                if internal_admin:
                    recipient = {
                        "givenname": logged_in_user.get("username"),
                        "email": internal_admin.email if internal_admin else ""
                    }
            else:
                # Try to find the user in the specified realm
                user_obj = User(logged_in_user.get("username"),
                                logged_in_user.get("realm"))
                if user_obj:
                    recipient = {
                        "givenname": user_obj.info.get("givenname"),
                        "surname": user_obj.info.get("surname"),
                        "email": user_obj.info.get("email"),
                        "mobile": user_obj.info.get("mobile")
                    }

        elif notify_type == NOTIFY_TYPE.EMAIL:
            email = handler_options.get("To " + NOTIFY_TYPE.EMAIL, "").split(",")
            recipient = {
                "email": email
            }
        else:
            log.warning("Was not able to determine the recipient for the user "
                        "notification: {0!s}".format(handler_def))

        if recipient or action.lower() == "savefile":
            # In case of "savefile" we do not need a recipient
            # Collect all data
            body = handler_options.get("body") or DEFAULT_BODY
            if body.startswith("file:"):  # pragma no cover
                # We read the template from the file.
                filename = body[5:]
                try:
                    with open(filename, "r", encoding="utf-8") as f:
                        body = f.read()
                except Exception as e:
                    log.warning(u"Failed to read email template from file {0!r}: {1!r}".format(filename, e))
                    log.debug(u"{0!s}".format(traceback.format_exc()))

            subject = handler_options.get("subject") or \
                      "An action was performed on your token."
            serial = request.all_data.get("serial") or \
                     content.get("detail", {}).get("serial") or \
                     g.audit_object.audit_data.get("serial")
            registrationcode = content.get("detail", {}).get("registrationcode")
            pin = content.get("detail", {}).get("pin")
            googleurl_value = content.get("detail", {}).get("googleurl",
                                                            {}).get("value")
            googleurl_img = content.get("detail", {}).get("googleurl",
                                                          {}).get("img")
            tokentype = None
            if serial:
                tokens = get_tokens(serial=serial)
                if tokens:
                    tokentype = tokens[0].get_tokentype()
            else:
                token_objects = get_tokens(user=tokenowner)
                serial = ','.join([tok.get_serial() for tok in token_objects])

            tags = create_tag_dict(logged_in_user=logged_in_user,
                                   request=request,
                                   client_ip=g.client_ip,
                                   pin=pin,
                                   googleurl_value=googleurl_value,
                                   recipient=recipient,
                                   tokenowner=tokenowner,
                                   serial=serial,
                                   tokentype=tokentype,
                                   registrationcode=registrationcode,
                                   escape_html=action.lower() == "sendmail" and
                                               handler_options.get("mimetype", "").lower() == "html")

            body = to_unicode(body).format(googleurl_img=googleurl_img, **tags)
            subject = subject.format(**tags)
            # Send notification
            if action.lower() == "sendmail":
                emailconfig = handler_options.get("emailconfig")
                mimetype = handler_options.get("mimetype", "plain")
                useremail = recipient.get("email")
                attach_qrcode = is_true(handler_options.get("attach_qrcode"))

                if attach_qrcode and googleurl_img:
                    # get the image part of the googleurl
                    googleurl = urlopen(googleurl_img)
                    mail_body = MIMEMultipart('related')
                    mail_body.attach(MIMEText(body, mimetype))
                    mail_img = MIMEImage(googleurl.read())
                    mail_img.add_header('Content-ID', '<token_image>')
                    mail_img.add_header('Content-Disposition',
                                        'inline; filename="{0!s}.png"'.format(serial))
                    mail_body.attach(mail_img)
                    body = mail_body
                try:
                    ret = send_email_identifier(emailconfig,
                                                recipient=useremail,
                                                subject=subject, body=body,
                                                reply_to=reply_to,
                                                mimetype=mimetype)
                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(
                        recipient))
                else:
                    log.warning("Failed to send a notification email to user "
                                "{0}".format(recipient))

            elif action.lower() == "savefile":
                spooldir = get_app_config_value("PI_NOTIFICATION_HANDLER_SPOOLDIRECTORY",
                                                "/var/lib/privacyidea/notifications/")
                filename = handler_options.get("filename")
                random = get_alphanum_str(16)
                filename = filename.format(random=random, **tags).lstrip(os.path.sep)
                outfile = os.path.normpath(os.path.join(spooldir, filename))
                if not outfile.startswith(spooldir):
                    log.error(u'Cannot write outside of spooldir {0!s}!'.format(spooldir))
                else:
                    try:
                        with open(outfile, "w") as f:
                            f.write(body)
                    except Exception as err:
                        log.error(u"Failed to write notification file: {0!s}".format(err))

            elif action.lower() == "sendsms":
                smsconfig = handler_options.get("smsconfig")
                userphone = recipient.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(
                        recipient))
                else:
                    log.warning("Failed to send a notification email to user "
                                "{0}".format(recipient))

        return ret
示例#35
0
    def test_16_passthru_assign(self):
        user = User("cornelius", realm="r1")
        passw = "{0!s}test".format(self.valid_otp_values[1])
        options = {}
        # remove all tokens of cornelius
        remove_token(user=user)

        # create unassigned tokens in realm r1
        init_token({
            "type": "hotp",
            "otpkey": "00" * 20,
            "serial": "TOKFAIL"
        },
                   tokenrealms=["r1"])
        init_token(
            {
                "type": "hotp",
                "otpkey": self.otpkey,
                "serial": "TOKMATCH"
            },
            tokenrealms=["r1"])

        # A user with no tokens will fail to authenticate
        self.assertEqual(get_tokens(user=user, count=True), 0)
        rv = auth_user_passthru(check_user_pass, user, passw, options)
        self.assertFalse(rv[0])
        self.assertEqual(rv[1].get("message"),
                         "The user has no tokens assigned")

        # Now add a PASSTHRU policy to a RADIUS config
        radiusmock.setdata(response=radiusmock.AccessAccept)
        set_policy(name="pol1",
                   scope=SCOPE.AUTH,
                   action="{0!s}=radiusconfig1".format(ACTION.PASSTHRU))
        r = add_radius("radiusconfig1",
                       "1.2.3.4",
                       "testing123",
                       dictionary=DICT_FILE)
        self.assertTrue(r > 0)
        set_policy(name="pol2",
                   scope=SCOPE.AUTH,
                   action="{0!s}=6:pin:1234".format(ACTION.PASSTHRU_ASSIGN))

        g = FakeFlaskG()
        g.policy_object = PolicyClass()
        g.audit_object = FakeAudit()
        options = {"g": g}

        rv = auth_user_passthru(check_user_pass, user, passw, options=options)
        self.assertTrue(rv[0])
        self.assertTrue(u"against RADIUS server radiusconfig1 due to 'pol1'" in
                        rv[1].get("message"))
        self.assertTrue(u"autoassigned TOKMATCH" in rv[1].get("message"))

        # Check if the token is assigned and can authenticate
        r = check_user_pass(User("cornelius", "r1"),
                            "test{0!s}".format(self.valid_otp_values[2]))
        self.assertTrue(r[0])
        self.assertEqual(r[1].get("serial"), "TOKMATCH")

        remove_token("TOKFAIL")
        remove_token("TOKMATCH")
        delete_policy("pol1")
        delete_policy("pol2")
示例#36
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``.

    In case of a challenge response authentication a parameter ``exception=1``
    can be passed. This would result in a HTTP 500 Server Error response if
    an error occurred during sending of SMS or Email.

    In case ``/validate/radiuscheck`` is requested, the responses are
    modified as follows: A successful authentication returns an empty HTTP
    204 response. An unsuccessful authentication returns an empty HTTP
    400 response. Error responses are the same responses as for the
    ``/validate/check`` endpoint.

    :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"
            }

    **Example response** for this first part of a challenge response
    authentication:

       .. sourcecode:: http

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

            {
              "detail": {
                "serial": "PIEM0000AB00",
                "type": "email",
                "transaction_id": "12345678901234567890",
                "multi_challenge: [ {"serial": "PIEM0000AB00",
                                     "transaction_id":  "12345678901234567890",
                                     "message": "Please enter otp from your
                                     email"},
                                    {"serial": "PISM12345678",
                                     "transaction_id": "12345678901234567890",
                                     "message": "Please enter otp from your
                                     SMS"}
                ]
              },
              "id": 1,
              "jsonrpc": "2.0",
              "result": {
                "status": true,
                "value": false
              },
              "version": "privacyIDEA unknown"
            }

    In this example two challenges are triggered, one with an email and one
    with an SMS. The application and thus the user has to decide, which one
    to use. They can use either.

    .. note:: All challenge response tokens have the same transaction_id in
       this case.
    """
    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 user:
            # check if the given token belongs to the user
            if not get_tokens(user=user, serial=serial, count=True):
                raise ParameterError(
                    'Given serial does not belong to given user!')
        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": log_used_user(user, details.get("message")),
        "success": result,
        "serial": serial or details.get("serial"),
        "token_type": details.get("type")
    })
    return send_result(result, details=details)
示例#37
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``.

    In case of a challenge response authentication a parameter ``exception=1``
    can be passed. This would result in a HTTP 500 Server Error response if
    an error occurred during sending of SMS or Email.

    In case ``/validate/radiuscheck`` is requested, the responses are
    modified as follows: A successful authentication returns an empty ``HTTP
    204`` response. An unsuccessful authentication returns an empty ``HTTP
    400`` response. Error responses are the same responses as for the
    ``/validate/check`` endpoint.

    :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 type: The tokentype of the tokens, that are taken into account during
        authentication. Requires the *authz* policy :ref:`application_tokentype_policy`.
        It is ignored when a distinct serial is given.
    :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"
            }

    **Example response** for this first part of a challenge response authentication:

        .. sourcecode:: http

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

            {
              "detail": {
                "serial": "PIEM0000AB00",
                "type": "email",
                "transaction_id": "12345678901234567890",
                "multi_challenge: [ {"serial": "PIEM0000AB00",
                                     "transaction_id":  "12345678901234567890",
                                     "message": "Please enter otp from your email",
                                     "client_mode": "interactive"},
                                    {"serial": "PISM12345678",
                                     "transaction_id": "12345678901234567890",
                                     "message": "Please enter otp from your SMS",
                                     "client_mode": "interactive"}
                ]
              },
              "id": 2,
              "jsonrpc": "2.0",
              "result": {
                "status": true,
                "value": false
              },
              "version": "privacyIDEA unknown"
            }

    In this example two challenges are triggered, one with an email and one
    with an SMS. The application and thus the user has to decide, which one
    to use. They can use either.

    The challenges also contain the information of the "client_mode". This
    tells the plugin, whether it should display an input field to ask for the
    OTP value or e.g. to poll for an answered authentication.
    Read more at :ref:`client_modes`.

    .. note:: All challenge response tokens have the same ``transaction_id`` in
       this case.


    **Example response** for a successful authentication with ``/samlcheck``:

       .. 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 = request.User
    serial = getParam(request.all_data, "serial")
    password = getParam(request.all_data, "pass", required)
    otp_only = getParam(request.all_data, "otponly")
    token_type = getParam(request.all_data, "type")
    options = {"g": g,
               "clientip": g.client_ip,
               "user": user}
    # Add all params to the options
    for key, value in request.all_data.items():
        if value and key not in ["g", "clientip", "user"]:
            options[key] = value

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

    if serial:
        if user:
            # check if the given token belongs to the user
            if not get_tokens(user=user, serial=serial, count=True):
                raise ParameterError('Given serial does not belong to given user!')
        if not otp_only:
            success, details = check_serial_pass(serial, password, options=options)
        else:
            success, details = check_otp(serial, password)
        result = success

    else:
        options["token_type"] = token_type
        success, details = check_user_pass(user, password, options=options)
        result = success
        if request.path.endswith("samlcheck"):
            ui = user.info
            result = {"auth": success,
                      "attributes": {}}
            if return_saml_attributes():
                if success or return_saml_attributes_on_fail():
                    # privacyIDEA's own attribute map
                    result["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["attributes"][k] = v

    g.audit_object.log({"info": log_used_user(user, details.get("message")),
                        "success": success,
                        "serial": serial or details.get("serial"),
                        "token_type": details.get("type")})
    return send_result(result, rid=2, details=details)
示例#38
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") == 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
                    and 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=g.client_ip,
            unique=True)
        pol_maxlen = policy_object.get_action_values(
            action=ACTION.OTPPINMAXLEN,
            scope=SCOPE.USER,
            user=user_object.login,
            realm=user_object.realm,
            client=g.client_ip,
            unique=True)
        pol_contents = policy_object.get_action_values(
            action=ACTION.OTPPINCONTENTS,
            scope=SCOPE.USER,
            user=user_object.login,
            realm=user_object.realm,
            client=g.client_ip,
            unique=True)

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

        if len(pol_maxlen) == 1 and len(pin) > int(pol_maxlen[0]):
            # check the maximum length requirement
            raise PolicyError("The maximum OTP PIN length is {0!s}".format(
                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: {0!s}".format(chars))
            if "n" in pol_contents[0] and not re.search(digits, pin):
                raise PolicyError(
                    "Missing character in PIN: {0!s}".format(digits))
            if "s" in pol_contents[0] and not re.search(special, pin):
                raise PolicyError(
                    "Missing character in PIN: {0!s}".format(special))

    return True
示例#39
0
    def check_condition(self, options):
        """
        Check if all conditions are met and if the action should be executed.
        The the conditions are met, we return "True"
        :return: True
        """
        g = options.get("g")
        request = options.get("request")
        response = options.get("response")
        e_handler_def = options.get("handler_def")
        if not e_handler_def:
            # options is the handler definition
            return True
        # conditions can be corresponding to the property conditions
        conditions = e_handler_def.get("conditions")
        content = self._get_response_content(response)
        user = self._get_tokenowner(request)

        serial = request.all_data.get("serial") or content.get(
            "detail", {}).get("serial")
        tokenrealms = []
        tokenresolvers = []
        tokentype = None
        token_obj = None
        if serial:
            # We have determined the serial number from the request.
            token_obj_list = get_tokens(serial=serial)
        elif user:
            # We have to determine the token via the user object. But only if
            #  the user has only one token
            token_obj_list = get_tokens(user=user)
        else:
            token_obj_list = []

        if len(token_obj_list) == 1:
            # There is a token involved, so we determine it's resolvers and realms
            token_obj = token_obj_list[0]
            tokenrealms = token_obj.get_realms()
            tokentype = token_obj.get_tokentype()

            all_realms = get_realms()
            for tokenrealm in tokenrealms:
                resolvers = all_realms.get(tokenrealm, {}).get("resolver", {})
                tokenresolvers.extend([r.get("name") for r in resolvers])
            tokenresolvers = list(set(tokenresolvers))

        if CONDITION.CLIENT_IP in conditions:
            if g and g.client_ip:
                ip_policy = [
                    ip.strip()
                    for ip in conditions.get(CONDITION.CLIENT_IP).split(",")
                ]
                found, excluded = check_ip_in_policy(g.client_ip, ip_policy)
                if not found or excluded:
                    return False

        if CONDITION.REALM in conditions:
            if user.realm != conditions.get(CONDITION.REALM):
                return False

        if CONDITION.RESOLVER in conditions:
            if user.resolver != conditions.get(CONDITION.RESOLVER):
                return False

        if "logged_in_user" in conditions:
            # Determine the role of the user
            try:
                logged_in_user = g.logged_in_user
                user_role = logged_in_user.get("role")
            except Exception:
                # A non-logged-in-user is a User, not an admin
                user_role = ROLE.USER
            if user_role != conditions.get("logged_in_user"):
                return False

        if CONDITION.RESULT_VALUE in conditions:
            condition_value = conditions.get(CONDITION.RESULT_VALUE)
            result_value = content.get("result", {}).get("value")
            if is_true(condition_value) != is_true(result_value):
                return False

        if CONDITION.RESULT_STATUS in conditions:
            condition_value = conditions.get(CONDITION.RESULT_STATUS)
            result_status = content.get("result", {}).get("status")
            if is_true(condition_value) != is_true(result_status):
                return False

        # checking of max-failcounter state of the token
        if "token_locked" in conditions:
            if token_obj:
                locked = token_obj.get_failcount() >= \
                         token_obj.get_max_failcount()
                if (conditions.get("token_locked") in ["True", True]) != \
                      locked:
                    return False
            else:
                # check all tokens of the user, if any token is maxfail
                token_objects = get_tokens(user=user, maxfail=True)
                if not ','.join([tok.get_serial() for tok in token_objects]):
                    return False

        if CONDITION.TOKENREALM in conditions and tokenrealms:
            res = False
            for trealm in tokenrealms:
                if trealm in conditions.get(CONDITION.TOKENREALM).split(","):
                    res = True
                    break
            if not res:
                return False

        if CONDITION.TOKENRESOLVER in conditions and tokenresolvers:
            res = False
            for tres in tokenresolvers:
                if tres in conditions.get(CONDITION.TOKENRESOLVER).split(","):
                    res = True
                    break
            if not res:
                return False

        if "serial" in conditions and serial:
            serial_match = conditions.get("serial")
            if not bool(re.match(serial_match, serial)):
                return False

        if CONDITION.USER_TOKEN_NUMBER in conditions and user:
            num_tokens = get_tokens(user=user, count=True)
            if num_tokens != int(conditions.get(CONDITION.USER_TOKEN_NUMBER)):
                return False

        if CONDITION.DETAIL_ERROR_MESSAGE in conditions:
            message = content.get("detail", {}).get("error", {}).get("message")
            search_exp = conditions.get(CONDITION.DETAIL_ERROR_MESSAGE)
            m = re.search(search_exp, message)
            if not bool(m):
                return False

        if CONDITION.DETAIL_MESSAGE in conditions:
            message = content.get("detail", {}).get("message")
            search_exp = conditions.get(CONDITION.DETAIL_MESSAGE)
            m = re.search(search_exp, message)
            if not bool(m):
                return False

        if CONDITION.COUNTER in conditions:
            # Can be counter==1000
            if not compare_generic_condition(
                    conditions.get(
                        CONDITION.COUNTER), lambda x: counter_read(x) or 0,
                    "Misconfiguration in your counter "
                    "condition: {0!s}"):
                return False

        # Token specific conditions
        if token_obj:
            if CONDITION.TOKENTYPE in conditions:
                if tokentype not in conditions.get(
                        CONDITION.TOKENTYPE).split(","):
                    return False

            if CONDITION.TOKEN_HAS_OWNER in conditions:
                uid = token_obj.get_user_id()
                check = conditions.get(CONDITION.TOKEN_HAS_OWNER)
                if uid and check in ["True", True]:
                    res = True
                elif not uid and check in ["False", False]:
                    res = True
                else:
                    log.debug("Condition token_has_owner for token {0!r} "
                              "not fulfilled.".format(token_obj))
                    return False

            if CONDITION.TOKEN_IS_ORPHANED in conditions:
                orphaned = token_obj.is_orphaned()
                check = conditions.get(CONDITION.TOKEN_IS_ORPHANED)
                if orphaned and check in ["True", True]:
                    res = True
                elif not orphaned and check in ["False", False]:
                    res = True
                else:
                    log.debug(
                        "Condition token_is_orphaned for token {0!r} not "
                        "fulfilled.".format(token_obj))
                    return False

            if CONDITION.TOKEN_VALIDITY_PERIOD in conditions:
                valid = token_obj.check_validity_period()
                if (conditions.get(CONDITION.TOKEN_VALIDITY_PERIOD)
                        in ["True", True]) != valid:
                    return False

            if CONDITION.OTP_COUNTER in conditions:
                cond = conditions.get(CONDITION.OTP_COUNTER)
                if not compare_condition(cond, token_obj.token.count):
                    return False

            if CONDITION.LAST_AUTH in conditions:
                if token_obj.check_last_auth_newer(
                        conditions.get(CONDITION.LAST_AUTH)):
                    return False

            if CONDITION.COUNT_AUTH in conditions:
                count = token_obj.get_count_auth()
                cond = conditions.get(CONDITION.COUNT_AUTH)
                if not compare_condition(cond, count):
                    return False

            if CONDITION.COUNT_AUTH_SUCCESS in conditions:
                count = token_obj.get_count_auth_success()
                cond = conditions.get(CONDITION.COUNT_AUTH_SUCCESS)
                if not compare_condition(cond, count):
                    return False

            if CONDITION.COUNT_AUTH_FAIL in conditions:
                count = token_obj.get_count_auth()
                c_success = token_obj.get_count_auth_success()
                c_fail = count - c_success
                cond = conditions.get(CONDITION.COUNT_AUTH_FAIL)
                if not compare_condition(cond, c_fail):
                    return False

            if CONDITION.FAILCOUNTER in conditions:
                failcount = token_obj.get_failcount()
                cond = conditions.get(CONDITION.FAILCOUNTER)
                if not compare_condition(cond, failcount):
                    return False

            if CONDITION.TOKENINFO in conditions:
                cond = conditions.get(CONDITION.TOKENINFO)
                # replace {now} in condition
                cond, td = parse_time_offset_from_now(cond)
                s_now = (datetime.datetime.now(tzlocal()) +
                         td).strftime(DATE_FORMAT)
                cond = cond.format(now=s_now)
                if not compare_generic_condition(
                        cond, token_obj.get_tokeninfo,
                        "Misconfiguration in your tokeninfo "
                        "condition: {0!s}"):
                    return False

            if CONDITION.ROLLOUT_STATE in conditions:
                cond = conditions.get(CONDITION.ROLLOUT_STATE)
                if not cond == token_obj.token.rollout_state:
                    return False

        return True
示例#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.
    :param type: The tokentype of the tokens, that are taken into account during
        authentication. Requires authz policy application_tokentype.
        Is ignored when a distinct serial is given.

    :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

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

            {
               "detail": {
                    "client_mode": "interactive",
                    "message": "please enter otp: , please enter otp: ",
                    "messages":     [
                        "please enter otp: ",
                        "please enter otp: "
                    ],
                    "multi_challenge": [
                        {
                            "client_mode": "interactive",
                            "message": "please enter otp: ",
                            "serial": "TOTP000026CB",
                            "transaction_id": "11451135673179897001",
                            "type": "totp"
                        },
                        {
                            "client_mode": "interactive",
                            "message": "please enter otp: ",
                            "serial": "OATH0062752C",
                            "transaction_id": "11451135673179897001",
                            "type": "hotp"
                        }
                    ],
                    "serial": "OATH0062752C",
                    "threadid": 140329819764480,
                    "transaction_id": "11451135673179897001",
                    "transaction_ids": [
                        "11451135673179897001",
                        "11451135673179897001"
                    ],
                    "type": "hotp"
               },
               "id": 2,
               "jsonrpc": "2.0",
               "result": {
                   "status": true,
                   "value": 2
               }

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

        .. sourcecode:: http

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

            {
              "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

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

            {
              "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")
    token_type = getParam(request.all_data, "type")
    details = {"messages": [],
               "transaction_ids": []}
    options = {"g": g,
               "clientip": g.client_ip,
               "user": user}
    # Add all params to the options
    for key, value in request.all_data.items():
        if value and key not in ["g", "clientip", "user"]:
            options[key] = value

    token_objs = get_tokens(serial=serial, user=user, active=True, revoked=False, locked=False, tokentype=token_type)
    # Only use the tokens, that are allowed to do challenge response
    chal_resp_tokens = [token_obj for token_obj in token_objs if "challenge" in token_obj.mode]
    create_challenges_from_tokens(chal_resp_tokens, details, options)
    result_obj = len(details.get("multi_challenge"))

    challenge_serials = [challenge_info["serial"] for challenge_info in details["multi_challenge"]]
    g.audit_object.log({
        "user": user.login,
        "resolver": user.resolver,
        "realm": user.realm,
        "success": result_obj > 0,
        "info": log_used_user(user, "triggered {0!s} challenges".format(result_obj)),
        "serial": ",".join(challenge_serials),
    })

    return send_result(result_obj, rid=2, details=details)
示例#41
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,
                            active=True,
                            revoked=False,
                            locked=False)
    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)
示例#42
0
    def do(self, action, options=None):
        """
        This method executes the defined action in the given event.

        :param action:
        :param options: Contains the flask parameters g, request, response
            and the handler_def configuration
        :type options: dict
        :return:
        """
        ret = True
        g = options.get("g")
        request = options.get("request")
        response = options.get("response")
        content = self._get_response_content(response)
        handler_def = options.get("handler_def")
        handler_options = handler_def.get("options", {})
        notify_type = handler_options.get("To", NOTIFY_TYPE.TOKENOWNER)
        try:
            logged_in_user = g.logged_in_user
        except Exception:
            logged_in_user = {}

        tokenowner = self._get_tokenowner(request)
        log.debug(u"Executing event for action {0!r}, user {1!r}, "
                  u"logged_in_user {2!r}".format(action, tokenowner,
                                                 logged_in_user))

        # Determine recipient
        recipient = None

        if notify_type == NOTIFY_TYPE.TOKENOWNER and not tokenowner.is_empty():
            recipient = {
                "givenname": tokenowner.info.get("givenname"),
                "surname": tokenowner.info.get("surname"),
                "username": tokenowner.login,
                "userrealm": tokenowner.realm,
                "email": tokenowner.info.get("email"),
                "mobile": tokenowner.info.get("mobile")
            }
        elif notify_type == NOTIFY_TYPE.INTERNAL_ADMIN:
            username = handler_options.get("To " + NOTIFY_TYPE.INTERNAL_ADMIN)
            internal_admin = get_db_admin(username)
            recipient = {
                "givenname": username,
                "email": internal_admin.email if internal_admin else ""
            }
        elif notify_type == NOTIFY_TYPE.ADMIN_REALM:
            # Send emails to all the users in the specified admin realm
            admin_realm = handler_options.get("To " + NOTIFY_TYPE.ADMIN_REALM)
            ulist = get_user_list({"realm": admin_realm})
            # create a list of all user-emails, if the user has an email
            emails = [u.get("email") for u in ulist if u.get("email")]
            recipient = {
                "givenname": "admin of realm {0!s}".format(admin_realm),
                "email": emails
            }
        elif notify_type == NOTIFY_TYPE.LOGGED_IN_USER:
            # Send notification to the logged in user
            if logged_in_user.get(
                    "username") and not logged_in_user.get("realm"):
                # internal admins have no realm
                internal_admin = get_db_admin(logged_in_user.get("username"))
                if internal_admin:
                    recipient = {
                        "givenname": logged_in_user.get("username"),
                        "email": internal_admin.email if internal_admin else ""
                    }
            else:
                # Try to find the user in the specified realm
                user_obj = User(logged_in_user.get("username"),
                                logged_in_user.get("realm"))
                if user_obj:
                    recipient = {
                        "givenname": user_obj.info.get("givenname"),
                        "surname": user_obj.info.get("surname"),
                        "email": user_obj.info.get("email"),
                        "mobile": user_obj.info.get("mobile")
                    }

        elif notify_type == NOTIFY_TYPE.EMAIL:
            email = handler_options.get("To " + NOTIFY_TYPE.EMAIL,
                                        "").split(",")
            recipient = {"email": email}
        else:
            log.warning("Was not able to determine the recipient for the user "
                        "notification: {0!s}".format(handler_def))

        if recipient:
            # Collect all data
            body = handler_options.get("body") or DEFAULT_BODY
            subject = handler_options.get("subject") or \
                      "An action was performed on your token."
            serial = request.all_data.get("serial") or \
                     content.get("detail", {}).get("serial") or \
                     g.audit_object.audit_data.get("serial")
            registrationcode = content.get("detail",
                                           {}).get("registrationcode")
            googleurl_value = content.get("detail", {}).get("googleurl",
                                                            {}).get("value")
            googleurl_img = content.get("detail", {}).get("googleurl",
                                                          {}).get("img")
            tokentype = None
            if serial:
                tokens = get_tokens(serial=serial)
                if tokens:
                    tokentype = tokens[0].get_tokentype()
            else:
                token_objects = get_tokens(user=tokenowner)
                serial = ','.join([tok.get_serial() for tok in token_objects])

            time = datetime.datetime.now().strftime("%H:%M:%S")
            date = datetime.datetime.now().strftime("%Y-%m-%d")
            tags = dict(admin=logged_in_user.get("username"),
                        realm=logged_in_user.get("realm"),
                        action=request.path,
                        serial=serial,
                        url=request.url_root,
                        user=tokenowner.info.get("givenname"),
                        surname=tokenowner.info.get("surname"),
                        givenname=recipient.get("givenname"),
                        username=tokenowner.login,
                        userrealm=tokenowner.realm,
                        tokentype=tokentype,
                        registrationcode=registrationcode,
                        recipient_givenname=recipient.get("givenname"),
                        recipient_surname=recipient.get("surname"),
                        googleurl_value=googleurl_value,
                        time=time,
                        date=date,
                        client_ip=g.client_ip,
                        ua_browser=request.user_agent.browser,
                        ua_string=request.user_agent.string)
            body = body.format(googleurl_img=googleurl_img, **tags)
            subject = subject.format(**tags)
            # Send notification
            if action.lower() == "sendmail":
                emailconfig = handler_options.get("emailconfig")
                mimetype = handler_options.get("mimetype", "plain")
                useremail = recipient.get("email")
                reply_to = handler_options.get("reply_to")

                try:
                    ret = send_email_identifier(emailconfig,
                                                recipient=useremail,
                                                subject=subject,
                                                body=body,
                                                reply_to=reply_to,
                                                mimetype=mimetype)
                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(
                        recipient))
                else:
                    log.warning("Failed to send a notification email to user "
                                "{0}".format(recipient))

            elif action.lower() == "sendsms":
                smsconfig = handler_options.get("smsconfig")
                userphone = recipient.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(
                        recipient))
                else:
                    log.warning("Failed to send a notification email to user "
                                "{0}".format(recipient))

        return ret
示例#43
0
    def test_13_passthru_priorities(self):
        user = User("cornelius", realm="r1")
        passw = "test"
        options = {}
        # remove all tokens of cornelius
        remove_token(user=user)

        # A user with no tokens will fail to authenticate
        self.assertEqual(get_tokens(user=user, count=True), 0)
        rv = auth_user_passthru(check_user_pass, user, passw, options)
        self.assertFalse(rv[0])
        self.assertEqual(rv[1].get("message"),
                         "The user has no tokens assigned")

        # Now set a PASSTHRU policy to the userstore
        set_policy(name="pol1",
                   scope=SCOPE.AUTH,
                   action="{0!s}=userstore".format(ACTION.PASSTHRU))
        g = FakeFlaskG()
        g.policy_object = PolicyClass()
        g.audit_object = FakeAudit()
        options = {"g": g}
        rv = auth_user_passthru(check_user_pass, user, passw, options=options)
        self.assertTrue(rv[0])
        self.assertEqual(rv[1].get("message"),
                         u"against userstore due to 'pol1'")

        # Now add a PASSTHRU policy to a RADIUS config
        radiusmock.setdata(response=radiusmock.AccessAccept)
        set_policy(name="pol2",
                   scope=SCOPE.AUTH,
                   action="{0!s}=radiusconfig1".format(ACTION.PASSTHRU))
        r = add_radius("radiusconfig1",
                       "1.2.3.4",
                       "testing123",
                       dictionary=DICT_FILE)
        self.assertTrue(r > 0)
        g = FakeFlaskG()
        g.policy_object = PolicyClass()
        g.audit_object = FakeAudit()
        options = {"g": g}

        # They will conflict, because they use the same priority
        with self.assertRaises(PolicyError):
            auth_user_passthru(check_user_pass, user, passw, options=options)

        # Lower pol1 priority
        set_policy(name="pol1", priority=2)

        rv = auth_user_passthru(check_user_pass, user, passw, options=options)
        self.assertTrue(rv[0])
        self.assertEqual(rv[1].get("message"),
                         u"against RADIUS server radiusconfig1 due to 'pol2'")

        # Lower pol2 priority
        set_policy(name="pol2", priority=3)

        rv = auth_user_passthru(check_user_pass, user, passw, options=options)
        self.assertTrue(rv[0])
        self.assertEqual(rv[1].get("message"),
                         u"against userstore due to 'pol1'")

        # Add old style priority
        set_policy(name="pol3", scope=SCOPE.AUTH, action=ACTION.PASSTHRU)

        rv = auth_user_passthru(check_user_pass, user, passw, options=options)
        self.assertTrue(rv[0])
        self.assertEqual(rv[1].get("message"),
                         u"against userstore due to 'pol3'")
        set_policy(name="pol3", priority=2)

        # They will conflict, because they use the same priority
        with self.assertRaises(PolicyError):
            auth_user_passthru(check_user_pass, user, passw, options=options)

        delete_policy("pol3")

        # Now assign a token to the user. If the user has a token and the
        # passthru policy is set, the user must not be able to authenticate
        # with his userstore password.
        init_token({
            "serial": "PTHRU",
            "type": "spass",
            "pin": "Hallo"
        },
                   user=user)
        rv = auth_user_passthru(check_user_pass, user, passw, options=options)
        self.assertFalse(rv[0])
        self.assertEqual(rv[1].get("message"), "wrong otp pin")

        remove_token("PTHRU")
        delete_policy("pol1")
        delete_policy("pol2")
示例#44
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 = response.json
    # check, if the authentication was successful, then we need to do nothing
    if content.get("result").get("value") is False:
        user_obj = request.User
        #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
            autoassign_values = Match.user(g,
                                           scope=SCOPE.ENROLL,
                                           action=ACTION.AUTOASSIGN,
                                           user_object=user_obj).action_values(
                                               unique=True,
                                               write_to_audit_log=False)
            # check if the user has no token
            if autoassign_values and get_tokens(user=user_obj,
                                                count=True) == 0:
                # Check if 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 list(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
                                detail = content.setdefault("detail", {})
                                detail["serial"] = token_obj.token.serial
                                detail["otplen"] = token_obj.token.otplen
                                detail["type"] = token_obj.type
                                detail[
                                    "message"] = "Token assigned to user via Autoassignment"
                                response.set_data(json.dumps(content))

                                g.audit_object.log({
                                    "success":
                                    True,
                                    "info":
                                    "Token assigned via auto assignment",
                                    "serial":
                                    token_obj.token.serial
                                })
                                # The token was assigned by autoassign. We save the first policy name
                                g.audit_object.add_policy(
                                    next(iter(autoassign_values.values())))
                                break

    return response
示例#45
0
    def api_endpoint(request, g):
        """
        This provides a function to be plugged into the API endpoint
        /ttype/<tokentype> which is defined in api/ttype.py
        See :ref:`rest_ttype`.

        :param request: The Flask request
        :param g: The Flask global object g
        :return: Flask Response or text
        """
        params = request.all_data
        action = getParam(params, "action", optional) or \
                 API_ACTIONS.AUTHENTICATION
        if action not in API_ACTIONS.ALLOWED_ACTIONS:
            raise ParameterError("Allowed actions are {0!s}".format(
                API_ACTIONS.ALLOWED_ACTIONS))

        if action == API_ACTIONS.METADATA:
            session = getParam(params, "session", required)
            serial = getParam(params, "serial", required)
            # The user identifier is displayed in the App
            # We need to set the user ID
            tokens = get_tokens(serial=serial)
            if not tokens:  # pragma: no cover
                raise ParameterError(
                    "No token with serial {0!s}".format(serial))
            user_identifier, user_displayname = tokens[0].get_user_displayname(
            )

            service_identifier = get_from_config("tiqr.serviceIdentifier") or\
                                 "org.privacyidea"
            ocrasuite = get_from_config("tiqr.ocrasuite") or OCRA_DEFAULT_SUITE
            service_displayname = get_from_config("tiqr.serviceDisplayname") or \
                                  "privacyIDEA"
            reg_server = get_from_config("tiqr.regServer")
            auth_server = get_from_config("tiqr.authServer") or reg_server
            logo_url = get_from_config("tiqr.logoUrl")

            service = {
                "displayName":
                service_displayname,
                "identifier":
                service_identifier,
                "logoUrl":
                logo_url,
                "infoUrl":
                "https://www.privacyidea.org",
                "authenticationUrl":
                "{0!s}".format(auth_server),
                "ocraSuite":
                ocrasuite,
                "enrollmentUrl":
                "{0!s}?action={1!s}&session={2!s}&serial={3!s}".format(
                    reg_server, API_ACTIONS.ENROLLMENT, session, serial)
            }
            identity = {
                "identifier": user_identifier,
                "displayName": user_displayname
            }

            res = {"service": service, "identity": identity}

            return "json", res

        elif action == API_ACTIONS.ENROLLMENT:
            """
            operation: register
            secret: HEX
            notificationType: GCM
            notificationAddress: ...
            language: de
            session:
            serial:
            """
            res = "Fail"
            serial = getParam(params, "serial", required)
            session = getParam(params, "session", required)
            secret = getParam(params, "secret", required)
            # The secret needs to be stored in the token object.
            # We take the token "serial" and check, if it contains the "session"
            # in the tokeninfo.
            enroll_tokens = get_tokens(serial=serial)
            if len(enroll_tokens) == 1:
                if enroll_tokens[0].get_tokeninfo("session") == session:
                    # save the secret
                    enroll_tokens[0].set_otpkey(secret)
                    # delete the session
                    enroll_tokens[0].del_tokeninfo("session")
                    res = "OK"
                else:
                    raise ParameterError("Invalid Session")

            return "plain", res
        elif action == API_ACTIONS.AUTHENTICATION:
            res = "FAIL"
            userId = getParam(params, "userId", required)
            session = getParam(params, "sessionKey", required)
            passw = getParam(params, "response", required)
            operation = getParam(params, "operation", required)
            res = "INVALID_CHALLENGE"
            # The sessionKey is stored in the db_challenge.transaction_id
            # We need to get the token serial for this sessionKey
            challenges = get_challenges(transaction_id=session)
            # We found exactly one challenge
            if (len(challenges) == 1 and challenges[0].is_valid()
                    and challenges[0].otp_valid is False):
                # Challenge is still valid (time has not passed) and no
                # correct response was given.
                serial = challenges[0].serial
                tokens = get_tokens(serial=serial)
                if len(tokens) == 1:
                    # We found exactly the one token
                    res = "INVALID_RESPONSE"
                    r = tokens[0].verify_response(
                        challenge=challenges[0].challenge, passw=passw)
                    if r > 0:
                        res = "OK"
                        # Mark the challenge as answered successfully.
                        challenges[0].set_otp_status(True)

            cleanup_challenges()

            return "plain", res
示例#46
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=request.remote_addr)

            if len(autoassign_values) > 1:
                raise PolicyError("Contradicting Autoassign policies.")
            if autoassign_values:
                # 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)
                        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")["type"] = token_obj.type
                                    content.get("detail")["message"] = "Token " \
                                                                       "assigned to " \
                                                                       "user via " \
                                                                       "Autoassignment"
                                    response.data = json.dumps(content)
                                    break

    return response
示例#47
0
def get_webui_settings(request, response):
    """
    This decorator is used in the /auth API to add configuration information
    like the logout_time or the policy_template_url to the response.
    :param request: flask request object
    :param response: flask response object
    :return: the response
    """
    content = response.json
    # check, if the authentication was successful, then we need to do nothing
    if content.get("result").get("status") is True:
        role = content.get("result").get("value").get("role")
        loginname = content.get("result").get("value").get("username")
        realm = content.get("result").get("value").get("realm")
        realm = realm or get_default_realm()

        logout_time_pol = Match.realm(g,
                                      scope=SCOPE.WEBUI,
                                      action=ACTION.LOGOUTTIME,
                                      realm=realm).action_values(unique=True)
        timeout_action_pol = Match.realm(
            g, scope=SCOPE.WEBUI, action=ACTION.TIMEOUT_ACTION,
            realm=realm).action_values(unique=True)
        token_page_size_pol = Match.realm(
            g, scope=SCOPE.WEBUI, action=ACTION.TOKENPAGESIZE,
            realm=realm).action_values(unique=True)
        user_page_size_pol = Match.realm(
            g, scope=SCOPE.WEBUI, action=ACTION.USERPAGESIZE,
            realm=realm).action_values(unique=True)
        token_wizard_2nd = (role == ROLE.USER and Match.realm(
            g, scope=SCOPE.WEBUI, action=ACTION.TOKENWIZARD2ND,
            realm=realm).policies())
        token_wizard = False
        dialog_no_token = False
        if role == ROLE.USER:
            user_obj = User(loginname, realm)
            user_token_num = get_tokens(user=user_obj, count=True)
            token_wizard_pol = Match.user(g,
                                          scope=SCOPE.WEBUI,
                                          action=ACTION.TOKENWIZARD,
                                          user_object=user_obj).any()
            # We also need to check, if the user has not tokens assigned.
            # If the user has no tokens, we run the wizard. If the user
            # already has tokens, we do not run the wizard.
            token_wizard = token_wizard_pol and (user_token_num == 0)

            dialog_no_token_pol = Match.user(g,
                                             scope=SCOPE.WEBUI,
                                             action=ACTION.DIALOG_NO_TOKEN,
                                             user_object=user_obj).any()
            dialog_no_token = dialog_no_token_pol and (user_token_num == 0)
        user_details_pol = Match.realm(g,
                                       scope=SCOPE.WEBUI,
                                       action=ACTION.USERDETAILS,
                                       realm=realm).policies()
        search_on_enter = Match.realm(g,
                                      scope=SCOPE.WEBUI,
                                      action=ACTION.SEARCH_ON_ENTER,
                                      realm=realm).policies()
        hide_welcome = Match.realm(g,
                                   scope=SCOPE.WEBUI,
                                   action=ACTION.HIDE_WELCOME,
                                   realm=realm).any()
        hide_buttons = Match.realm(g,
                                   scope=SCOPE.WEBUI,
                                   action=ACTION.HIDE_BUTTONS,
                                   realm=realm).any()
        default_tokentype_pol = Match.realm(
            g, scope=SCOPE.WEBUI, action=ACTION.DEFAULT_TOKENTYPE,
            realm=realm).action_values(unique=True)
        show_seed = Match.realm(g,
                                scope=SCOPE.WEBUI,
                                action=ACTION.SHOW_SEED,
                                realm=realm).any()
        token_page_size = DEFAULT_PAGE_SIZE
        user_page_size = DEFAULT_PAGE_SIZE
        default_tokentype = DEFAULT_TOKENTYPE
        if len(token_page_size_pol) == 1:
            token_page_size = int(list(token_page_size_pol)[0])
        if len(user_page_size_pol) == 1:
            user_page_size = int(list(user_page_size_pol)[0])
        if len(default_tokentype_pol) == 1:
            default_tokentype = list(default_tokentype_pol)[0]

        logout_time = DEFAULT_LOGOUT_TIME
        if len(logout_time_pol) == 1:
            logout_time = int(list(logout_time_pol)[0])

        timeout_action = DEFAULT_TIMEOUT_ACTION
        if len(timeout_action_pol) == 1:
            timeout_action = list(timeout_action_pol)[0]

        policy_template_url_pol = Match.action_only(
            g, scope=SCOPE.WEBUI,
            action=ACTION.POLICYTEMPLATEURL).action_values(unique=True)
        policy_template_url = DEFAULT_POLICY_TEMPLATE_URL
        if len(policy_template_url_pol) == 1:
            policy_template_url = list(policy_template_url_pol)[0]

        content["result"]["value"]["logout_time"] = logout_time
        content["result"]["value"]["token_page_size"] = token_page_size
        content["result"]["value"]["user_page_size"] = user_page_size
        content["result"]["value"]["policy_template_url"] = policy_template_url
        content["result"]["value"]["default_tokentype"] = default_tokentype
        content["result"]["value"]["user_details"] = len(user_details_pol) > 0
        content["result"]["value"]["token_wizard"] = token_wizard
        content["result"]["value"]["token_wizard_2nd"] = token_wizard_2nd
        content["result"]["value"]["dialog_no_token"] = dialog_no_token
        content["result"]["value"]["search_on_enter"] = len(
            search_on_enter) > 0
        content["result"]["value"]["timeout_action"] = timeout_action
        content["result"]["value"]["hide_welcome"] = hide_welcome
        content["result"]["value"]["hide_buttons"] = hide_buttons
        content["result"]["value"]["show_seed"] = show_seed
        content["result"]["value"][
            "subscription_status"] = subscription_status()
        response.set_data(json.dumps(content))
    return response