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)
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)
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], "@@")
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)
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")
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
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")
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)
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
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")
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
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
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()
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)
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")
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()))
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))
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)
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())
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")
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)
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)
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")
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")
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)
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")
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
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")
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)
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)
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
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
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)
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)
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
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")
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
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
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
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