def test_07_login_mode(self): # a realm: cornelius@r1: PW: test def check_webui_user_userstore(user_obj, password, options=None, superuser_realms=None, check_otp=False): self.assertEqual(check_otp, False) def check_webui_user_privacyidea(user_obj, password, options=None, superuser_realms=None, check_otp=False): self.assertEqual(check_otp, True) user_obj = User("cornelius", "r1") g = FakeFlaskG() P = PolicyClass() g.policy_object = P options = {"g": g} # No policy, the function is called with check_otp=False login_mode(check_webui_user_userstore, user_obj, "", options=options, superuser_realms="", check_otp=False) set_policy(name="pol2", scope=SCOPE.WEBUI, action="%s=%s" % (ACTION.LOGINMODE, LOGINMODE.PRIVACYIDEA)) g = FakeFlaskG() P = PolicyClass() g.policy_object = P options = {"g": g} # Policy is set, the function is called with check_otp=True login_mode(check_webui_user_privacyidea, user_obj, "", options=options, superuser_realms="", check_otp=False)
def test_01_challenge(self): set_policy("chalresp", scope=SCOPE.AUTHZ, action="{0!s}=hotp".format(ACTION.CHALLENGERESPONSE)) token = init_token({"genkey": 1, "serial": "CHAL1", "pin": "pin"}) from privacyidea.lib.token import check_serial_pass r = check_serial_pass(token.token.serial, "pin") # The OTP PIN is correct self.assertEqual(r[0], False) self.assertEqual(r[1].get("message"), "please enter otp: ") transaction_id = r[1].get("transaction_id") chals = get_challenges() self.assertEqual(len(chals), 1) self.assertEqual(chals[0].transaction_id, transaction_id) # get challenge for this serial chals = get_challenges(serial="CHAL1") self.assertEqual(len(chals), 1) self.assertEqual(chals[0].transaction_id, transaction_id) # get challenge for another seial chals = get_challenges(serial="CHAL2") self.assertEqual(len(chals), 0) delete_policy("chalresp")
def test_03_realm_dropdown(self): set_policy("realmdrop", scope=SCOPE.WEBUI, action="{0!s}=Hello World".format(ACTION.REALMDROPDOWN)) with self.app.test_request_context('/', method='GET'): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) self.assertIsNotNone(re.search(r'id="REALMS" value=".*World.*"', res.data), res)
def test_18_auth_timelimit_fail(self): user = User("timelimituser", realm=self.realm2) pin = "spass" # create a token token = init_token({"type": "spass", "pin": pin}, user=user) # set policy for timelimit set_policy(name="pol_time1", scope=SCOPE.AUTHZ, action="%s=2/20s" % ACTION.AUTHMAXFAIL) for i in [1, 2]: with self.app.test_request_context( "/validate/check", method="POST", data={"user": "******", "realm": self.realm2, "pass": "******"}, ): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) result = json.loads(res.data).get("result") self.assertEqual(result.get("value"), False) # Now we do the correct authentication, but # as already two authentications failed, this will fail, too with self.app.test_request_context( "/validate/check", method="POST", data={"user": "******", "realm": self.realm2, "pass": pin} ): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) result = json.loads(res.data).get("result") self.assertEqual(result.get("value"), False) details = json.loads(res.data).get("detail") self.assertEqual(details.get("message"), "Only 2 failed authentications per 0:00:20") delete_policy("pol_time1") remove_token(token.token.serial)
def test_19_emailtext(self): # create a EMAILTEXT policy: p = set_policy(name="emailtext", action="%s=%s" % (EMAILACTION.EMAILTEXT, "'Your <otp>'"), scope=SCOPE.AUTH) self.assertTrue(p > 0) g = FakeFlaskG() P = PolicyClass() g.policy_object = P options = {"g": g} smtpmock.setdata(response={"*****@*****.**": (200, "OK")}) transactionid = "123456098713" db_token = Token.query.filter_by(serial=self.serial1).first() token = EmailTokenClass(db_token) c = token.create_challenge(transactionid, options=options) self.assertTrue(c[0], c) display_message = c[1] self.assertTrue(c[3].get("state"), transactionid) self.assertEqual(display_message, "Enter the OTP from the Email:") # Test AUTOEMAIL p = set_policy(name="autoemail", action=EMAILACTION.EMAILAUTO, scope=SCOPE.AUTH) self.assertTrue(p > 0) g = FakeFlaskG() P = PolicyClass() g.policy_object = P options = {"g": g} r = token.check_otp("287922", options=options) self.assertTrue(r > 0, r)
def test_19_validate_passthru(self): # user passthru, realm: self.realm2, passwd: pthru set_policy(name="pthru", scope=SCOPE.AUTH, action=ACTION.PASSTHRU) # Passthru with GET request with self.app.test_request_context( '/validate/check', method='GET', query_string=urlencode({"user": "******", "realm": self.realm2, "pass": "******"})): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) result = json.loads(res.data).get("result") self.assertEqual(result.get("value"), True) # Passthru with POST Request with self.app.test_request_context('/validate/check', method='POST', data={"user": "******", "realm": self.realm2, "pass": "******"}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) result = json.loads(res.data).get("result") self.assertEqual(result.get("value"), True) delete_policy("pthru")
def test_01a_admin_realms(self): admin1 = {"username": "******", "role": "admin", "realm": "realm1"} admin2 = {"username": "******", "role": "admin", "realm": "realm2"} set_policy(name="pol", scope=SCOPE.ADMIN, action="*", adminrealm="realm1") g.policy_object = PolicyClass() 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" req = Request(env) req.all_data = {} # admin1 is allowed to do everything g.logged_in_user = admin1 r = check_base_action(req, action="delete") self.assertTrue(r) # admin2 is not allowed. g.logged_in_user = admin2 self.assertRaises(PolicyError, check_base_action, req, action="delete") delete_policy("pol")
def test_03_is_password_reset(self): # create resolver and realm param = self.parameters param["resolver"] = "register" param["type"] = "sqlresolver" r = save_resolver(param) self. assertTrue(r > 0) added, failed = set_realm("register", resolvers=["register"]) self.assertTrue(added > 0) self.assertEqual(len(failed), 0) # No user policy at all r = is_password_reset() self.assertEqual(r, True) # create policy set_policy(name="pwrest", scope=SCOPE.USER, action=ACTION.PASSWORDRESET) r = is_password_reset() self.assertEqual(r, True) # create policy that does not allow password_reset set_policy(name="pwrest", scope=SCOPE.USER, action=ACTION.DELETE) r = is_password_reset() self.assertEqual(r, False)
def test_03_check_token_upload(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" req = Request(env) req.all_data = {"filename": "token.xml"} # Set a policy, that does allow the action set_policy(name="pol1", scope=SCOPE.ADMIN, action="enrollTOTP, enrollHOTP, %s" % ACTION.IMPORT, client="10.0.0.0/8") g.policy_object = PolicyClass() # Try to import tokens r = check_token_upload(req) self.assertTrue(r) # The admin can not upload from another IP address # An exception is raised env["REMOTE_ADDR"] = "192.168.0.1" req = Request(env) req.all_data = {"filename": "token.xml"} self.assertRaises(PolicyError, check_token_upload, req) # finally delete policy delete_policy("pol1")
def test_17_auth_timelimit_success(self): user = User("timelimituser", realm=self.realm2) pin = "spass" # create a token token = init_token({"type": "spass", "pin": pin}, user=user) # set policy for timelimit set_policy(name="pol_time1", scope=SCOPE.AUTHZ, action="%s=2/20s" % ACTION.AUTHMAXSUCCESS) for i in [1, 2]: with self.app.test_request_context('/validate/check', method='POST', data={"user": "******", "realm": self.realm2, "pass": pin}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) result = json.loads(res.data).get("result") self.assertEqual(result.get("value"), True) with self.app.test_request_context('/validate/check', method='POST', data={"user": "******", "realm": self.realm2, "pass": pin}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) result = json.loads(res.data).get("result") self.assertEqual(result.get("value"), False) delete_policy("pol_time1") remove_token(token.token.serial)
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 test_14_required_email(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" req = Request(env) # Set a mangle policy to change the username # and only use the last 4 characters of the username set_policy(name="email1", scope=SCOPE.REGISTER, action="%s=/.*@mydomain\..*" % ACTION.REQUIREDEMAIL) g.policy_object = PolicyClass() # request, that matches the policy req.all_data = {"email": "*****@*****.**"} # This emails is allowed r = required_email(req) self.assertTrue(r) # This email is not allowed req.all_data = {"email": "*****@*****.**"} # This emails is allowed self.assertRaises(RegistrationError, required_email, req) delete_policy("email1") g.policy_object = PolicyClass() # Without a policy, this email can register req.all_data = {"email": "*****@*****.**"} # This emails is allowed r = required_email(req) self.assertTrue(r)
def test_03_otppin_for_serial(self): # now create a policy with userstore PW set_policy(name="pol1", scope=SCOPE.AUTH, action="%s=%s" % (ACTION.OTPPIN, ACTIONVALUE.USERSTORE)) g = FakeFlaskG() P = PolicyClass() g.policy_object = P options = {"g": g, "serial": "T001"} # create a token and assign to user cornelius token = init_token({"serial": "T001", "type": "hotp", "genkey": 1}, user=User("cornelius", realm="r1")) self.assertTrue(token) # Wrong password # Not identified by the user but by the token owner r = auth_otppin(self.fake_check_otp, token, "WrongPW", options=options, user=None) self.assertFalse(r) # Correct password from userstore: "test" # Not identified by the user but by the token owner r = auth_otppin(self.fake_check_otp, token, "test", options=options, user=None) self.assertTrue(r) delete_policy("pol1") remove_token("T001")
def test_03_no_detail_on_success(self): builder = EnvironBuilder(method='POST', data={'serial': "HOTP123435"}, headers={}) env = builder.get_environ() # Set the remote address so that we can filter for it env["REMOTE_ADDR"] = "10.0.0.1" req = Request(env) # The response contains the token type SPASS res = {"jsonrpc": "2.0", "result": {"status": True, "value": True}, "version": "privacyIDEA test", "id": 1, "detail": {"message": "matching 1 tokens", "serial": "HOTP123456", "type": "hotp"}} resp = Response(json.dumps(res)) # Set a policy, that does not allow the detail on success set_policy(name="pol2", scope=SCOPE.AUTHZ, action="no_detail_on_success", client="10.0.0.0/8") g.policy_object = PolicyClass() new_response = no_detail_on_success(req, resp) jresult = json.loads(new_response.data) self.assertTrue("detail" not in jresult, jresult) delete_policy("pol2")
def test_15_reset_password(self): builder = EnvironBuilder(method='POST', data={'user': "******", "realm": self.realm1}, headers={}) env = builder.get_environ() # Set the remote address so that we can filter for it env["REMOTE_ADDR"] = "10.0.0.1" req = Request(env) # Set a mangle policy to change the username # and only use the last 4 characters of the username set_policy(name="recover", scope=SCOPE.USER, action="%s" % ACTION.RESYNC) g.policy_object = PolicyClass() req.all_data = {"user": "******", "realm": self.realm1} # There is a user policy without password reset, so an exception is # raised self.assertRaises(PolicyError, check_anonymous_user, req, ACTION.PASSWORDRESET) # The password reset is allowed set_policy(name="recover", scope=SCOPE.USER, action="%s" % ACTION.PASSWORDRESET) g.policy_object = PolicyClass() r = check_anonymous_user(req, ACTION.PASSWORDRESET) self.assertEqual(r, True)
def test_05_user_has_no_tokens(self): user = User("cornelius", realm="r1") passw = "test" options = {} # A user with no tokens will fail to authenticate rv = auth_user_has_no_token(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 policy, that a non existing user will authenticate set_policy(name="pol1", scope=SCOPE.AUTH, action=ACTION.PASSNOTOKEN) g = FakeFlaskG() g.policy_object = PolicyClass() g.audit_object = FakeAudit() options = {"g": g} rv = auth_user_has_no_token(check_user_pass, user, passw, options=options) self.assertTrue(rv[0]) self.assertEqual(rv[1].get("message"), u"user has no token, accepted due to " u"'pol1'") delete_policy("pol1")
def test_04a_user_does_not_exist_without_resolver(self): user = User("MisterX", realm=self.realm1) passw = "somePW" # Now we set a policy, that a non existing user will authenticate set_policy(name="pol1", scope=SCOPE.AUTH, action="{0}, {1}, {2}, {3}=none".format( ACTION.RESETALLTOKENS, ACTION.PASSNOUSER, ACTION.PASSNOTOKEN, ACTION.OTPPIN ), realm=self.realm1) g = FakeFlaskG() g.policy_object = PolicyClass() g.audit_object = FakeAudit() options = {"g": g} rv = auth_user_does_not_exist(check_user_pass, user, passw, options=options) self.assertTrue(rv[0]) self.assertEqual(rv[1].get("message"), u"user does not exist, accepted due " u"to 'pol1'") delete_policy("pol1")
def test_16_init_token_defaults(self): g.logged_in_user = {"username": "******", "role": "user"} builder = EnvironBuilder(method='POST', data={'type': "totp", "genkey": "1"}, 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) # Set a policy that defines the default totp settings set_policy(name="pol1", scope=SCOPE.USER, action="totp_otplen=8,totp_hashlib=sha256,totp_timestep=60") g.policy_object = PolicyClass() # request, that matches the policy req.all_data = { "type": "totp", "genkey": "1"} init_token_defaults(req) # Check, if the token defaults were added self.assertEqual(req.all_data.get("totp.hashlib"), "sha256") self.assertEqual(req.all_data.get("otplen"), "8") self.assertEqual(req.all_data.get("timeStep"), "60") # finally delete policy delete_policy("pol1")
def test_02_userstore_password(self): # create a realm, where cornelius has a password test rid = save_resolver({"resolver": "myreso", "type": "passwdresolver", "fileName": PWFILE2}) self.assertTrue(rid > 0, rid) (added, failed) = set_realm("r1", ["myreso"]) self.assertTrue(len(failed) == 0) self.assertTrue(len(added) == 1) # now create a policy with userstore PW set_policy(name="pol1", scope=SCOPE.AUTH, action="%s=%s" % (ACTION.OTPPIN, ACTIONVALUE.USERSTORE)) g = FakeFlaskG() P = PolicyClass() g.policy_object = P options = {"g": g} # Wrong password r = auth_otppin(self.fake_check_otp, None, "WrongPW", options=options, user=User("cornelius", realm="r1")) self.assertFalse(r) # Correct password from userstore: "test" r = auth_otppin(self.fake_check_otp, None, "test", options=options, user=User("cornelius", realm="r1")) self.assertTrue(r) delete_policy("pol1")
def test_13_remote_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" env["REMOTE_USER"] = "******" req = Request(env) # A user, for whom the login via REMOTE_USER is allowed. set_policy(name="ruser", scope=SCOPE.WEBUI, action="%s=%s" % (ACTION.REMOTE_USER, REMOTE_USER.ACTIVE)) g.policy_object = PolicyClass() r = is_remote_user_allowed(req) self.assertEqual(r, [REMOTE_USER.ACTIVE]) # Login for the REMOTE_USER is not allowed. # Only allowed for user "super", but REMOTE_USER=admin set_policy(name="ruser", scope=SCOPE.WEBUI, action="%s=%s" % (ACTION.REMOTE_USER, REMOTE_USER.ACTIVE), user="******") g.policy_object = PolicyClass() r = is_remote_user_allowed(req) self.assertEqual(r, []) delete_policy("ruser")
def test_09_challenge_response_allowed(self): user = User("cornelius", realm="r1") pin = "test" g = FakeFlaskG() g.policy_object = PolicyClass() options = {"g": g} token = init_token({"type": "hotp", "otpkey": "1234", "pin": pin}, user=user) # With no policy, it will be no chal resp rv = token.is_challenge_request(pin, user=user, options=options) self.assertEqual(rv, False) # Now we set a policy with several tokentypes set_policy(name="pol_chal_resp_1", scope=SCOPE.AUTH, action="%s=hotp tiqr totp" % ACTION.CHALLENGERESPONSE) set_policy(name="pol_chal_resp_2", scope=SCOPE.AUTH, action="%s=hotp motp" % ACTION.CHALLENGERESPONSE) g = FakeFlaskG() g.policy_object = PolicyClass() options = {"g": g} rv = token.is_challenge_request(pin, user=user, options=options) self.assertEqual(rv, True) delete_policy("pol_chal_resp_1")
def test_08_encrypt_pin(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" req = Request(env) # Set a policy that defines the PIN to be encrypted set_policy(name="pol1", scope=SCOPE.ENROLL, action=ACTION.ENCRYPTPIN) g.policy_object = PolicyClass() # request, that matches the policy req.all_data = { "user": "******", "realm": "home"} encrypt_pin(req) # Check, if the tokenlabel was added self.assertEqual(req.all_data.get("encryptpin"), "True") # finally delete policy delete_policy("pol1")
def test_01_otppin(self): my_user = User("cornelius", realm="r1") set_policy(name="pol1", scope=SCOPE.AUTH, action="%s=%s" % (ACTION.OTPPIN, ACTIONVALUE.NONE)) g = FakeFlaskG() P = PolicyClass() g.policy_object = P options = {"g": g} # NONE with empty PIN -> success r = auth_otppin(self.fake_check_otp, None, "", options=options, user=my_user) self.assertTrue(r) # NONE with empty PIN -> success, even if the authentication is done # for a serial and not a user, since the policy holds for all realms token = init_token({"type": "HOTP", "otpkey": "1234"}) r = auth_otppin(self.fake_check_otp, token, "", options=options, user=None) self.assertTrue(r) # NONE with some pin -> fail r = auth_otppin(self.fake_check_otp, None, "some pin", options=options, user=my_user) self.assertFalse(r) delete_policy("pol1") set_policy(name="pol1", scope=SCOPE.AUTH, action="%s=%s" % (ACTION.OTPPIN, ACTIONVALUE.TOKENPIN)) g = FakeFlaskG() P = PolicyClass() g.policy_object = P options = {"g": g} r = auth_otppin(self.fake_check_otp, None, "FAKE", options=options, user=my_user) self.assertTrue(r) r = auth_otppin(self.fake_check_otp, None, "Wrong Pin", options=options, user=my_user) self.assertFalse(r) delete_policy("pol1")
def test_07_set_random_pin(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" req = Request(env) # Set a policy that defines the tokenlabel set_policy(name="pol1", scope=SCOPE.ENROLL, action="%s=%s" % (ACTION.OTPPINRANDOM, "12")) set_policy(name="pinhandling", scope=SCOPE.ENROLL, action="%s=privacyidea.lib.pinhandling.base.PinHandler" % ACTION.PINHANDLING) g.policy_object = PolicyClass() # request, that matches the policy req.all_data = { "user": "******", "realm": "home"} init_random_pin(req) # Check, if the tokenlabel was added self.assertEqual(len(req.all_data.get("pin")), 12) # finally delete policy delete_policy("pol1") delete_policy("pinhandling")
def test_08_config_lost_token_policy(self): def func1(serial, validity=10, contents="Ccns", pw_len=16, options=None): self.assertEqual(validity, 10) self.assertEqual(contents, "Ccns") self.assertEqual(pw_len, 16) def func2(serial, validity=10, contents="Ccns", pw_len=16, options=None): self.assertEqual(validity, 5) self.assertEqual(contents, "C") self.assertEqual(pw_len, 3) init_token({"serial": "LOST001", "type": "hotp", "genkey": 1}, user=User("cornelius", realm="r1")) g = FakeFlaskG() P = PolicyClass() g.policy_object = P options = {"g": g} # No policy, the function is called with default values config_lost_token(func1, "LOST001", options=options) set_policy( name="lost_pol2", scope=SCOPE.ENROLL, action="%s=%s, %s=%s," "%s=%s" % (ACTION.LOSTTOKENPWCONTENTS, "C", ACTION.LOSTTOKENVALID, 5, ACTION.LOSTTOKENPWLEN, 3), ) g = FakeFlaskG() P = PolicyClass() g.policy_object = P options = {"g": g} # Policy is set, the function is called with check_otp=True config_lost_token(func2, "LOST001", options=options)
def test_06_set_tokenlabel(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" req = Request(env) # Set a policy that defines the tokenlabel set_policy(name="pol1", scope=SCOPE.ENROLL, action="%s=%s" % (ACTION.TOKENLABEL, "<u>@<r>")) set_policy(name="pol2", scope=SCOPE.ENROLL, action="%s=%s" % (ACTION.TOKENISSUER, "myPI")) g.policy_object = PolicyClass() # request, that matches the policy req.all_data = { "user": "******", "realm": "home"} init_tokenlabel(req) # Check, if the tokenlabel was added self.assertEqual(req.all_data.get("tokenlabel"), "<u>@<r>") # Check, if the tokenissuer was added self.assertEqual(req.all_data.get("tokenissuer"), "myPI") # finally delete policy delete_policy("pol1") delete_policy("pol2")
def test_01_create_simple_policy(self): p = set_policy(name="pol1", action="read", scope="system") self.assertTrue(p > 0) p = set_policy(name="pol2", action="tokentype=HOTP", scope=SCOPE.AUTHZ) self.assertTrue(p > 0) p = set_policy(name="pol3", action="serial=OATH", scope=SCOPE.AUTHZ) self.assertTrue(p > 0) p = set_policy(name="pol4", action="enroll, init, disable , enable", scope="admin") self.assertTrue(p > 0) P = PolicyClass() policies = P.get_policies(name="pol3") # only one policy found self.assertTrue(len(policies) == 1, len(policies)) policies = P.get_policies(scope=SCOPE.AUTHZ) self.assertTrue(len(policies) == 2, len(policies)) policies = P.get_policies(scope=SCOPE.AUTHZ, action="tokentype") self.assertTrue(len(policies) == 1, len(policies)) policies = P.get_policies(scope="admin", action="disable") self.assertTrue(len(policies) == 1, len(policies)) self.assertTrue(policies[0].get("name") == "pol4")
def test_22_validate_locked(self): # test a user with two tokens # One token is locked/revoked. # But the user must be able to login with the 2nd token # user lockeduser, realm: self.realm2 # enroll two tokens user = "******" set_policy(name="locked", scope=SCOPE.AUTH, action="{0!s}={1!s}".format(ACTION.OTPPIN, "tokenpin")) r = init_token({"type": "spass", "serial": "spass1l", "pin": "locked"}, user=User(user, self.realm2)) r = init_token({"type": "spass", "serial": "spass2l", "pin": "locked"}, user=User(user, self.realm2)) # disable first token r = revoke_token("spass1l") self.assertEqual(r, True) # Check that the user still can authenticate with the 2nd token with self.app.test_request_context('/validate/check', method='POST', data={"user": user, "realm": self.realm2, "pass": "******"}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) result = json.loads(res.data).get("result") self.assertEqual(result.get("value"), True) remove_token("spass1l") remove_token("spass2l") delete_policy("locked")
def test_19_emailtext(self): # create a EMAILTEXT policy: p = set_policy(name="emailtext", action="{0!s}={1!s}".format(EMAILACTION.EMAILTEXT, "'Your <otp>'"), scope=SCOPE.AUTH) self.assertTrue(p > 0) g = FakeFlaskG() P = PolicyClass() g.policy_object = P options = {"g": g} smtpmock.setdata(response={"*****@*****.**": (200, "OK")}) transactionid = "123456098713" db_token = Token.query.filter_by(serial=self.serial1).first() token = EmailTokenClass(db_token) c = token.create_challenge(transactionid, options=options) self.assertTrue(c[0], c) display_message = c[1] self.assertTrue(c[3].get("state"), transactionid) self.assertEqual(display_message, "Enter the OTP from the Email:") _, mimetype = token._get_email_text_or_subject(options, EMAILACTION.EMAILTEXT) self.assertEqual(mimetype, "plain") # Test AUTOEMAIL p = set_policy(name="autoemail", action=EMAILACTION.EMAILAUTO, scope=SCOPE.AUTH) self.assertTrue(p > 0) g = FakeFlaskG() P = PolicyClass() g.policy_object = P options = {"g": g} r = token.check_otp("287922", options=options) self.assertTrue(r > 0, r) # create a EMAILTEXT policy with template p = set_policy(name="emailtext", action="{0!s}=file:{1!s}".format(EMAILACTION.EMAILTEXT, TEMPLATE_FILE), scope=SCOPE.AUTH) self.assertTrue(p > 0) g = FakeFlaskG() P = PolicyClass() g.policy_object = P options = {"g": g} smtpmock.setdata(response={"*****@*****.**": (200, "OK")}) transactionid = "123456098714" db_token = Token.query.filter_by(serial=self.serial1).first() token = EmailTokenClass(db_token) email_text, mimetype = token._get_email_text_or_subject(options, EMAILACTION.EMAILTEXT) self.assertTrue("<p>Hello,</p>" in email_text) self.assertEqual(mimetype, "html") c = token.create_challenge(transactionid, options=options) self.assertTrue(c[0], c) display_message = c[1] self.assertTrue(c[3].get("state"), transactionid) self.assertEqual(display_message, "Enter the OTP from the Email:")
def test_05_custom_login_text(self): set_policy("logtext", scope=SCOPE.WEBUI, action="{0!s}=Go for it!".format(ACTION.LOGIN_TEXT)) with self.app.test_request_context('/', method='GET'): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) self.assertTrue(b"Go for it!" in res.data)
def test_02_get_allowed_audit_realm(self): # Check that an administrator is only allowed to see log entries of # the defined realms. # fill some audit entries Audit(action="enroll", success=1, realm="realm1a").save() Audit(action="enroll", success=1, realm="realm1a").save() Audit(action="enroll", success=1, realm="realm2b").save() Audit(action="enroll", success=1, realm="realm2b").save() Audit(action="enroll", success=1, realm="realm2b").save() # check, that we see all audit entries with self.app.test_request_context('/audit/', method='GET', data={"realm": "realm1a"}, headers={'Authorization': self.at}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) json_response = json.loads(res.data.decode('utf8')) self.assertTrue(json_response.get("result").get("status"), res) self.assertEqual( json_response.get("result").get("value").get("count"), 2) with self.app.test_request_context('/audit/', method='GET', headers={'Authorization': self.at}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) json_response = json.loads(res.data.decode('utf8')) self.assertTrue(json_response.get("result").get("status"), res) self.assertEqual( json_response.get("result").get("value").get("count"), 8) audit_list = json_response.get("result").get("value").get( "auditdata") audit_actions = [ a for a in audit_list if a.get("action") == "GET /audit/" ] self.assertEqual(len(audit_actions), 2) with self.app.test_request_context('/audit/', method='GET', data={"realm": "realm2b"}, headers={'Authorization': self.at}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) json_response = json.loads(res.data.decode('utf8')) self.assertTrue(json_response.get("result").get("status"), res) self.assertEqual( json_response.get("result").get("value").get("count"), 3) # set policy for audit realms set_policy("audit01", scope=SCOPE.ADMIN, action=ACTION.AUDIT, realm="realm1a") # check, that we only see allowed audit realms with self.app.test_request_context('/audit/', method='GET', headers={'Authorization': self.at}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) json_response = json.loads(res.data.decode('utf8')) self.assertTrue(json_response.get("result").get("status"), res) # We now have 3 entries, as we added one by the search in line #43 audit_list = json_response.get("result").get("value").get( "auditdata") audit_realms = [ a for a in audit_list if a.get("realm") == "realm1a" ] self.assertEqual(len(audit_realms), 3) self.assertEqual( json_response.get("result").get("value").get("count"), 3) # delete policy delete_policy("audit01")
def test_19_emailtext(self): # create a EMAILTEXT policy: p = set_policy(name="emailtext", action="{0!s}={1!s}".format(EMAILACTION.EMAILTEXT, "'Your <otp>'"), scope=SCOPE.AUTH) self.assertTrue(p > 0) g = FakeFlaskG() P = PolicyClass() g.policy_object = P options = {"g": g} smtpmock.setdata(response={"*****@*****.**": (200, "OK")}) transactionid = "123456098713" db_token = Token.query.filter_by(serial=self.serial1).first() token = EmailTokenClass(db_token) c = token.create_challenge(transactionid, options=options) self.assertTrue(c[0], c) display_message = c[1] self.assertTrue(c[3].get("state"), transactionid) self.assertEqual(display_message, _("Enter the OTP from the Email:")) _n, mimetype = token._get_email_text_or_subject( options, EMAILACTION.EMAILTEXT) self.assertEqual(mimetype, "plain") # Test AUTOEMAIL p = set_policy(name="autoemail", action=EMAILACTION.EMAILAUTO, scope=SCOPE.AUTH) self.assertTrue(p > 0) g = FakeFlaskG() P = PolicyClass() g.policy_object = P options = {"g": g} r = token.check_otp("287922", options=options) self.assertTrue(r > 0, r) # create a EMAILTEXT policy with template p = set_policy(name="emailtext", action="{0!s}=file:{1!s}".format( EMAILACTION.EMAILTEXT, TEMPLATE_FILE), scope=SCOPE.AUTH) self.assertTrue(p > 0) g = FakeFlaskG() P = PolicyClass() g.policy_object = P options = {"g": g} smtpmock.setdata(response={"*****@*****.**": (200, "OK")}) transactionid = "123456098714" db_token = Token.query.filter_by(serial=self.serial1).first() token = EmailTokenClass(db_token) email_text, mimetype = token._get_email_text_or_subject( options, EMAILACTION.EMAILTEXT) self.assertTrue("<p>Hello,</p>" in email_text) self.assertEqual(mimetype, "html") c = token.create_challenge(transactionid, options=options) self.assertTrue(c[0], c) display_message = c[1] self.assertTrue(c[3].get("state"), transactionid) self.assertEqual(display_message, _("Enter the OTP from the Email:"))
def test_05_u2f_auth_fails_wrong_issuer(self): # test data taken from # https://fidoalliance.org/specs/fido-u2f-v1.0-ps-20141009/fido-u2f-raw-message-formats-ps-20141009.html#examples serial = "U2F0010BF6F" set_privacyidea_config("u2f.appId", "https://puck.az.intern") pin = "test" # Registration data client_data = "eyJ0eXAiOiJuYXZpZ2F0b3IuaWQuZmluaXNoRW5yb2xsbWVudCIsImNoYWxsZW5nZSI6ImpIakIxaEM2VjA3dDl4ZnNNaDRfOEQ3U1JuSHRFY1BqUTdsaVl3cWxkX009Iiwib3JpZ2luIjoiaHR0cHM6Ly9wdWNrLmF6LmludGVybiIsImNpZF9wdWJrZXkiOiJ1bnVzZWQifQ" reg_data = "BQRHjwxEYFCkLHz3xdrmifKOHl2h17BmRJQ_S1Y9PRAhS2R186T391YE-ryqWis9HSmdp0XpRqUaKk9L8lxJTPpTQF_xFJ_LAsKkPTzKIwUlPIjGZDsLmv0en2Iya17Yz8X8OS89fuxwZOvEok-NQOKUTJP3att_RVe3dEAbq_iOtyAwggJEMIIBLqADAgECAgRVYr6gMAsGCSqGSIb3DQEBCzAuMSwwKgYDVQQDEyNZdWJpY28gVTJGIFJvb3QgQ0EgU2VyaWFsIDQ1NzIwMDYzMTAgFw0xNDA4MDEwMDAwMDBaGA8yMDUwMDkwNDAwMDAwMFowKjEoMCYGA1UEAwwfWXViaWNvIFUyRiBFRSBTZXJpYWwgMTQzMjUzNDY4ODBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABEszH3c9gUS5mVy-RYVRfhdYOqR2I2lcvoWsSCyAGfLJuUZ64EWw5m8TGy6jJDyR_aYC4xjz_F2NKnq65yvRQwmjOzA5MCIGCSsGAQQBgsQKAgQVMS4zLjYuMS40LjEuNDE0ODIuMS41MBMGCysGAQQBguUcAgEBBAQDAgUgMAsGCSqGSIb3DQEBCwOCAQEArBbZs262s6m3bXWUs09Z9Pc-28n96yk162tFHKv0HSXT5xYU10cmBMpypXjjI-23YARoXwXn0bm-BdtulED6xc_JMqbK-uhSmXcu2wJ4ICA81BQdPutvaizpnjlXgDJjq6uNbsSAp98IStLLp7fW13yUw-vAsWb5YFfK9f46Yx6iakM3YqNvvs9M9EUJYl_VrxBJqnyLx2iaZlnpr13o8NcsKIJRdMUOBqt_ageQg3ttsyq_3LyoNcu7CQ7x8NmeCGm_6eVnZMQjDmwFdymwEN4OxfnM5MkcKCYhjqgIGruWkVHsFnJa8qjZXneVvKoiepuUQyDEJ2GcqvhU2YKY1zBFAiEA4ZkIXXyjEPExcMGtW6kJXqYv7UHgjxJR5h3H9w9FV7gCIFGdhxZDqwCQKplDi-LU4WJ45OyCpNK6lGa72eZqUR_k" # Authentication data transaction_id = "05871369157706202013" challenge = "1616515928c389ba9e028d83eb5f63782cbf351ca6abbc81aeb0dddd4895b609" # challenge = "FhZRWSjDibqeAo2D619jeCy_NRymq7yBrrDd3UiVtgk" key_handle = "X_EUn8sCwqQ9PMojBSU8iMZkOwua_R6fYjJrXtjPxfw5Lz1-7HBk68SiT41A4pRMk_dq239FV7d0QBur-I63IA" client_data_auth = "eyJ0eXAiOiJuYXZpZ2F0b3IuaWQuZ2V0QXNzZXJ0aW9uIiwiY2hhbGxlbmdlIjoiRmhaUldTakRpYnFlQW8yRDYxOWplQ3lfTlJ5bXE3eUJyckRkM1VpVnRnayIsIm9yaWdpbiI6Imh0dHBzOi8vcHVjay5hei5pbnRlcm4iLCJjaWRfcHVia2V5IjoidW51c2VkIn0" signature_data = "AQAAAAMwRQIgU8d6waOIRVVydg_AXxediEZGkfFioUjd6FG3OxH2wUMCIQDpxzavJyxRlMwgNmD1Kw-iw_oP2egdshU9hrpxFHTRzQ" # step 1 with self.app.test_request_context('/token/init', method='POST', data={ "type": "u2f", "user": "******", "realm": self.realm1, "serial": serial }, headers={'Authorization': self.at}): res = self.app.full_dispatch_request() self.assertEqual(res.status_code, 200) result = json.loads(res.data.decode('utf8')).get("result") detail = json.loads(res.data.decode('utf8')).get("detail") self.assertEqual(result.get("status"), True) self.assertEqual(result.get("value"), True) # Init step 2 with self.app.test_request_context('/token/init', method='POST', data={ "type": "u2f", "serial": serial, "regdata": reg_data, "clientdata": client_data }, headers={'Authorization': self.at}): res = self.app.full_dispatch_request() self.assertEqual(res.status_code, 200) result = json.loads(res.data.decode('utf8')).get("result") detail = json.loads(res.data.decode('utf8')).get("detail") self.assertEqual(result.get("status"), True) self.assertEqual(result.get("value"), True) # create a challenge in the database db_challenge = Challenge(serial, transaction_id=transaction_id, challenge=challenge, data=None) db_challenge.save() set_policy(name="u2f01", scope=SCOPE.AUTHZ, action="{0!s}=issuer/.*Plugup.*/".format(U2FACTION.REQ)) # Successful C/R authentication with self.app.test_request_context('/validate/check', method='POST', data={ "serial": serial, "pass": "", "transaction_id": transaction_id, "clientdata": client_data_auth, "signaturedata": signature_data }): res = self.app.full_dispatch_request() self.assertEqual(res.status_code, 403) result = json.loads(res.data.decode('utf8')).get("result") self.assertEqual(result.get("status"), False) self.assertEqual(result.get("error").get("code"), ERROR.POLICY) self.assertEqual( result.get("error").get("message"), u'The U2F device is not allowed to authenticate due to policy restriction.' ) delete_policy("u2f01") remove_token(serial)
def test_01_setup_eventhandlers(self): # This test create an HOTP token with C/R with a pre-event handler # and the user uses this HOTP token to directly login to /auth # Setup realm rid = save_resolver({ "resolver": self.resolvername1, "type": "passwdresolver", "fileName": PWFILE }) self.assertTrue(rid > 0, rid) (added, failed) = set_realm(self.realm1, [self.resolvername1]) self.assertTrue(len(failed) == 0) self.assertTrue(len(added) == 1) set_default_realm(self.realm1) # set a policy to authenticate against privacyIDEA set_policy("piLogin", scope=SCOPE.WEBUI, action="{0!s}=privacyIDEA".format(ACTION.LOGINMODE)) # set a policy to for otppin=userstore set_policy("otppin", scope=SCOPE.AUTH, action="{0!s}=userstore".format(ACTION.OTPPIN)) # Set a policy to do C/R with HOTP tokens set_policy("crhotp", scope=SCOPE.AUTH, action="{0!s}=hotp".format(ACTION.CHALLENGERESPONSE)) # Create an event handler, that creates HOTP token on /auth with default OTP key eid = set_event( "createtoken", event=["auth"], handlermodule="Token", action="enroll", position="pre", conditions={CONDITION.USER_TOKEN_NUMBER: 0}, options={ "tokentype": "hotp", "user": "******", "additional_params": { 'otpkey': self.otpkey, # We need to set gekey=0, otherwise the Tokenhandler will # generate a random otpkey 'genkey': 0 } }) # cleanup tokens remove_token(user=User("someuser", self.realm1)) # user tries to log in with his userstore password and gets a transaction_id with self.app.test_request_context('/auth', method='POST', data={ "username": "******", "password": "******" }): res = self.app.full_dispatch_request() self.assertEqual(401, res.status_code, res) result = res.json.get("result") self.assertFalse(result.get("status"), result) detail = res.json.get("detail") self.assertEqual("please enter otp: ", detail.get("message")) transaction_id = detail.get("transaction_id") # Check if the token was enrolled toks = get_tokens(user=User("someuser", self.realm1)) self.assertEqual(len(toks), 1) self.assertEqual(toks[0].token.tokentype, "hotp") serial = toks[0].token.serial # Check if the correct otpkey was used hotptoken = toks[0] r = hotptoken.check_otp(self.valid_otp_values[1]) self.assertTrue(r >= 0) # Now the user logs in with the second step of C/R with OTP value of new token with self.app.test_request_context('/auth', method='POST', data={ "username": "******", "transaction_id": transaction_id, "password": self.valid_otp_values[2] }): res = self.app.full_dispatch_request() self.assertEqual(200, res.status_code, res) result = res.json.get("result") self.assertTrue(result.get("status")) self.assertTrue(result.get("value")) # Check that there is still only one token toks = get_tokens(user=User("someuser", self.realm1)) self.assertEqual(len(toks), 1) self.assertEqual(toks[0].token.tokentype, "hotp") self.assertEqual(serial, toks[0].token.serial) # cleanup delete_policy("piLogin") delete_policy("otppin") delete_policy("crhotp") delete_event(eid) remove_token(hotptoken.token.serial)
def test_19_ui_get_menus(self): delete_all_policies() luser = {"username": "******", "role": "admin"} # Without policies, the admin gets all P = PolicyClass() menus = P.ui_get_main_menus(luser) self.assertTrue(MAIN_MENU.USERS in menus) self.assertTrue(MAIN_MENU.TOKENS in menus) self.assertTrue(MAIN_MENU.COMPONENTS in menus) self.assertTrue(MAIN_MENU.CONFIG in menus) self.assertTrue(MAIN_MENU.MACHINES in menus) # Admin has only right to enroll HOTP! :-) set_policy("pol1", scope=SCOPE.ADMIN, user="******", action="enrollHOTP") P = PolicyClass() menus = P.ui_get_main_menus(luser) # Thus he can only see the token menu self.assertTrue(MAIN_MENU.USERS not in menus) self.assertTrue(MAIN_MENU.TOKENS in menus) self.assertTrue(MAIN_MENU.COMPONENTS not in menus) self.assertTrue(MAIN_MENU.CONFIG not in menus) self.assertTrue(MAIN_MENU.MACHINES not in menus) set_policy("pol2", scope=SCOPE.ADMIN, user="******", action=ACTION.USERLIST) P = PolicyClass() menus = P.ui_get_main_menus(luser) # Thus he can only see the token menu self.assertTrue(MAIN_MENU.USERS in menus) self.assertTrue(MAIN_MENU.TOKENS in menus) self.assertTrue(MAIN_MENU.COMPONENTS not in menus) self.assertTrue(MAIN_MENU.CONFIG not in menus) self.assertTrue(MAIN_MENU.MACHINES not in menus) set_policy("pol3", scope=SCOPE.ADMIN, user="******", action=ACTION.MACHINELIST) P = PolicyClass() menus = P.ui_get_main_menus(luser) # Thus he can only see the token menu self.assertTrue(MAIN_MENU.USERS in menus) self.assertTrue(MAIN_MENU.TOKENS in menus) self.assertTrue(MAIN_MENU.COMPONENTS not in menus) self.assertTrue(MAIN_MENU.CONFIG not in menus) self.assertTrue(MAIN_MENU.MACHINES in menus) set_policy("pol4", scope=SCOPE.ADMIN, user="******", action=ACTION.SYSTEMDELETE) P = PolicyClass() menus = P.ui_get_main_menus(luser) # Thus he can only see the token menu self.assertTrue(MAIN_MENU.USERS in menus) self.assertTrue(MAIN_MENU.TOKENS in menus) self.assertTrue(MAIN_MENU.COMPONENTS not in menus) self.assertTrue(MAIN_MENU.CONFIG in menus) self.assertTrue(MAIN_MENU.MACHINES in menus) delete_all_policies()
def test_08_get_webui_settings(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": { "role": "user", "username": "******" } }, "version": "privacyIDEA test", "detail": { "serial": serial }, "id": 1 } resp = Response(json.dumps(res)) new_response = get_webui_settings(req, resp) jresult = json.loads(new_response.data) self.assertEqual( jresult.get("result").get("value").get("token_wizard"), False) # Set a policy. User has not token, so "token_wizard" will be True set_policy(name="pol_wizard", scope=SCOPE.WEBUI, action=ACTION.TOKENWIZARD) g.policy_object = PolicyClass() new_response = get_webui_settings(req, resp) jresult = json.loads(new_response.data) self.assertEqual( jresult.get("result").get("value").get("token_wizard"), True) delete_policy("pol_wizard")
def test_10_auth_items_ssh(self): # create an SSH token token_obj = init_token({"serial": self.serial2, "type": "sshkey", "sshkey": SSHKEY}) self.assertEqual(token_obj.type, "sshkey") # Attach the token to the machine "gandalf" with the application SSH r = attach_token(hostname="gandalf", serial=self.serial2, application="ssh", options={"user": "******"}) self.assertEqual(r.machine_id, "192.168.0.1") # fetch the auth_items for application SSH on machine gandalf with self.app.test_request_context( '/machine/authitem/ssh?hostname=gandalf', method='GET', headers={'Authorization': self.at}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) result = res.json.get("result") self.assertEqual(result["status"], True) sshkey = result["value"].get("ssh")[0].get("sshkey") self.assertTrue(sshkey.startswith("ssh-rsa"), sshkey) # fetch the auth_items for user testuser with self.app.test_request_context( '/machine/authitem/ssh?hostname=gandalf&user=testuser', method='GET', headers={'Authorization': self.at}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) result = res.json.get("result") self.assertEqual(result["status"], True) sshkey = result["value"].get("ssh")[0].get("sshkey") self.assertTrue(sshkey.startswith("ssh-rsa"), sshkey) # fetch auth_items for testuser, but with mangle policy # Remove everything that sounds like "SOMETHING\" in front of # the username set_policy(name="mangle1", scope=SCOPE.AUTH, action="{0!s}=user/.*\\\\(.*)/\\1/".format(ACTION.MANGLE)) with self.app.test_request_context( '/machine/authitem/ssh?hostname=gandalf&user=DOMAIN\\testuser', method='GET', headers={'Authorization': self.at}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) result = res.json.get("result") self.assertEqual(result["status"], True) sshkey = result["value"].get("ssh")[0].get("sshkey") self.assertTrue(sshkey.startswith("ssh-rsa"), sshkey) delete_policy("mangle1") # Now that the policy is deleted, we will not get the auth_items # anymore, since the username is not mangled. with self.app.test_request_context( '/machine/authitem/ssh?hostname=gandalf&user=DOMAIN\\testuser', method='GET', headers={'Authorization': self.at}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) result = res.json.get("result") self.assertEqual(result["status"], True) sshkeys = result["value"].get("ssh") # No user DOMAIN\\testuser and no SSH keys self.assertFalse(sshkeys) # fetch the auth_items on machine gandalf for all applications with self.app.test_request_context( '/machine/authitem?hostname=gandalf', method='GET', headers={'Authorization': self.at}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) result = res.json.get("result") sshkey = result["value"].get("ssh")[0].get("sshkey") self.assertTrue(sshkey.startswith("ssh-rsa"), sshkey)
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 test_12_authcache(self): password = "******" username = "******" realm = "myrealm" resolver = "reso001" r = save_resolver({"resolver": "reso001", "type": "passwdresolver", "fileName": "tests/testdata/passwords"}) (added, failed) = set_realm("myrealm", ["reso001"]) def fake_check_user_pass(user, passw, options=None): return True, {"message": "Fake Authentication"} set_policy(name="pol1", scope=SCOPE.AUTH, realm=realm, resolver=resolver, action="{0!s}={1!s}".format(ACTION.AUTH_CACHE, "4h/5m")) g = FakeFlaskG() P = PolicyClass() g.policy_object = P g.audit_object = FakeAudit() options = {"g": g} # This successfully authenticates against the authcache # We have an authentication, that is within the policy timeout AuthCache(username, realm, resolver, _hash_password(password), first_auth=datetime.datetime.utcnow() - timedelta(hours=3), last_auth=datetime.datetime.utcnow() - timedelta(minutes=1)).save() r = auth_cache(fake_check_user_pass, User("cornelius", "myrealm"), password, options=options) self.assertTrue(r[0]) self.assertEqual(r[1].get("message"), "Authenticated by AuthCache." ) # We have an authentication, that is not read from the authcache, # since the authcache first_auth is too old. delete_from_cache(username, realm, resolver, password) AuthCache(username, realm, resolver, _hash_password(password), first_auth=datetime.datetime.utcnow() - timedelta(hours=5), last_auth=datetime.datetime.utcnow() - timedelta( minutes=1)).save() r = auth_cache(fake_check_user_pass, User("cornelius", "myrealm"), password, options=options) self.assertTrue(r[0]) self.assertEqual(r[1].get("message"), "Fake Authentication") # We have an authentication, that is not read from authcache, since # the last_auth is too old = 10 minutes. delete_from_cache(username, realm, resolver, password) AuthCache(username, realm, resolver, _hash_password(password), first_auth=datetime.datetime.utcnow() - timedelta(hours=1), last_auth=datetime.datetime.utcnow() - timedelta( minutes=10)).save() r = auth_cache(fake_check_user_pass, User("cornelius", "myrealm"), password, options=options) self.assertTrue(r[0]) self.assertEqual(r[1].get("message"), "Fake Authentication") # We have a policy, with no special last_auth delete_policy("pol1") set_policy(name="pol1", scope=SCOPE.AUTH, realm=realm, resolver=resolver, action="{0!s}={1!s}".format(ACTION.AUTH_CACHE, "4h")) g = FakeFlaskG() P = PolicyClass() g.policy_object = P g.audit_object = FakeAudit() options = {"g": g} delete_from_cache(username, realm, resolver, password) AuthCache(username, realm, resolver, _hash_password(password), first_auth=datetime.datetime.utcnow() - timedelta(hours=2), last_auth=datetime.datetime.utcnow() - timedelta( hours=1)).save() r = auth_cache(fake_check_user_pass, User("cornelius", "myrealm"), password, options=options) self.assertTrue(r[0]) self.assertEqual(r[1].get("message"), "Authenticated by AuthCache.") # Clean up delete_policy("pol1") delete_realm("myrealm") delete_resolver("reso001")
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_14_otppin_priority(self): my_user = User("cornelius", realm="r1") set_policy(name="pol1", scope=SCOPE.AUTH, action="{0!s}={1!s}".format(ACTION.OTPPIN, ACTIONVALUE.NONE), priority=2) set_policy(name="pol2", scope=SCOPE.AUTH, action="{0!s}={1!s}".format(ACTION.OTPPIN, ACTIONVALUE.TOKENPIN), priority=2) g = FakeFlaskG() P = PolicyClass() g.policy_object = P g.audit_object = FakeAudit() options = {"g": g} # error because of conflicting policies with self.assertRaises(PolicyError): auth_otppin(self.fake_check_otp, None, "", options=options, user=my_user) # lower pol2 priority set_policy(name="pol2", priority=3) g.policy_object.reload_from_db() # NONE with empty PIN -> success r = auth_otppin(self.fake_check_otp, None, "", options=options, user=my_user) self.assertTrue(r) # NONE with empty PIN -> success, even if the authentication is done # for a serial and not a user, since the policy holds for all realms token = init_token({"type": "HOTP", "otpkey": "1234"}) r = auth_otppin(self.fake_check_otp, token, "", options=options, user=None) self.assertTrue(r) # NONE with some pin -> fail r = auth_otppin(self.fake_check_otp, None, "some pin", options=options, user=my_user) self.assertFalse(r) # increase pol2 priority set_policy(name="pol2", priority=1) g.policy_object.reload_from_db() r = auth_otppin(self.fake_check_otp, None, "FAKE", options=options, user=my_user) self.assertTrue(r) r = auth_otppin(self.fake_check_otp, None, "Wrong Pin", options=options, user=my_user) self.assertFalse(r) delete_policy("pol1") delete_policy("pol2")
def test_01_otppin(self): my_user = User("cornelius", realm="r1") set_policy(name="pol1", scope=SCOPE.AUTH, action="{0!s}={1!s}".format(ACTION.OTPPIN, ACTIONVALUE.NONE)) g = FakeFlaskG() P = PolicyClass() g.policy_object = P g.audit_object = FakeAudit() options = {"g": g} # NONE with empty PIN -> success r = auth_otppin(self.fake_check_otp, None, "", options=options, user=my_user) self.assertTrue(r) # NONE with empty PIN -> success, even if the authentication is done # for a serial and not a user, since the policy holds for all realms token = init_token({"type": "HOTP", "otpkey": "1234"}) r = auth_otppin(self.fake_check_otp, token, "", options=options, user=None) self.assertTrue(r) # NONE with some pin -> fail r = auth_otppin(self.fake_check_otp, None, "some pin", options=options, user=my_user) self.assertFalse(r) delete_policy("pol1") set_policy(name="pol1", scope=SCOPE.AUTH, action="{0!s}={1!s}".format(ACTION.OTPPIN, ACTIONVALUE.TOKENPIN)) g = FakeFlaskG() P = PolicyClass() g.policy_object = P g.audit_object = FakeAudit() options = {"g": g} r = auth_otppin(self.fake_check_otp, None, "FAKE", options=options, user=my_user) self.assertTrue(r) r = auth_otppin(self.fake_check_otp, None, "Wrong Pin", options=options, user=my_user) self.assertFalse(r) delete_policy("pol1")
def test_06_api_auth(self): self.setUp_user_realms() # get enrolled push token toks = get_tokens(tokentype="push") self.assertEqual(len(toks), 1) tokenobj = toks[0] # set PIN tokenobj.set_pin("pushpin") tokenobj.add_user(User("cornelius", self.realm1)) # Set a loginmode policy set_policy("webui", scope=SCOPE.WEBUI, action="{}={}".format(ACTION.LOGINMODE, LOGINMODE.PRIVACYIDEA)) # Set a PUSH_WAIT action which will be ignored by privacyIDEA set_policy("push1", scope=SCOPE.AUTH, action="{0!s}=20".format(PUSH_ACTION.WAIT)) with mock.patch('privacyidea.lib.smsprovider.FirebaseProvider.ServiceAccountCredentials') as mySA: # alternative: side_effect instead of return_value mySA.from_json_keyfile_name.return_value = myCredentials(myAccessTokenInfo("my_bearer_token")) # add responses, to simulate the communication to firebase responses.add(responses.POST, 'https://fcm.googleapis.com/v1/projects/test-123456/messages:send', body="""{}""", content_type="application/json") with self.app.test_request_context('/auth', method='POST', data={"username": "******", "realm": self.realm1, # this will be overwritted by pushtoken_disable_wait PUSH_ACTION.WAIT: "10", "password": "******"}): res = self.app.full_dispatch_request() self.assertEqual(res.status_code, 401) jsonresp = res.json self.assertFalse(jsonresp.get("result").get("value")) self.assertFalse(jsonresp.get("result").get("status")) self.assertEqual(jsonresp.get("detail").get("serial"), tokenobj.token.serial) self.assertIn("transaction_id", jsonresp.get("detail")) transaction_id = jsonresp.get("detail").get("transaction_id") self.assertEqual(jsonresp.get("detail").get("message"), DEFAULT_CHALLENGE_TEXT) # Get the challenge from the database challengeobject_list = get_challenges(serial=tokenobj.token.serial, transaction_id=transaction_id) challenge = challengeobject_list[0].challenge # This is what the smartphone answers. # create the signature: sign_data = "{0!s}|{1!s}".format(challenge, tokenobj.token.serial) signature = b32encode_and_unicode( self.smartphone_private_key.sign(sign_data.encode("utf-8"), padding.PKCS1v15(), hashes.SHA256())) # We still cannot log in with self.app.test_request_context('/auth', method='POST', data={"username": "******", "realm": self.realm1, "pass": "", "transaction_id": transaction_id}): res = self.app.full_dispatch_request() self.assertEqual(res.status_code, 401) self.assertFalse(res.json['result']['status']) # Answer the challenge with self.app.test_request_context('/ttype/push', method='POST', data={"serial": tokenobj.token.serial, "nonce": challenge, "signature": signature}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) self.assertTrue(res.json['result']['status']) self.assertTrue(res.json['result']['value']) # We can now log in with self.app.test_request_context('/auth', method='POST', data={"username": "******", "realm": self.realm1, "pass": "", "transaction_id": transaction_id}): res = self.app.full_dispatch_request() self.assertEqual(res.status_code, 200) self.assertTrue(res.json['result']['status']) delete_policy("push1") delete_policy("webui")
def test_04_get_user_audit_log(self): # Test that the user only sees audit log entries with his own data self.setUp_user_realms() # prepare some log entries for the normal user to try to fetch Audit(action="enroll", success=1, user="******", administrator=None, resolver="resolver1", realm=self.realm1a).save() Audit(action="enroll", success=1, user="******", administrator=None, resolver="resolver1", realm=self.realm1a).save() Audit(action="enroll", success=1, user="******", administrator=None, resolver="resolver1", realm=self.realm2b).save() Audit(action="enroll", success=1, user="******", administrator=None, resolver="resolver1", realm=self.realm2b).save() Audit(action="enroll", success=1, user="******", administrator=None, resolver="resolver1", realm=self.realm2b).save() # set policy: normal users in realm1a are allowed to view audit log set_policy("audit01", scope=SCOPE.USER, action=ACTION.AUDIT, realm=self.realm1a) user_authorization = None with self.app.test_request_context('/auth', method='POST', data={ 'username': '******'.format( self.realm1a), 'password': '******' }): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) json_response = res.json value = json_response.get("result").get("value") self.assertTrue("auditlog" in value.get("rights")) user_authorization = value.get("token") # check, that the normal user only sees his own entries with self.app.test_request_context( '/audit/', method='GET', data={ "action": "**", "action_detail": "**", "administrator": "**", "client": "**", "date": "**", "info": "**", "page": "1", "page_size": "10", "policies": "**", "privacyidea_server": "**", "realm": "**", "resolver": "**", "serial": "**", "sortorder": "desc", "success": "**", "tokentype": "**", "user": "******" }, headers={'Authorization': user_authorization}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) json_response = res.json self.assertTrue(json_response.get("result").get("status"), res) # We now have 3 entries, as we added one by the search in line #43 count = json_response.get("result").get("value").get("count") auditdata = json_response.get("result").get("value").get( "auditdata") self.assertGreaterEqual(count, 3) # All entries are in realm1A! for ad in auditdata: self.assertEqual(ad.get("realm"), self.realm1a) # try to explicitly query another realm with self.app.test_request_context( '/audit/', method='GET', data={ "action": "**", "action_detail": "**", "administrator": "**", "client": "**", "date": "**", "info": "**", "page": "1", "page_size": "10", "policies": "**", "privacyidea_server": "**", "realm": self.realm2b, "resolver": "**", "serial": "**", "sortorder": "desc", "success": "**", "tokentype": "**", "user": "******" }, headers={'Authorization': user_authorization}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) json_response = res.json self.assertTrue(json_response.get("result").get("status"), res) # The normal user can not fetch audit entries, that do not belong to him! count = json_response.get("result").get("value").get("count") self.assertGreaterEqual(count, 0) # delete policy delete_policy("audit01")
def test_01_check_token_action(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" req = Request(env) req.all_data = {"serial": "SomeSerial"} # Set a policy, that does allow the action set_policy(name="pol1", scope=SCOPE.ADMIN, action="enable", client="10.0.0.0/8") g.policy_object = PolicyClass() # Action enable is cool r = check_base_action(request=req, action="enable") self.assertTrue(r) # Another action - like "disable" - is not allowed # An exception is self.assertRaises(PolicyError, check_base_action, req, "disable") # Action delete is not allowed self.assertRaises(PolicyError, check_base_action, req, "delete") # check action with a token realm set_policy(name="pol1", scope=SCOPE.ADMIN, action="enable", client="10.0.0.0/8", realm="realm1") set_policy(name="pol2", scope=SCOPE.ADMIN, action="*", client="10.0.0.0/8", realm="realm2") g.policy_object = PolicyClass() # set a polrealm1 and a polrealm2 # setup realm1 self.setUp_user_realms() # setup realm2 self.setUp_user_realm2() tokenobject = init_token({ "serial": "POL001", "type": "hotp", "otpkey": "1234567890123456" }) r = set_realms("POL001", [self.realm1]) tokenobject = init_token({ "serial": "POL002", "type": "hotp", "otpkey": "1234567890123456" }) r = set_realms("POL002", [self.realm2]) # Token in realm1 can not be deleted req.all_data = {"serial": "POL001"} self.assertRaises(PolicyError, check_base_action, req, "delete") # while token in realm2 can be deleted req.all_data = {"serial": "POL002"} r = check_base_action(req, action="delete") self.assertTrue(r) # A normal user can "disable", since no user policies are defined. g.logged_in_user = {"username": "******", "role": "user"} r = check_base_action(req, "disable") self.assertTrue(r) delete_policy("pol1") delete_policy("pol2") remove_token("POL001") remove_token("POL002")
def test_03b_api_authenticate_client(self): # Test the /validate/check endpoints without the smartphone endpoint /ttype/push self.setUp_user_realms() # get enrolled push token toks = get_tokens(tokentype="push") self.assertEqual(len(toks), 1) tokenobj = toks[0] # set PIN tokenobj.set_pin("pushpin") tokenobj.add_user(User("cornelius", self.realm1)) # We mock the ServiceAccountCredentials, since we can not directly contact the Google API with mock.patch('privacyidea.lib.smsprovider.FirebaseProvider.ServiceAccountCredentials') as mySA: # alternative: side_effect instead of return_value mySA.from_json_keyfile_name.return_value = myCredentials(myAccessTokenInfo("my_bearer_token")) # add responses, to simulate the communication to firebase responses.add(responses.POST, 'https://fcm.googleapis.com/v1/projects/test-123456/messages:send', body="""{}""", content_type="application/json") # Send the first authentication request to trigger the challenge with self.app.test_request_context('/validate/check', method='POST', data={"user": "******", "realm": self.realm1, "pass": "******"}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) jsonresp = res.json self.assertFalse(jsonresp.get("result").get("value")) self.assertTrue(jsonresp.get("result").get("status")) self.assertEqual(jsonresp.get("detail").get("serial"), tokenobj.token.serial) self.assertTrue("transaction_id" in jsonresp.get("detail")) transaction_id = jsonresp.get("detail").get("transaction_id") self.assertEqual(jsonresp.get("detail").get("message"), DEFAULT_CHALLENGE_TEXT) # Our ServiceAccountCredentials mock has not been called because we use a cached token self.assertEqual(len(mySA.from_json_keyfile_name.mock_calls), 0) self.assertIn(FIREBASE_FILE, get_app_local_store()["firebase_token"]) # The mobile device has not communicated with the backend, yet. # The user is not authenticated! with self.app.test_request_context('/validate/check', method='POST', data={"user": "******", "realm": self.realm1, "pass": "", "transaction_id": transaction_id}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) jsonresp = res.json # Result-Value is false, the user has not answered the challenge, yet self.assertFalse(jsonresp.get("result").get("value")) # Now the smartphone communicates with the backend and the challenge in the database table # is marked as answered successfully. challengeobject_list = get_challenges(serial=tokenobj.token.serial, transaction_id=transaction_id) challengeobject_list[0].set_otp_status(True) with self.app.test_request_context('/validate/check', method='POST', data={"user": "******", "realm": self.realm1, "pass": "", "state": transaction_id}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) jsonresp = res.json # Result-Value is True, since the challenge is marked resolved in the DB self.assertTrue(jsonresp.get("result").get("value")) # We mock the ServiceAccountCredentials, since we can not directly contact the Google API # Do single shot auth with waiting # Also mock time.time to be 4000 seconds in the future (exceeding the validity of myAccessTokenInfo), # so that we fetch a new auth token with mock.patch('privacyidea.lib.smsprovider.FirebaseProvider.time') as mock_time: mock_time.time.return_value = time.time() + 4000 with mock.patch('privacyidea.lib.smsprovider.FirebaseProvider.ServiceAccountCredentials') as mySA: # alternative: side_effect instead of return_value mySA.from_json_keyfile_name.return_value = myCredentials(myAccessTokenInfo("my_new_bearer_token")) # add responses, to simulate the communication to firebase responses.add(responses.POST, 'https://fcm.googleapis.com/v1/projects/test-123456/messages:send', body="""{}""", content_type="application/json") # In two seconds we need to run an update on the challenge table. Timer(2, self.mark_challenge_as_accepted).start() set_policy("push1", scope=SCOPE.AUTH, action="{0!s}=20".format(PUSH_ACTION.WAIT)) # Send the first authentication request to trigger the challenge with self.app.test_request_context('/validate/check', method='POST', data={"user": "******", "realm": self.realm1, "pass": "******"}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) jsonresp = res.json # We successfully authenticated! YEAH! self.assertTrue(jsonresp.get("result").get("value")) self.assertTrue(jsonresp.get("result").get("status")) self.assertEqual(jsonresp.get("detail").get("serial"), tokenobj.token.serial) delete_policy("push1") # Our ServiceAccountCredentials mock has been called once because we fetched a new token self.assertEqual(len(mySA.from_json_keyfile_name.mock_calls), 1) self.assertIn(FIREBASE_FILE, get_app_local_store()["firebase_token"]) self.assertEqual(get_app_local_store()["firebase_token"][FIREBASE_FILE].access_token, "my_new_bearer_token") # Authentication fails, if the push notification is not accepted within the configured time with mock.patch('privacyidea.lib.smsprovider.FirebaseProvider.ServiceAccountCredentials') as mySA: # alternative: side_effect instead of return_value mySA.from_json_keyfile_name.return_value = myCredentials(myAccessTokenInfo("my_bearer_token")) # add responses, to simulate the communication to firebase responses.add(responses.POST, 'https://fcm.googleapis.com/v1/projects/test-123456/messages:send', body="""{}""", content_type="application/json") set_policy("push1", scope=SCOPE.AUTH, action="{0!s}=1".format(PUSH_ACTION.WAIT)) # Send the first authentication request to trigger the challenge with self.app.test_request_context('/validate/check', method='POST', data={"user": "******", "realm": self.realm1, "pass": "******"}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) jsonresp = res.json # We fail to authenticate! Oh No! self.assertFalse(jsonresp.get("result").get("value")) self.assertTrue(jsonresp.get("result").get("status")) self.assertEqual(jsonresp.get("detail").get("serial"), tokenobj.token.serial) delete_policy("push1")
def test_02_update_policies(self): p = set_policy(name="pol1", action="read", scope="system", realm="*", resolver="*", user="******", client="0.0.0.0/0", active="False") self.assertTrue(p > 0) p = set_policy(name="pol2", action="tokentype=HOTP", scope=SCOPE.AUTHZ, realm="*") self.assertTrue(p > 0) p = set_policy(name="pol2a", action="tokentype=TOTP", scope=SCOPE.AUTHZ, realm="realm2") self.assertTrue(p > 0) p = set_policy(name="pol3", action="serial=OATH", scope=SCOPE.AUTHZ, realm="realm1", resolver="resolver1") self.assertTrue(p > 0) p = set_policy(name="pol4", action="enroll, init, disable , enable", scope="admin", realm="realm2", user="******") self.assertTrue(p > 0) # enable and disable policies policies = PolicyClass().get_policies(active=False) num_old = len(policies) p = enable_policy("pol4", False) policies = PolicyClass().get_policies(active=False) self.assertTrue(num_old + 1 == len(policies), (num_old, len(policies))) p = enable_policy("pol4", True) policies = PolicyClass().get_policies(active=False) self.assertTrue(num_old == len(policies), len(policies)) # find inactive policies P = PolicyClass() policies = P.get_policies(active=False) self.assertTrue(len(policies) == 1, len(policies)) self.assertTrue(policies[0].get("name") == "pol1") # find policies action tokentype policies = P.get_policies(action="tokentype") self.assertTrue(len(policies) == 2, policies) # find policies action serial policies = P.get_policies(action="serial") self.assertTrue(len(policies) == 1, policies) # find policies with scope authorization policies = P.get_policies(scope=SCOPE.AUTHZ) self.assertTrue(len(policies) == 3, policies) # find policies authorization and realm2 policies = P.get_policies(action="tokentype", scope=SCOPE.AUTHZ) self.assertTrue(len(policies) == 2, policies) # find policies with user admin policies = P.get_policies(scope="admin", user="******") self.assertTrue(len(policies) == 1, "%s" % len(policies)) # find policies with resolver2 and authorization. THe result should # be pol2 and pol2a policies = P.get_policies(resolver="resolver2", scope=SCOPE.AUTHZ) self.assertTrue(len(policies) == 2, policies) # find policies with realm1 and authorization. We also include the # "*" into the result list. We find pol2 and pol3 policies = P.get_policies(realm="realm1", scope=SCOPE.AUTHZ) self.assertTrue(len(policies) == 2, policies) # find policies with resolver1 and authorization. # All other authorization policies will also match, since they either # user * or # have no destinct information about resolvers policies = P.get_policies(resolver="resolver1", scope=SCOPE.AUTHZ) self.assertTrue(len(policies) == 3, policies)
def test_02_api_enroll(self): self.authenticate() # Failed enrollment due to missing policy with self.app.test_request_context('/token/init', method='POST', data={"type": "push", "genkey": 1}, headers={'Authorization': self.at}): res = self.app.full_dispatch_request() self.assertNotEqual(res.status_code, 200) error = res.json.get("result").get("error") self.assertEqual(error.get("message"), "Missing enrollment policy for push token: push_firebase_configuration") self.assertEqual(error.get("code"), 303) r = set_smsgateway(self.firebase_config_name, u'privacyidea.lib.smsprovider.FirebaseProvider.FirebaseProvider', "myFB", {FIREBASE_CONFIG.REGISTRATION_URL: "http://test/ttype/push", FIREBASE_CONFIG.TTL: 10, FIREBASE_CONFIG.API_KEY: "1", FIREBASE_CONFIG.APP_ID: "2", FIREBASE_CONFIG.PROJECT_NUMBER: "3", FIREBASE_CONFIG.PROJECT_ID: "test-123456", FIREBASE_CONFIG.JSON_CONFIG: FIREBASE_FILE}) self.assertTrue(r > 0) set_policy("push1", scope=SCOPE.ENROLL, action="{0!s}={1!s}".format(PUSH_ACTION.FIREBASE_CONFIG, self.firebase_config_name)) # 1st step with self.app.test_request_context('/token/init', method='POST', data={"type": "push", "genkey": 1}, headers={'Authorization': self.at}): res = self.app.full_dispatch_request() self.assertEqual(res.status_code, 200) detail = res.json.get("detail") serial = detail.get("serial") self.assertEqual(detail.get("rollout_state"), "clientwait") self.assertTrue("pushurl" in detail) # check that the new URL contains the serial number self.assertTrue("&serial=PIPU" in detail.get("pushurl").get("value")) self.assertTrue("appid=" in detail.get("pushurl").get("value")) self.assertTrue("appidios=" in detail.get("pushurl").get("value")) self.assertTrue("apikeyios=" in detail.get("pushurl").get("value")) self.assertFalse("otpkey" in detail) enrollment_credential = detail.get("enrollment_credential") # 2nd step. Failing with wrong serial number with self.app.test_request_context('/ttype/push', method='POST', data={"serial": "wrongserial", "pubkey": self.smartphone_public_key_pem_urlsafe, "fbtoken": "firebaseT"}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 404, res) status = res.json.get("result").get("status") self.assertFalse(status) error = res.json.get("result").get("error") self.assertEqual(error.get("message"), "No token with this serial number in the rollout state 'clientwait'.") # 2nd step. Fails with missing enrollment credential with self.app.test_request_context('/ttype/push', method='POST', data={"serial": serial, "pubkey": self.smartphone_public_key_pem_urlsafe, "fbtoken": "firebaseT", "enrollment_credential": "WRonG"}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 400, res) status = res.json.get("result").get("status") self.assertFalse(status) error = res.json.get("result").get("error") self.assertEqual(error.get("message"), "ERR905: Invalid enrollment credential. You are not authorized to finalize this token.") # 2nd step: as performed by the smartphone with self.app.test_request_context('/ttype/push', method='POST', data={"enrollment_credential": enrollment_credential, "serial": serial, "pubkey": self.smartphone_public_key_pem_urlsafe, "fbtoken": "firebaseT"}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) detail = res.json.get("detail") # still the same serial number self.assertEqual(serial, detail.get("serial")) self.assertEqual(detail.get("rollout_state"), "enrolled") # Now the smartphone gets a public key from the server augmented_pubkey = "-----BEGIN RSA PUBLIC KEY-----\n{}\n-----END RSA PUBLIC KEY-----\n".format( detail.get("public_key")) parsed_server_pubkey = serialization.load_pem_public_key( to_bytes(augmented_pubkey), default_backend()) self.assertIsInstance(parsed_server_pubkey, RSAPublicKey) pubkey = detail.get("public_key") # Now check, what is in the token in the database toks = get_tokens(serial=serial) self.assertEqual(len(toks), 1) token_obj = toks[0] self.assertEqual(token_obj.token.rollout_state, u"enrolled") self.assertTrue(token_obj.token.active) tokeninfo = token_obj.get_tokeninfo() self.assertEqual(tokeninfo.get("public_key_smartphone"), self.smartphone_public_key_pem_urlsafe) self.assertEqual(tokeninfo.get("firebase_token"), u"firebaseT") self.assertEqual(tokeninfo.get("public_key_server").strip().strip("-BEGIN END RSA PUBLIC KEY-").strip(), pubkey) # The token should also contain the firebase config self.assertEqual(tokeninfo.get(PUSH_ACTION.FIREBASE_CONFIG), self.firebase_config_name)
def test_21_check_all_resolver(self): # check_all_resolver allows to find a policy for a secondary user # resolver. # We create one realm "realm1" with the resolvers # reso1 (prio 1) # reso2 (prio 2) # reso3 (prio 3) # A user user@realm1 will be identified as user.reso1@realm1. # But we will also match policies for reso2. # no realm and resolver r = get_realms() self.assertEqual(r, {}) r = get_resolver_list() self.assertEqual(r, {}) # create user realm for reso in ["reso1", "resoX", "resoA"]: rid = save_resolver({ "resolver": reso, "type": "passwdresolver", "fileName": PWFILE }) self.assertTrue(rid > 0, rid) # create a realm with reso1 being the resolver with the highest priority (added, failed) = set_realm("realm1", ["reso1", "resoX", "resoA"], priority={ "reso1": 1, "resoX": 2, "resoA": 3 }) self.assertTrue(len(failed) == 0) self.assertTrue(len(added) == 3) user = User(login="******", realm="realm1") # The user, that is created, is cornelius.reso1@realm1 user_str = "{0!s}".format(user) self.assertEqual(user_str, "<cornelius.reso1@realm1>") # But the user "cornelius" is also contained in other resolves in # this realm r = user.get_ordererd_resolvers() self.assertEqual(r, ["reso1", "resoX", "resoA"]) self.assertFalse(user.is_empty()) self.assertTrue(User().is_empty()) # define a policy with the wrong resolver p = set_policy(name="checkAll", scope=SCOPE.AUTHZ, realm="realm1", resolver="resoX", action="{0}=totp".format(ACTION.TOKENTYPE)) self.assertTrue(p > 0) p = set_policy(name="catchAll", scope=SCOPE.AUTHZ, realm="realm1", action="{0}=totp".format(ACTION.TOKENTYPE)) self.assertTrue(p > 0) P = PolicyClass() pols = P.get_policies(scope=SCOPE.AUTHZ, realm=user.realm, resolver=user.resolver, user=user.login) self.assertEqual(len(pols), 1) # Now we change the policy, so that it uses check_all_resolver, i.e. p = set_policy(name="checkAll", scope=SCOPE.AUTHZ, realm="realm1", resolver="resoX", check_all_resolvers=True, action="{0}=totp".format(ACTION.TOKENTYPE)) self.assertTrue(p > 0) P = PolicyClass() pols = P.get_policies(scope=SCOPE.AUTHZ, realm=user.realm, resolver=user.resolver, user=user.login) self.assertEqual(len(pols), 2) # delete policy delete_policy("checkAll") delete_policy("catchAll") # delete resolvers and realm delete_realm("realm1") for reso in ["reso1", "resoX", "resoA"]: rid = delete_resolver(reso) self.assertTrue(rid > 0, rid)
def test_05_autoassign_userstore(self): # init a token, that does has no user self.setUp_user_realms() tokenobject = init_token( { "serial": "UASSIGN2", "type": "hotp", "otpkey": "3132333435363738393031" "323334353637383930" }, tokenrealms=[self.realm1]) user_obj = User("autoassignuser", self.realm1) # unassign all tokens from the user autoassignuser try: unassign_token(None, user=user_obj) except Exception: print("no need to unassign token") # 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"] = "10.0.0.1" req = Request(env) req.all_data = { "user": "******", "realm": self.realm1, "pass": "******" } # The response with a failed request res = { "jsonrpc": "2.0", "result": { "status": True, "value": False }, "version": "privacyIDEA test", "id": 1 } resp = Response(json.dumps(res)) # Set the autoassign policy # to "userstore" set_policy(name="pol2", scope=SCOPE.ENROLL, action="%s=%s" % (ACTION.AUTOASSIGN, AUTOASSIGNVALUE.USERSTORE), client="10.0.0.0/8") g.policy_object = PolicyClass() new_response = autoassign(req, resp) jresult = json.loads(new_response.data) self.assertEqual(jresult.get("result").get("value"), True) self.assertEqual(jresult.get("detail").get("serial"), "UASSIGN2") # authenticate with 287082 a second time will fail res, dict = check_user_pass(User("autoassignuser", self.realm1), "password287082") self.assertFalse(res) # authenticate with the next OTP 359152 will succeed res, dict = check_user_pass(User("autoassignuser", self.realm1), "password359152") self.assertTrue(res) delete_policy("pol2")
def test_15_ui_tokentypes(self): P = PolicyClass() logged_in_user = { "username": "******", "role": "admin", "realm": "realm1" } # Without policies, the admin gets all tt = P.ui_get_enroll_tokentypes("127.0.0.1", logged_in_user) self.assertTrue("hotp" in tt) self.assertTrue("totp" in tt) self.assertTrue("motp" in tt) self.assertTrue("sms" in tt) self.assertTrue("spass" in tt) self.assertTrue("sshkey" in tt) self.assertTrue("email" in tt) self.assertTrue("certificate" in tt) self.assertTrue("yubico" in tt) self.assertTrue("yubikey" in tt) self.assertTrue("radius" in tt) # An admin may only enroll Yubikeys set_policy(name="tokenEnroll", scope=SCOPE.ADMIN, action="enrollYUBIKEY") P = PolicyClass() tt = P.ui_get_enroll_tokentypes("127.0.0.1", logged_in_user) self.assertFalse("hotp" in tt) self.assertFalse("totp" in tt) self.assertFalse("motp" in tt) self.assertFalse("sms" in tt) self.assertFalse("spass" in tt) self.assertFalse("sshkey" in tt) self.assertFalse("email" in tt) self.assertFalse("certificate" in tt) self.assertFalse("yubico" in tt) self.assertTrue("yubikey" in tt) self.assertFalse("radius" in tt) # A user may enroll nothing set_policy(name="someUserAction", scope=SCOPE.USER, action="disable") P = PolicyClass() tt = P.ui_get_enroll_tokentypes("127.0.0.1", { "username": "******", "realm": "realm", "role": "user" }) self.assertEqual(len(tt), 0) delete_policy("tokenEnroll") # Two admins: # adminA is allowed to enroll tokens in all realms # adminB is allowed to enroll tokens only in realmB set_policy(name="polAdminA", scope=SCOPE.ADMIN, user="******", action="enrollHOTP, enrollTOTP") set_policy(name="polAdminB", scope=SCOPE.ADMIN, user="******", realm="realmB", action="enrollHOTP") P = PolicyClass() # realm is empty, since in case of an admin, this is the admin realm rights = P.ui_get_enroll_tokentypes(None, { "role": SCOPE.ADMIN, "realm": None, "username": "******" }) self.assertTrue("hotp" in rights) self.assertTrue("totp" in rights) rights = P.ui_get_enroll_tokentypes(None, { "role": SCOPE.ADMIN, "realm": "", "username": "******" }) self.assertTrue("totp" not in rights) self.assertTrue("hotp" in rights) rights = P.ui_get_enroll_tokentypes(None, { "role": SCOPE.ADMIN, "realm": "", "username": "******" }) self.assertEqual(rights, {}) delete_policy("polAdminA") delete_policy("polAdminB")
def test_03_custom_parameters(self): set_policy( name="enrollhotp", action=["enrollHOTP=1", "delete", "hotp_2step=allow"], scope=SCOPE.ADMIN, ) with self.app.test_request_context( '/token/init', method='POST', data={ "type": "hotp", "genkey": "1", "2stepinit": "1", "2step_serversize": "5", "2step_clientsize": "16", "2step_difficulty": "17898", "hashlib": "sha512", # force 64-byte secret "otplen": "8", }, headers={'Authorization': self.at}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) result = res.json.get("result") self.assertTrue(result.get("status") is True, result) self.assertTrue(result.get("value") is True, result) detail = res.json.get("detail") serial = detail.get("serial") otpkey_url = detail.get("otpkey", {}).get("value") server_component = binascii.unhexlify(otpkey_url.split("/")[2]) client_component = b"wrongsize0" # 10 bytes hex_client_component = binascii.hexlify(client_component) # Supply a client secret of incorrect size with self.app.test_request_context('/token/init', method='POST', data={ "type": "hotp", "serial": serial, "otpkey": hex_client_component, }, headers={'Authorization': self.at}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 400, res) result = res.json.get("result") self.assertFalse(result.get("status")) client_component = b"correctsizeABCDE" # 16 bytes hex_client_component = binascii.hexlify(client_component) # Now doing the correct 2nd step with self.app.test_request_context( '/token/init', method='POST', data={ "type": "hotp", "serial": serial, "otpkey": hex_client_component, "2step_serversize": "3", # will have no effect "2step_clientsize": "4", "2step_difficulty": "33333" }, headers={'Authorization': self.at}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) result = res.json.get("result") self.assertTrue(result.get("status") is True, result) self.assertTrue(result.get("value") is True, result) detail = res.json.get("detail") otpkey_url = detail.get("otpkey", {}).get("value") otpkey = otpkey_url.split("/")[2] # Now try to authenticate otpkey_bin = binascii.unhexlify(otpkey) otp_value = HmacOtp(digits=8, hashfunc=hashlib.sha512).generate(key=otpkey_bin, counter=1) with self.app.test_request_context('/validate/check', method='POST', data={ "serial": serial, "pass": otp_value }): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) result = res.json.get("result") self.assertEqual(result.get("status"), True) self.assertEqual(result.get("value"), True) # Check serversize self.assertEqual(len(server_component), 5) # Check that the OTP key is what we expected it to be expected_secret = pbkdf2(binascii.hexlify(server_component), client_component, 17898, 64) self.assertEqual(otpkey_bin, expected_secret) with self.app.test_request_context('/token/' + serial, method='DELETE', headers={'Authorization': self.at}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) delete_policy("enrollhotp")
def test_04_remote_user_auth(self): self.setUp_user_realms() # first check that without a remote_user policy the login fails with self.app.test_request_context( '/auth', method='POST', data={"username": "******"}, environ_base={"REMOTE_USER": "******"}): res = self.app.full_dispatch_request() self.assertEqual(res.status_code, 401, res) result = res.json.get("result") self.assertFalse(result.get("status"), result) self.assertEqual(result.get("error").get('code'), 4031, result) self.assertEqual( result.get("error").get('message'), 'Authentication failure. Wrong credentials', result) aentry = self.find_most_recent_audit_entry(action='POST /auth') self.assertEqual(aentry['action'], 'POST /auth', aentry) self.assertEqual(aentry['success'], 0, aentry) # now check that with a disabled remote_user policy the login fails set_policy(name="remote", scope=SCOPE.WEBUI, action="{0!s}={1!s}".format(ACTION.REMOTE_USER, REMOTE_USER.DISABLE)) with self.app.test_request_context( '/auth', method='POST', data={"username": "******"}, environ_base={"REMOTE_USER": "******"}): res = self.app.full_dispatch_request() self.assertEqual(res.status_code, 401, res) result = res.json.get("result") self.assertFalse(result.get("status"), result) self.assertEqual(result.get("error").get('code'), 4031, result) self.assertEqual( result.get("error").get('message'), 'Authentication failure. Wrong credentials', result) aentry = self.find_most_recent_audit_entry(action='POST /auth') self.assertEqual(aentry['action'], 'POST /auth', aentry) self.assertEqual(aentry['success'], 0, aentry) self.assertEqual(aentry['policies'], 'remote', aentry) # And now check that with an enabled remote_user policy the login succeeds set_policy(name="remote", scope=SCOPE.WEBUI, action="{0!s}={1!s}".format(ACTION.REMOTE_USER, REMOTE_USER.ACTIVE)) with self.app.test_request_context( '/auth', method='POST', data={"username": "******"}, environ_base={"REMOTE_USER": "******"}): res = self.app.full_dispatch_request() self.assertEqual(res.status_code, 200, res) result = res.json.get("result") self.assertTrue(result.get("status"), result) self.assertEqual(result.get("value").get('role'), 'user', result) aentry = self.find_most_recent_audit_entry(action='POST /auth') self.assertEqual(aentry['action'], 'POST /auth', aentry) self.assertEqual(aentry['success'], 1, aentry) self.assertEqual(aentry['policies'], 'remote', aentry) # check that a remote user with "@" works as well set_policy(name="remote", scope=SCOPE.WEBUI, realm=self.realm1, action="{0!s}={1!s}".format(ACTION.REMOTE_USER, REMOTE_USER.ACTIVE)) with self.app.test_request_context( '/auth', method='POST', data={"username": "******"}, environ_base={"REMOTE_USER": "******"}): res = self.app.full_dispatch_request() self.assertEqual(res.status_code, 200, res) result = res.json.get("result") self.assertTrue(result.get("status"), result) self.assertEqual(result.get("value").get('role'), 'user', result) aentry = self.find_most_recent_audit_entry(action='POST /auth') self.assertEqual(aentry['action'], 'POST /auth', aentry) self.assertEqual(aentry['success'], 1, aentry) self.assertEqual(aentry['policies'], 'remote', aentry) # check that the policy remote_user=force passes the necessary hidden tag to the # login window set_policy(name="remote", scope=SCOPE.WEBUI, realm=self.realm1, action="{0!s}={1!s}".format(ACTION.REMOTE_USER, REMOTE_USER.FORCE)) with self.app.test_request_context( '/', method='GET', environ_base={"REMOTE_USER": "******"}): res = self.app.full_dispatch_request() self.assertEqual(res.status_code, 200, res) # The login page contains the info about force remote_user, which will hide the # "login with credentials" button. self.assertIn( u'input type=hidden id=FORCE_REMOTE_USER value="True"', to_unicode(res.data)) # bind the remote user policy to an unknown realm set_policy(name="remote", scope=SCOPE.WEBUI, realm='unknown', action="{0!s}={1!s}".format(ACTION.REMOTE_USER, REMOTE_USER.ACTIVE)) with self.app.test_request_context( '/auth', method='POST', data={"username": "******"}, environ_base={"REMOTE_USER": "******"}): res = self.app.full_dispatch_request() self.assertEqual(res.status_code, 401, res) result = res.json.get("result") self.assertFalse(result.get("status"), result) aentry = self.find_most_recent_audit_entry(action='POST /auth') self.assertEqual(aentry['action'], 'POST /auth', aentry) self.assertEqual(aentry['success'], 0, aentry) self.assertEqual(aentry['policies'], '', aentry) # check split@sign is working correctly set_policy(name="remote", scope=SCOPE.WEBUI, realm=self.realm1, action="{0!s}={1!s}".format(ACTION.REMOTE_USER, REMOTE_USER.ACTIVE)) set_privacyidea_config(SYSCONF.SPLITATSIGN, False) with self.app.test_request_context( '/auth', method='POST', data={"username": "******"}, environ_base={"REMOTE_USER": "******"}): res = self.app.full_dispatch_request() self.assertEqual(res.status_code, 200, res) result = res.json.get("result") self.assertTrue(result.get("status"), result) self.assertEqual(result.get("value").get('role'), 'user', result) aentry = self.find_most_recent_audit_entry(action='POST /auth') self.assertEqual(aentry['action'], 'POST /auth', aentry) self.assertEqual(aentry['success'], 1, aentry) self.assertEqual(aentry['policies'], 'remote', aentry) delete_policy(name='remote') set_privacyidea_config(SYSCONF.SPLITATSIGN, True)
def test_05_init_totp_token(self): set_policy( name="allow_2step", action=["totp_2step=allow", "enrollTOTP=1", "delete"], scope=SCOPE.ADMIN, ) with self.app.test_request_context('/token/init', method='POST', data={ "type": "totp", "genkey": "1", "2stepinit": "1" }, headers={'Authorization': self.at}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) result = res.json.get("result") self.assertTrue(result.get("status") is True, result) self.assertTrue(result.get("value") is True, result) detail = res.json.get("detail") serial = detail.get("serial") otpkey_url = detail.get("otpkey", {}).get("value") server_component = binascii.unhexlify(otpkey_url.split("/")[2]) google_url = detail["googleurl"]["value"] self.assertIn('2step_difficulty=10000', google_url) self.assertIn('2step_salt=8', google_url) self.assertIn('2step_output=20', google_url) self.assertEqual(detail['2step_difficulty'], 10000) self.assertEqual(detail['2step_salt'], 8) self.assertEqual(detail['2step_output'], 20) client_component = b"VRYSECRT" checksum = hashlib.sha1(client_component).digest()[:4] base32check_client_component = base64.b32encode( checksum + client_component).strip(b"=") # Try to do a 2stepinit on a second step will raise an error with self.app.test_request_context('/token/init', method='POST', data={ "type": "totp", "2stepinit": "1", "serial": serial, "otpkey": base32check_client_component, "otpkeyformat": "base32check" }, headers={'Authorization': self.at}): res = self.app.full_dispatch_request() self.assertEqual(res.status_code, 400) result = res.json.get("result") self.assertIn( '2stepinit is only to be used in the first initialization step', result.get("error").get("message")) # Invalid base32check will raise an error with self.app.test_request_context( '/token/init', method='POST', data={ "type": "totp", "2stepinit": "1", "serial": serial, "otpkey": b"A" + base32check_client_component[1:], "otpkeyformat": "base32check" }, headers={'Authorization': self.at}): res = self.app.full_dispatch_request() self.assertEqual(res.status_code, 400) result = res.json.get("result") self.assertIn('Malformed base32check data: Incorrect checksum', result.get("error").get("message")) # Authentication does not work yet! wrong_otp_value = HmacOtp().generate(key=server_component, counter=int(time.time() // 30)) with self.app.test_request_context('/validate/check', method='POST', data={ "serial": serial, "pass": wrong_otp_value }): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) result = res.json self.assertTrue(result.get("result").get("status")) self.assertFalse(result.get("result").get("value")) self.assertEqual( result.get("detail").get("message"), u'matching 1 tokens, Token is disabled') # Now doing the correct 2nd step with self.app.test_request_context('/token/init', method='POST', data={ "type": "totp", "serial": serial, "otpkey": base32check_client_component, "otpkeyformat": "base32check" }, headers={'Authorization': self.at}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) result = res.json.get("result") self.assertTrue(result.get("status") is True, result) self.assertTrue(result.get("value") is True, result) detail = res.json.get("detail") otpkey_url = detail.get("otpkey", {}).get("value") otpkey = otpkey_url.split("/")[2] self.assertNotIn('2step', detail) # Now try to authenticate otpkey_bin = binascii.unhexlify(otpkey) otp_value = HmacOtp().generate(key=otpkey_bin, counter=int(time.time() // 30)) with self.app.test_request_context('/validate/check', method='POST', data={ "serial": serial, "pass": otp_value }): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) result = res.json.get("result") self.assertEqual(result.get("status"), True) self.assertEqual(result.get("value"), True) # Check that the OTP key is what we expected it to be expected_secret = pbkdf2(binascii.hexlify(server_component), client_component, 10000, 20) self.assertEqual(otpkey_bin, expected_secret) with self.app.test_request_context('/token/' + serial, method='DELETE', headers={'Authorization': self.at}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) delete_policy("allow_2step")
def test_01_register_user(self): smtpmock.setdata(response={"*****@*****.**": (200, "OK")}) # create resolver and realm param = self.parameters param["resolver"] = "register" param["type"] = "sqlresolver" r = save_resolver(param) self.assertTrue(r > 0) added, failed = set_realm("register", resolvers=["register"]) self.assertTrue(added > 0) self.assertEqual(len(failed), 0) # create policy r = set_policy(name="pol2", scope=SCOPE.REGISTER, action="{0!s}={1!s}, {2!s}={3!s}".format( ACTION.REALM, "register", ACTION.RESOLVER, "register")) # Try to register, but missing parameter with self.app.test_request_context('/register', method='POST', data={ "username": "******", "surname": "Kölbel" }): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 400, res) # Register fails, missing SMTP config with self.app.test_request_context('/register', method='POST', data={ "username": "******", "surname": "Kölbel", "givenname": "Cornelius", "password": "******", "email": "*****@*****.**" }): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 400, res) data = json.loads(res.data) self.assertEqual( data.get("result").get("error").get("message"), u'ERR10: No SMTP server configuration specified!') # Set SMTP config and policy add_smtpserver("myserver", "1.2.3.4", sender="pi@localhost") set_policy("pol3", scope=SCOPE.REGISTER, action="{0!s}=myserver".format(ACTION.EMAILCONFIG)) with self.app.test_request_context('/register', method='POST', data={ "username": "******", "surname": "Kölbel", "givenname": "Cornelius", "password": "******", "email": "*****@*****.**" }): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) # Registering the user a second time will fail with self.app.test_request_context('/register', method='POST', data={ "username": "******", "surname": "Kölbel", "givenname": "Cornelius", "password": "******", "email": "*****@*****.**" }): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 400, res) # get the register status with self.app.test_request_context('/register', method='GET'): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) data = json.loads(res.data) self.assertEqual(data.get("result").get("value"), True)
def test_06_force_totp_parameters(self): set_policy( name="force_2step", action=["totp_2step=force", "enrollTOTP=1", "delete"], scope=SCOPE.ADMIN, ) set_policy( name="2step_params", action=[ "totp_2step_difficulty=12345", "totp_2step_serversize=33", "totp_2step_clientsize=11" ], scope=SCOPE.ENROLL, ) with self.app.test_request_context( '/token/init', method='POST', data={ "type": "totp", "genkey": "1", "2stepinit": "0", # will be forced nevertheless "2step_serversize": "3", "2step_clientsize": "4", "2step_difficulty": "33333", "timeStep": "60", "hashlib": "sha512", "otplen": "8", }, headers={'Authorization': self.at}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) result = res.json.get("result") self.assertTrue(result.get("status") is True, result) self.assertTrue(result.get("value") is True, result) detail = res.json.get("detail") serial = detail.get("serial") otpkey_url = detail.get("otpkey", {}).get("value") server_component = binascii.unhexlify(otpkey_url.split("/")[2]) google_url = detail["googleurl"]["value"] self.assertIn('2step_difficulty=12345', google_url) self.assertIn('2step_salt=11', google_url) self.assertIn('2step_output=64', google_url) # Authentication does not work yet! wrong_otp_value = HmacOtp(digits=8, hashfunc=hashlib.sha512).generate( key=server_component, counter=int(time.time() // 60)) with self.app.test_request_context('/validate/check', method='POST', data={ "serial": serial, "pass": wrong_otp_value }): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) result = res.json self.assertTrue(result.get("result").get("status")) self.assertFalse(result.get("result").get("value")) self.assertEqual( result.get("detail").get("message"), u'matching 1 tokens, Token is disabled') client_component = b"wrongsize" # 9 bytes hex_client_component = binascii.hexlify(client_component) # Supply a client secret of incorrect size with self.app.test_request_context('/token/init', method='POST', data={ "type": "totp", "serial": serial, "otpkey": hex_client_component, }, headers={'Authorization': self.at}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 400, res) result = res.json.get("result") self.assertFalse(result.get("status")) client_component = b"correctsize" # 11 bytes hex_client_component = binascii.hexlify(client_component) # Now doing the correct 2nd step with self.app.test_request_context( '/token/init', method='POST', data={ "type": "totp", "serial": serial, "otpkey": hex_client_component, "2step_serversize": "3", # will have no effect "2step_clientsize": "4", "2step_difficulty": "33333" }, headers={'Authorization': self.at}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) result = res.json.get("result") self.assertTrue(result.get("status") is True, result) self.assertTrue(result.get("value") is True, result) detail = res.json.get("detail") otpkey_url = detail.get("otpkey", {}).get("value") otpkey = otpkey_url.split("/")[2] # Now try to authenticate otpkey_bin = binascii.unhexlify(otpkey) otp_value = HmacOtp(digits=8, hashfunc=hashlib.sha512).generate( key=otpkey_bin, counter=int(time.time() // 60)) with self.app.test_request_context('/validate/check', method='POST', data={ "serial": serial, "pass": otp_value }): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) result = res.json.get("result") self.assertEqual(result.get("status"), True) self.assertEqual(result.get("value"), True) # Check serversize self.assertEqual(len(server_component), 33) # Check that the OTP key is what we expected it to be expected_secret = pbkdf2(binascii.hexlify(server_component), client_component, 12345, 64) self.assertEqual(otpkey_bin, expected_secret) with self.app.test_request_context('/token/' + serial, method='DELETE', headers={'Authorization': self.at}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) delete_policy("force_2step") delete_policy("2step_params")
def test_03_get_allowed_audit_realm(self): # Check than an internal admin is allowed to see all realms # A helpdesk user in "adminrealm" is only allowerd to see realm1A Audit(action="enroll", success=1, realm=self.realm1a).save() Audit(action="enroll", success=1, realm=self.realm1a).save() Audit(action="enroll", success=1, realm=self.realm2b).save() Audit(action="enroll", success=1, realm=self.realm2b).save() Audit(action="enroll", success=1, realm=self.realm2b).save() # check, that we see all audit entries with self.app.test_request_context('/audit/', method='GET', data={"realm": self.realm1a}, headers={'Authorization': self.at}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) json_response = res.json self.assertTrue(json_response.get("result").get("status"), res) self.assertGreaterEqual( json_response.get("result").get("value").get("count"), 2) with self.app.test_request_context('/audit/', method='GET', data={"realm": self.realm2b}, headers={'Authorization': self.at}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) json_response = res.json self.assertTrue(json_response.get("result").get("status"), res) self.assertGreaterEqual( json_response.get("result").get("value").get("count"), 3) # set policy: helpdesk users in adminrealm are only allowed to # view "realm1A". set_policy("audit01", scope=SCOPE.ADMIN, action=ACTION.AUDIT, adminrealm="adminrealm", realm=self.realm1a) # Test admin is allowed to view unrestricted logs! set_policy("audit02", scope=SCOPE.ADMIN, action=ACTION.AUDIT, user="******") rid = save_resolver({ "resolver": self.resolvername1, "type": "passwdresolver", "fileName": PWFILE }) self.assertTrue(rid > 0, rid) (added, failed) = set_realm("adminrealm", [self.resolvername1]) self.assertTrue(len(failed) == 0) self.assertTrue(len(added) == 1) helpdesk_authorization = None with self.app.test_request_context('/auth', method='POST', data={ 'username': '******', 'password': '******' }): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) json_response = res.json value = json_response.get("result").get("value") # Helpdesk user is allowed to view the audit log. self.assertTrue("auditlog" in value.get("rights")) helpdesk_authorization = value.get("token") # check, that we only see allowed audit realms with self.app.test_request_context( '/audit/', method='GET', data={ "action": "**", "action_detail": "**", "administrator": "**", "client": "**", "date": "**", "info": "**", "page": "1", "page_size": "10", "policies": "**", "privacyidea_server": "**", "realm": "**", "resolver": "**", "serial": "**", "sortorder": "desc", "success": "**", "tokentype": "**", "user": "******" }, headers={'Authorization': helpdesk_authorization}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) json_response = res.json self.assertTrue(json_response.get("result").get("status"), res) # We now have 3 entries, as we added one by the search in line #43 count = json_response.get("result").get("value").get("count") auditdata = json_response.get("result").get("value").get( "auditdata") self.assertGreaterEqual(count, 3) # All entries are in realm1A! for ad in auditdata: self.assertEqual(ad.get("realm"), self.realm1a) # Now check, that the testadmin (self.at) sees all entries! with self.app.test_request_context('/audit/', method='GET', headers={'Authorization': self.at}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) json_response = res.json self.assertTrue(json_response.get("result").get("status"), res) # We now have 3 entries, as we added one by the search in line #43 count = json_response.get("result").get("value").get("count") auditdata = json_response.get("result").get("value").get( "auditdata") self.assertGreaterEqual(count, 10) # delete policy delete_policy("audit01") delete_policy("audit02")
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 (old style) set_policy(name="pol1", scope=SCOPE.AUTH, action=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 set a PASSTHRU policy to the userstore (new style) 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 set a PASSTHRU policy to a RADIUS config (new style) 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) 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 RADIUS server radiusconfig1 due to '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 test_07_login_mode(self): # a realm: cornelius@r1: PW: test def check_webui_user_userstore(user_obj, password, options=None, superuser_realms=None, check_otp=False): self.assertEqual(check_otp, False) def check_webui_user_privacyidea(user_obj, password, options=None, superuser_realms=None, check_otp=False): self.assertEqual(check_otp, True) user_obj = User("cornelius", "r1") g = FakeFlaskG() P = PolicyClass() g.policy_object = P g.audit_object = FakeAudit() options = {"g": g} # No policy, the function is called with check_otp=False login_mode(check_webui_user_userstore, user_obj, "", options=options, superuser_realms="", check_otp=False) set_policy(name="pol2", scope=SCOPE.WEBUI, action="{0!s}={1!s}".format(ACTION.LOGINMODE, LOGINMODE.PRIVACYIDEA)) g = FakeFlaskG() P = PolicyClass() g.policy_object = P g.audit_object = FakeAudit() options = {"g": g} # Policy is set, the function is called with check_otp=True login_mode(check_webui_user_privacyidea, user_obj, "", options=options, superuser_realms="", check_otp=False) # Set policy, so that the user is not allowed to login at all set_policy(name="pol2", scope=SCOPE.WEBUI, action="{0!s}={1!s}".format(ACTION.LOGINMODE, LOGINMODE.DISABLE)) g = FakeFlaskG() P = PolicyClass() g.policy_object = P g.audit_object = FakeAudit() options = {"g": g} # Policy is set. Trying to login raises a policy error self.assertRaises(PolicyError, login_mode, check_webui_user_privacyidea, user_obj, "", options=options, superuser_realms="", check_otp=False) delete_policy("pol2")
def test_11_otppin_with_resolvers(self): # This tests, if the otppin policy differentiates between users in # the same realm but in different resolvers. r = save_resolver({ "resolver": "reso001", "type": "passwdresolver", "fileName": "tests/testdata/passwords" }) # user "cornelius" is in resolver reso001 self.assertTrue(r > 0) r = save_resolver({ "resolver": "reso002", "type": "passwdresolver", "fileName": "tests/testdata/pw-2nd-resolver" }) # user "userresolver2" is in resolver reso002 self.assertTrue(r > 0) (added, failed) = set_realm("myrealm", ["reso001", "reso002"]) self.assertEqual(len(added), 2) self.assertEqual(len(failed), 0) my_user_1 = User("cornelius", realm="myrealm") my_user_2 = User("userresolver2", realm="myrealm") # We set a policy only for resolver reso002 set_policy(name="pol1", scope=SCOPE.AUTH, realm="myrealm", resolver="reso002", action="{0!s}={1!s}".format(ACTION.OTPPIN, ACTIONVALUE.NONE)) g = FakeFlaskG() P = PolicyClass() g.policy_object = P g.audit_object = FakeAudit() options = {"g": g} # user in reso001 fails with empty PIN, since the policy does not # match for him r = auth_otppin(self.fake_check_otp, None, "", options=options, user=my_user_1) self.assertFalse(r) # user in reso002 succeeds with empty PIN, since policy pol1 matches # for him r = auth_otppin(self.fake_check_otp, None, "", options=options, user=my_user_2) self.assertTrue(r) # user in reso002 fails with any PIN, since policy pol1 matches # for him r = auth_otppin(self.fake_check_otp, None, "anyPIN", options=options, user=my_user_2) self.assertFalse(r) delete_policy("pol1") delete_realm("myrealm") delete_resolver("reso001") delete_resolver("reso002")