def autoassign(request, response): """ This decorator decorates the function /validate/check. Depending on ACTION.AUTOASSIGN it checks if the user has no token and if the given OTP-value matches a token in the users realm, that is not yet assigned to any user. If a token can be found, it assigns the token to the user also taking into account ACTION.MAXTOKENUSER and ACTION.MAXTOKENREALM. :return: """ content = json.loads(response.data) # check, if the authentication was successful, then we need to do nothing if content.get("result").get("value") is False: user_obj = get_user_from_param(request.all_data) password = request.all_data.get("pass", "") if user_obj.login and user_obj.realm: # If there is no user in the request (because it is a serial # authentication request) we immediately bail out # check if the policy is defined policy_object = g.policy_object autoassign_bool = policy_object.get_policies( action=ACTION.AUTOASSIGN, scope=SCOPE.ENROLL, user=user_obj.login, realm=user_obj.realm, client=request.remote_addr, active=True, ) if len(autoassign_bool) >= 1: # check if the user has no token if get_tokens(user=user_obj, count=True) == 0: # Check is the token would match # get all unassigned tokens in the realm and look for a matching OTP: realm_tokens = get_tokens(realm=user_obj.realm, assigned=False) for token_obj in realm_tokens: (res, pin, otp) = token_obj.split_pin_pass(password) # TODO: What do we want to do with the PIN? # Check it against userstore? if token_obj.check_otp(otp) >= 0: # we found a matching token # check MAXTOKENUSER and MAXTOKENREALM check_max_token_user(request=request) check_max_token_realm(request=request) # Assign token assign_token(serial=token_obj.token.serial, user=user_obj, pin=pin) # Set the response to true content.get("result")["value"] = True # Set the serial number if not content.get("detail"): content["detail"] = {} content.get("detail")["serial"] = token_obj.token.serial content.get("detail")["type"] = token_obj.type content.get("detail")["message"] = "Token " "assigned to " "user via " "Autoassignment" response.data = json.dumps(content) break return response
def test_43_encryptpin(self): serial = "ENC01" # encrypt pin on init init_token({ "serial": serial, "genkey": 1, "pin": "Hallo", "encryptpin": True }) tokenobj = get_tokens(serial=serial)[0] self.assertEqual(tokenobj.token.pin_hash[0:2], "@@") # set a hashed pin set_pin(serial, "test", encrypt_pin=False) tokenobj = get_tokens(serial=serial)[0] self.assertTrue(tokenobj.token.pin_hash[0:2] != "@@") # set an encrypted PIN set_pin(serial, "test", encrypt_pin=True) tokenobj = get_tokens(serial=serial)[0] self.assertEqual(tokenobj.token.pin_hash[0:2], "@@") # assign the token with a PIN assign_token(serial, User(login="******", realm=self.realm1), pin="WellWell", encrypt_pin=True) # check if pinhash starts with "@@" to indicate the encryption tokenobj = get_tokens(serial=serial)[0] self.assertEqual(tokenobj.token.pin_hash[0:2], "@@")
def test_43_encryptpin(self): serial = "ENC01" # encrypt pin on init init_token({"serial": serial, "genkey": 1, "pin": "Hallo", "encryptpin": True}) tokenobj = get_tokens(serial=serial)[0] self.assertEqual(tokenobj.token.pin_hash[0:2], "@@") # set a hashed pin set_pin(serial, "test", encrypt_pin=False) tokenobj = get_tokens(serial=serial)[0] self.assertTrue(tokenobj.token.pin_hash[0:2] != "@@") # set an encrypted PIN set_pin(serial, "test", encrypt_pin=True) tokenobj = get_tokens(serial=serial)[0] self.assertEqual(tokenobj.token.pin_hash[0:2], "@@") # assign the token with a PIN assign_token(serial, User(login="******", realm=self.realm1), pin="WellWell", encrypt_pin=True) # check if pinhash starts with "@@" to indicate the encryption tokenobj = get_tokens(serial=serial)[0] self.assertEqual(tokenobj.token.pin_hash[0:2], "@@")
def test_04_user_can_not_lost_another_token(self): self.authenticate_selfserive_user() assign_token(self.foreign_serial, User("cornelius", self.realm1)) with self.app.test_request_context('/token/lost/%s' % self.foreign_serial, method='POST', headers={'Authorization': self.at_user}): self.assertRaises(TokenAdminError, self.app.full_dispatch_request)
def test_04_user_can_not_lost_another_token(self): self.authenticate_selfserive_user() assign_token(self.foreign_serial, User("cornelius", self.realm1)) with self.app.test_request_context( '/token/lost/{0!s}'.format(self.foreign_serial), method='POST', headers={'Authorization': self.at_user}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 400, res)
def test_04_user_can_not_lost_another_token(self): self.authenticate_selfserive_user() assign_token(self.foreign_serial, User("cornelius", self.realm1)) with self.app.test_request_context('/token/lost/%s' % self.foreign_serial, method='POST', headers={'Authorization': self.at_user}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 400, res)
def reassign_token(serial, username): app = Flask(__name__, static_folder="static", template_folder="static/templates") app.config.from_pyfile("/etc/privacyidea/pi.cfg", silent=True) db = SQLAlchemy() db.init_app(app) with app.app_context(): # Set global values unassign_token(serial) assign_token(serial, User(username, NEW_REALM))
def test_04_user_can_not_delete_another_token(self): self.authenticate_selfservice_user() assign_token(self.foreign_serial, User("cornelius", self.realm1)) with self.app.test_request_context('/token/{0!s}'.format(self.foreign_serial), method='DELETE', headers={'Authorization': self.at_user}): res = self.app.full_dispatch_request() self.assertEqual(res.status_code, 404) response = json.loads(res.data.decode('utf8')) self.assertFalse(response["result"]["status"]) # check if the token still exists! tokenobject_list = get_tokens(serial=self.foreign_serial) self.assertTrue(len(tokenobject_list) == 1, len(tokenobject_list))
def test_04_user_can_not_delete_another_token(self): self.authenticate_selfserive_user() assign_token(self.foreign_serial, User("cornelius", self.realm1)) with self.app.test_request_context( '/token/%s' % self.foreign_serial, method='DELETE', headers={'Authorization': self.at_user}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) response = json.loads(res.data) self.assertFalse( response.get("result").get("value"), response.get("result")) # check if the token still exists! tokenobject_list = get_tokens(serial=self.foreign_serial) self.assertTrue(len(tokenobject_list) == 1, len(tokenobject_list))
def test_04_user_can_not_delete_another_token(self): self.authenticate_selfserive_user() assign_token(self.foreign_serial, User("cornelius", self.realm1)) with self.app.test_request_context('/token/%s' % self.foreign_serial, method='DELETE', headers={'Authorization': self.at_user}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) response = json.loads(res.data) self.assertFalse(response.get("result").get("value"), response.get("result")) # check if the token still exists! tokenobject_list = get_tokens(serial=self.foreign_serial) self.assertTrue(len(tokenobject_list) == 1, len(tokenobject_list))
def test_18_assign_token(self): serial = "ASSTOKEN" user = User("cornelius", resolver=self.resolvername1, realm=self.realm1) tokenobject = init_token({"serial": serial, "otpkey": "1234567890123456"}) r = assign_token(serial, user, pin="1234") self.assertTrue(r) self.assertTrue(tokenobject.token.user_id == "1000", tokenobject.token.user_id) # token already assigned... self.assertRaises(TokenAdminError, assign_token, serial, User("shadow", realm=self.realm1)) # unassign token r = unassign_token(serial) self.assertTrue(r) self.assertTrue(tokenobject.token.user_id == "", tokenobject.token.user_id) remove_token(serial) # assign or unassign a token, that does not exist self.assertRaises(TokenAdminError, assign_token, serial, user) self.assertRaises(TokenAdminError, unassign_token, serial)
def test_04_user_can_not_disable_another_token(self): self.authenticate_selfservice_user() assign_token(self.foreign_serial, User("cornelius", self.realm1)) with self.app.test_request_context( '/token/disable/{0!s}'.format(self.foreign_serial), method='POST', headers={'Authorization': self.at_user}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) response = json.loads(res.data) self.assertFalse( response.get("result").get("value"), response.get("result")) # check if the token still is enabled! tokenobject_list = get_tokens(serial=self.foreign_serial) self.assertTrue(len(tokenobject_list) == 1, len(tokenobject_list)) self.assertTrue(tokenobject_list[0].token.active)
def test_18_assign_token(self): serial = "ASSTOKEN" user = User("cornelius", resolver=self.resolvername1, realm=self.realm1) tokenobject = init_token({ "serial": serial, "otpkey": "1234567890123456" }) r = assign_token(serial, user, pin="1234") self.assertTrue(r) self.assertTrue(tokenobject.token.user_id == "1000", tokenobject.token.user_id) # token already assigned... self.assertRaises(TokenAdminError, assign_token, serial, User("shadow", realm=self.realm1)) # unassign token r = unassign_token(serial) self.assertTrue(r) self.assertTrue(tokenobject.token.user_id == "", tokenobject.token.user_id) remove_token(serial) # assign or unassign a token, that does not exist self.assertRaises(TokenAdminError, assign_token, serial, user) self.assertRaises(TokenAdminError, unassign_token, serial)
def test_37_challenge(self): # We create a challenge by first sending the PIN of the HOTP token # then we answer the challenge by sending the OTP. num1 = Challenge.query.filter(Challenge.serial == "hotptoken").count() # The correct PIN will create a challenge r, reply = check_serial_pass("hotptoken", "hotppin") self.assertTrue(r is False, r) num2 = Challenge.query.filter(Challenge.serial == "hotptoken").count() # check that the challenge is created self.assertTrue(num1 + 1 == num2, (num1, num2)) self.assertTrue(type(reply) == dict, reply) transaction_id = reply.get("transaction_id", "") self.assertTrue(len(transaction_id) > 10, reply) # Challenge Response, with the transaction id r, reply = check_serial_pass("hotptoken", "436521", {"transaction_id": transaction_id}) self.assertTrue(r) self.assertTrue( reply.get("message") == "Found matching challenge", reply) # create two tokens with the same OTP Key and the same PIN, so # this token will create the same challenge # creating a challenge will not work! tokenobject = init_token({ "serial": "CHALL001", "type": "hotp", "otpkey": self.otpkey }) tokenobject = init_token({ "serial": "CHALL002", "type": "hotp", "otpkey": self.otpkey }) user = User("cornelius", realm=self.realm1) assign_token("CHALL001", user) assign_token("CHALL002", user) set_pin("CHALL001", "challpin") set_pin("CHALL002", "challpin") r, reply = check_user_pass(user, "challpin") self.assertFalse(r) self.assertTrue( "Multiple tokens to create a challenge found" in reply.get("message"), reply) remove_token("CHALL001") remove_token("CHALL002")
def test_04_user_can_not_disable_another_token(self): self.authenticate_selfservice_user() assign_token(self.foreign_serial, User("cornelius", self.realm1)) with self.app.test_request_context('/token/disable/{0!s}'.format( self.foreign_serial), method='POST', headers={'Authorization': self.at_user}): res = self.app.full_dispatch_request() self.assertTrue(res.status_code == 200, res) response = json.loads(res.data) self.assertFalse(response.get("result").get("value"), response.get("result")) # check if the token still is enabled! tokenobject_list = get_tokens(serial=self.foreign_serial) self.assertTrue(len(tokenobject_list) == 1, len(tokenobject_list)) self.assertTrue(tokenobject_list[0].token.active)
def test_37_challenge(self): # We create a challenge by first sending the PIN of the HOTP token # then we answer the challenge by sending the OTP. num1 = Challenge.query.filter(Challenge.serial == "hotptoken").count() # The correct PIN will create a challenge r, reply = check_serial_pass("hotptoken", "hotppin") self.assertTrue(r is False, r) num2 = Challenge.query.filter(Challenge.serial == "hotptoken").count() # check that the challenge is created self.assertTrue(num1 + 1 == num2, (num1, num2)) self.assertTrue(type(reply) == dict, reply) transaction_id = reply.get("transaction_id","") self.assertTrue(len(transaction_id) > 10, reply) # Challenge Response, with the transaction id r, reply = check_serial_pass("hotptoken", "436521", {"transaction_id": transaction_id}) self.assertTrue(r) self.assertTrue(reply.get("message") == "Found matching challenge", reply) # create two tokens with the same OTP Key and the same PIN, so # this token will create the same challenge # creating a challenge will not work! tokenobject = init_token({"serial": "CHALL001", "type": "hotp", "otpkey": self.otpkey}) tokenobject = init_token({"serial": "CHALL002", "type": "hotp", "otpkey": self.otpkey}) user = User("cornelius", realm=self.realm1) assign_token("CHALL001", user) assign_token("CHALL002", user) set_pin("CHALL001", "challpin") set_pin("CHALL002", "challpin") r, reply = check_user_pass(user, "challpin") self.assertFalse(r) self.assertTrue("Multiple tokens to create a challenge found" in reply.get("message"), reply) remove_token("CHALL001") remove_token("CHALL002")
def test_33_lost_token(self): # create a token with a user serial1 = "losttoken" tobject1 = init_token({"serial": serial1, "genkey": 1}) r = assign_token(serial1, User(login="******", realm=self.realm1)) self.assertTrue(r, r) # call the losttoken self.assertRaises(TokenAdminError, lost_token, "doesnotexist") r = lost_token(serial1) """ r = {'end_date': '16/12/14 23:59', 'pin': True, 'valid_to': 'xxxx', 'init': True, 'disable': 1, 'user': True, 'serial': 'lostlosttoken', 'password': '******'} """ self.assertTrue(r.get("pin") == True, r) self.assertTrue(r.get("init") == True, r) self.assertTrue(r.get("user") == True, r) self.assertTrue(r.get("serial") == "lost%s" % serial1, r) remove_token("losttoken") remove_token("lostlosttoken")
def assign_user(resolver, realm, username, email, givenname, surname, serial, pin): app = create_app(config_name="production", config_file="/etc/privacyidea/pi.cfg", silent=True) with app.app_context(): # User operations try: print("+ Processing user {0!s} in {1!s}/{2!s}.".format(username, resolver, realm)) user_obj = User(username, realm, resolver=resolver) except UserError as err: sys.stderr.write(" +-- Failed finding user: {0!s}.\n".format(err)) return if not user_obj.exist(): print(" +- Creating user {0!s} in {1!s}/{2!s}.".format(username, resolver, realm)) try: create_user(resolver, {"username": username, "email": email, "givenname": givenname, "surname": surname}, password="") user_obj = User(username, realm, resolver=resolver) except UserError as err: sys.stderr.write("+-- Failed to create user: {0!s}.\n".format(err)) return except Exception as err: sys.stderr.write("+-- Failed to create user: {0!s}.\n".format(err)) return # Token operations try: print(" +- Processing token {0!s}".format(serial)) t = assign_token(serial, user_obj, pin) print(" +-- Assigned token to user {0!s}.".format(user_obj)) except TokenAdminError as err: sys.stderr.write(" +-- Failed assigning token {0!s}: {1!s}.\n".format(serial, err)) except ResourceNotFoundError as err: sys.stderr.write(" +-- Failed assigning token {0!s}: {1!s}.\n".format(serial, err))
def test_32_copy_token_user(self): serial1 = "tcopy1" tobject1 = init_token({"serial": serial1, "genkey": 1}) r = assign_token(serial1, User(login="******", realm=self.realm1)) self.assertTrue(r, r) serial2 = "tcopy2" tobject2 = init_token({"serial": serial2, "genkey": 1}) r = copy_token_user(serial1, serial2) assert isinstance(tobject2, TokenClass) self.assertTrue(tobject2.token.user_id == "1000", tobject2.token.user_id) self.assertTrue(tobject2.token.resolver == self.resolvername1) self.assertTrue(tobject2.token.resolver_type == "passwdresolver") # check if the realms where copied: self.assertTrue(tobject2.get_realms() == [self.realm1]) # check exceptions self.assertRaises(TokenAdminError, copy_token_user, serial1, "none") self.assertRaises(TokenAdminError, copy_token_user, "none", serial2) remove_token(serial1) remove_token(serial2)
def autoassign(request, response): """ This decorator decorates the function /validate/check. Depending on ACTION.AUTOASSIGN it checks if the user has no token and if the given OTP-value matches a token in the users realm, that is not yet assigned to any user. If a token can be found, it assigns the token to the user also taking into account ACTION.MAXTOKENUSER and ACTION.MAXTOKENREALM. :return: """ content = json.loads(response.data) # check, if the authentication was successful, then we need to do nothing if content.get("result").get("value") is False: user_obj = request.User #user_obj = get_user_from_param(request.all_data) password = request.all_data.get("pass", "") if user_obj.login and user_obj.realm: # If there is no user in the request (because it is a serial # authentication request) we immediately bail out # check if the policy is defined policy_object = g.policy_object autoassign_values = policy_object.\ get_action_values(action=ACTION.AUTOASSIGN, scope=SCOPE.ENROLL, user=user_obj.login, resolver=user_obj.resolver, realm=user_obj.realm, client=g.client_ip) if len(autoassign_values) > 1: raise PolicyError("Contradicting Autoassign policies.") # check if the user has no token if autoassign_values and get_tokens(user=user_obj, count=True) == 0: # Check is the token would match # get all unassigned tokens in the realm and look for # a matching OTP: realm_tokens = get_tokens(realm=user_obj.realm, assigned=False) for token_obj in realm_tokens: (res, pin, otp) = token_obj.split_pin_pass(password) if res: pin_check = True if autoassign_values[0] == \ AUTOASSIGNVALUE.USERSTORE: # If the autoassign policy is set to userstore, # we need to check against the userstore. pin_check = user_obj.check_password(pin) if pin_check: otp_check = token_obj.check_otp(otp) if otp_check >= 0: # we found a matching token # check MAXTOKENUSER and MAXTOKENREALM check_max_token_user(request=request) check_max_token_realm(request=request) # Assign token assign_token(serial=token_obj.token.serial, user=user_obj, pin=pin) # Set the response to true content.get("result")["value"] = True # Set the serial number if not content.get("detail"): content["detail"] = {} content.get("detail")["serial"] = \ token_obj.token.serial content.get("detail")["otplen"] = \ token_obj.token.otplen content.get("detail")["type"] = token_obj.type content.get("detail")["message"] = "Token " \ "assigned to " \ "user via " \ "Autoassignment" response.data = json.dumps(content) g.audit_object.log({ "success": True, "action_info": "Token assigned via auto assignment", "serial": token_obj.token.serial }) break return response
def autoassign(request, response): """ This decorator decorates the function /validate/check. Depending on ACTION.AUTOASSIGN it checks if the user has no token and if the given OTP-value matches a token in the users realm, that is not yet assigned to any user. If a token can be found, it assigns the token to the user also taking into account ACTION.MAXTOKENUSER and ACTION.MAXTOKENREALM. :return: """ content = response.json # check, if the authentication was successful, then we need to do nothing if content.get("result").get("value") is False: user_obj = request.User #user_obj = get_user_from_param(request.all_data) password = request.all_data.get("pass", "") if user_obj.login and user_obj.realm: # If there is no user in the request (because it is a serial # authentication request) we immediately bail out # check if the policy is defined autoassign_values = Match.user(g, scope=SCOPE.ENROLL, action=ACTION.AUTOASSIGN, user_object=user_obj).action_values(unique=True, write_to_audit_log=False) # check if the user has no token if autoassign_values and get_tokens(user=user_obj, count=True) == 0: # Check if the token would match # get all unassigned tokens in the realm and look for # a matching OTP: realm_tokens = get_tokens(realm=user_obj.realm, assigned=False) for token_obj in realm_tokens: (res, pin, otp) = token_obj.split_pin_pass(password) if res: pin_check = True if list(autoassign_values)[0] == \ AUTOASSIGNVALUE.USERSTORE: # If the autoassign policy is set to userstore, # we need to check against the userstore. pin_check = user_obj.check_password(pin) if pin_check: otp_check = token_obj.check_otp(otp) if otp_check >= 0: # we found a matching token # check MAXTOKENUSER and MAXTOKENREALM check_max_token_user(request=request) check_max_token_realm(request=request) # Assign token assign_token(serial=token_obj.token.serial, user=user_obj, pin=pin) # Set the response to true content.get("result")["value"] = True # Set the serial number detail = content.setdefault("detail", {}) detail["serial"] = token_obj.token.serial detail["otplen"] = token_obj.token.otplen detail["type"] = token_obj.type detail["message"] = "Token assigned to user via Autoassignment" response.set_data(json.dumps(content)) g.audit_object.log( {"success": True, "info": "Token assigned via auto assignment", "serial": token_obj.token.serial}) # The token was assigned by autoassign. We save the first policy name g.audit_object.add_policy(next(iter(autoassign_values.values()))) break return response
def auth_user_passthru(wrapped_function, user_object, passw, options=None): """ This decorator checks the policy settings of ACTION.PASSTHRU. If the authentication against the userstore is not successful, the wrapped function is called. The wrapped function is usually token.check_user_pass, which takes the arguments (user, passw, options={}) :param wrapped_function: :param user_object: :param passw: :param options: Dict containing values for "g" and "clientip" :return: Tuple of True/False and reply-dictionary """ from privacyidea.lib.token import get_tokens from privacyidea.lib.token import assign_token options = options or {} g = options.get("g") if g: policy_object = g.policy_object pass_thru = Match.user( g, scope=SCOPE.AUTH, action=ACTION.PASSTHRU, user_object=user_object).policies(write_to_audit_log=False) # We only go to passthru, if the user has no tokens! if pass_thru and get_tokens(user=user_object, count=True) == 0: # Ensure that there are no conflicting action values within the same priority policy_object.check_for_conflicts(pass_thru, "passthru") pass_thru_action = pass_thru[0].get("action").get("passthru") policy_name = pass_thru[0].get("name") if pass_thru_action in ["userstore", True]: # Now we need to check the userstore password if user_object.check_password(passw): g.audit_object.add_policy( [p.get("name") for p in pass_thru]) return True, { "message": u"against userstore due to '{!s}'".format(policy_name) } else: # We are doing RADIUS passthru log.info("Forwarding the authentication request to the radius " "server %s" % pass_thru_action) radius = get_radius(pass_thru_action) r = radius.request(radius.config, user_object.login, passw) if r: g.audit_object.add_policy( [p.get("name") for p in pass_thru]) # TODO: here we can check, if the token should be assigned. passthru_assign = Match.user( g, scope=SCOPE.AUTH, action=ACTION.PASSTHRU_ASSIGN, user_object=user_object).action_values(unique=True) messages = [] if passthru_assign: components = list(passthru_assign)[0].split(":") if len(components) >= 2: prepend_pin = components[0] == "pin" otp_length = int(components[int(prepend_pin)]) pin, otp = split_pin_pass(passw, otp_length, prepend_pin) realm_tokens = get_tokens(realm=user_object.realm, assigned=False) window = 100 if len(components) == 3: window = int(components[2]) for token_obj in realm_tokens: otp_check = token_obj.check_otp(otp, window=window) if otp_check >= 0: # We do not check any max tokens per realm or user, # since this very user currently has no token # and the unassigned token already was contained in the user's realm assign_token(serial=token_obj.token.serial, user=user_object, pin=pin) messages.append( u"autoassigned {0!s}".format( token_obj.token.serial)) break else: log.warning( "Wrong value in passthru_assign policy: {0!s}". format(passthru_assign)) messages.append( u"against RADIUS server {!s} due to '{!s}'".format( pass_thru_action, policy_name)) return True, {'message': ",".join(messages)} # If nothing else returned, we return the wrapped function return wrapped_function(user_object, passw, options)
def auth_user_passthru(wrapped_function, user_object, passw, options=None): """ This decorator checks the policy settings of ACTION.PASSTHRU. If the authentication against the userstore is not successful, the wrapped function is called. The wrapped function is usually token.check_user_pass, which takes the arguments (user, passw, options={}) :param wrapped_function: :param user_object: :param passw: :param options: Dict containing values for "g" and "clientip" :return: Tuple of True/False and reply-dictionary """ from privacyidea.lib.token import get_tokens from privacyidea.lib.token import assign_token options = options or {} g = options.get("g") if g: policy_object = g.policy_object clientip = options.get("clientip") pass_thru = policy_object.get_policies(action=ACTION.PASSTHRU, scope=SCOPE.AUTH, realm=user_object.realm, resolver=user_object.resolver, user=user_object.login, client=clientip, active=True, sort_by_priority=True) # We only go to passthru, if the user has no tokens! if pass_thru and get_tokens(user=user_object, count=True) == 0: # Ensure that there are no conflicting action values within the same priority policy_object.check_for_conflicts(pass_thru, "passthru") pass_thru_action = pass_thru[0].get("action").get("passthru") policy_name = pass_thru[0].get("name") if pass_thru_action in ["userstore", True]: # Now we need to check the userstore password if user_object.check_password(passw): g.audit_object.add_policy([p.get("name") for p in pass_thru]) return True, {"message": u"against userstore due to '{!s}'".format( policy_name)} else: # We are doing RADIUS passthru log.info("Forwarding the authentication request to the radius " "server %s" % pass_thru_action) radius = get_radius(pass_thru_action) r = radius.request(radius.config, user_object.login, passw) if r: g.audit_object.add_policy([p.get("name") for p in pass_thru]) # TODO: here we can check, if the token should be assigned. passthru_assign = policy_object.get_action_values(action=ACTION.PASSTHRU_ASSIGN, scope=SCOPE.AUTH, realm=user_object.realm, resolver=user_object.resolver, user=user_object.login, client=clientip, unique=True, audit_data=g.audit_object.audit_data) messages = [] if passthru_assign: components = list(passthru_assign)[0].split(":") if len(components) >= 2: prepend_pin = components[0] == "pin" otp_length = int(components[int(prepend_pin)]) pin, otp = split_pin_pass(passw, otp_length, prepend_pin) realm_tokens = get_tokens(realm=user_object.realm, assigned=False) window = 100 if len(components) == 3: window = int(components[2]) for token_obj in realm_tokens: otp_check = token_obj.check_otp(otp, window=window) if otp_check >= 0: # We do not check any max tokens per realm or user, # since this very user currently has no token # and the unassigned token already was contained in the user's realm assign_token(serial=token_obj.token.serial, user=user_object, pin=pin) messages.append(u"autoassigned {0!s}".format(token_obj.token.serial)) break else: log.warning("Wrong value in passthru_assign policy: {0!s}".format(passthru_assign)) messages.append(u"against RADIUS server {!s} due to '{!s}'".format(pass_thru_action, policy_name)) return True, {'message': ",".join(messages)} # If nothing else returned, we return the wrapped function return wrapped_function(user_object, passw, options)
def assign_user(resolver, realm, username, email, givenname, surname, serial, pin, validity, hard_or_soft): app = create_app(config_name="production", config_file="/etc/privacyidea/pi.cfg", silent=True) with app.app_context(): # User operations try: print("+ Processing user {0!s} in {1!s}/{2!s}.".format( username, resolver, realm)) user_obj = User(username, realm, resolver=resolver) except UserError as err: sys.stderr.write(" +-- Failed finding user: {0!s}.\n".format(err)) return if not user_obj.exist(): # Create new user print(" +- Creating user {0!s} in {1!s}/{2!s}.".format( username, resolver, realm)) try: create_user(resolver, { "username": username, "email": email, "givenname": givenname, "surname": surname }, password="") user_obj = User(username, realm, resolver=resolver) except UserError as err: sys.stderr.write( "+-- Failed to create user: {0!s}.\n".format(err)) return except Exception as err: sys.stderr.write( "+-- Failed to create user: {0!s}.\n".format(err)) return else: # Update existing user print(" +- Updating user {0!s} in {1!s}/{2!s}.".format( username, resolver, realm)) user_obj.update_user_info({ "email": email, "givenname": givenname, "surname": surname }) # Token operations ## Assign token or create registration code if hard_or_soft.strip().upper() == HARDWARE: if serial: # Assign an existing token try: print(" +- Processing token {0!s}".format(serial)) t = assign_token(serial, user_obj, pin) print( " +-- Assigned token to user {0!s}.".format(user_obj)) except TokenAdminError as err: sys.stderr.write( " +-- Failed assigning token {0!s}: {1!s}.\n".format( serial, err)) except ResourceNotFoundError as err: sys.stderr.write( " +-- Failed assigning token {0!s}: {1!s}.\n".format( serial, err)) else: sys.stderr.write( "+-- User {0!s} is supposed to get a hardware token, but no serial defined!" .format(user_obj)) elif hard_or_soft.strip().upper() == SOFTWARE: # Create a registration code, since no serial number is given print(" +- Creating token of type {0!s}.".format(TOKEN_TYPE)) params = { "type": TOKEN_TYPE, "genkey": 1, "user": user_obj.login, "realm": user_obj.realm } r = requests.post('https://localhost/token/init', verify=False, data=params, headers={"Authorization": get_auth_tok()}) if not r.json().get("result").get("status"): sys.stderr.write( " +-- Failed to create token for user {0!s}.".format( user_obj)) else: sys.stderr.write( "+-- Unknown Hard/Soft specifier for user {0!s}: {1!s}".format( user_obj, hard_or_soft)) # Create RADIUS token with validity period print(" +- Creating RADIUS token for user {0!s}.".format(user_obj)) tok = init_token( { "type": "radius", "radius.identifier": RADIUS_IDENTIFIER, "radius.user": user_obj.login }, user=user_obj) for k, v in TOKENINFO.items(): tok.add_tokeninfo(k, v) validity_end = datetime.datetime.now() + datetime.timedelta( int(validity)) tok.set_validity_period_end( validity_end.strftime("%Y-%m-%d %H:%M:00 CET"))
def autoassign(request, response): """ This decorator decorates the function /validate/check. Depending on ACTION.AUTOASSIGN it checks if the user has no token and if the given OTP-value matches a token in the users realm, that is not yet assigned to any user. If a token can be found, it assigns the token to the user also taking into account ACTION.MAXTOKENUSER and ACTION.MAXTOKENREALM. :return: """ content = json.loads(response.data) # check, if the authentication was successful, then we need to do nothing if content.get("result").get("value") is False: user_obj = get_user_from_param(request.all_data) password = request.all_data.get("pass", "") if user_obj.login and user_obj.realm: # If there is no user in the request (because it is a serial # authentication request) we immediately bail out # check if the policy is defined policy_object = g.policy_object autoassign_values = policy_object.\ get_action_values(action=ACTION.AUTOASSIGN, scope=SCOPE.ENROLL, user=user_obj.login, realm=user_obj.realm, client=g.client_ip) if len(autoassign_values) > 1: raise PolicyError("Contradicting Autoassign policies.") # check if the user has no token if autoassign_values and get_tokens(user=user_obj, count=True) == 0: # Check is the token would match # get all unassigned tokens in the realm and look for # a matching OTP: realm_tokens = get_tokens(realm=user_obj.realm, assigned=False) for token_obj in realm_tokens: (res, pin, otp) = token_obj.split_pin_pass(password) if res: pin_check = True if autoassign_values[0] == \ AUTOASSIGNVALUE.USERSTORE: # If the autoassign policy is set to userstore, # we need to check against the userstore. pin_check = user_obj.check_password(pin) if pin_check: otp_check = token_obj.check_otp(otp) if otp_check >= 0: # we found a matching token # check MAXTOKENUSER and MAXTOKENREALM check_max_token_user(request=request) check_max_token_realm(request=request) # Assign token assign_token(serial=token_obj.token.serial, user=user_obj, pin=pin) # Set the response to true content.get("result")["value"] = True # Set the serial number if not content.get("detail"): content["detail"] = {} content.get("detail")["serial"] = \ token_obj.token.serial content.get("detail")["otplen"] = \ token_obj.token.otplen content.get("detail")["type"] = token_obj.type content.get("detail")["message"] = "Token " \ "assigned to " \ "user via " \ "Autoassignment" response.data = json.dumps(content) g.audit_object.log( {"success": True, "action_info": "Token assigned via auto assignment", "serial": token_obj.token.serial}) break return response
def migrate(config_obj): from_app = create_app(config_name="production", config_file=config_obj.PRIVACYIDEA_FROM, silent=True) to_app = create_app(config_name="production", config_file=config_obj.PRIVACYIDEA_TO, silent=True) new_users = [] new_tokens = [] with from_app.app_context(): # find all the users userlist = get_user_list(param=config_obj.MIGRATE_USER_FIND) for user in userlist: if re.match(config_obj.MIGRATE_USER_PATTERN, user.get("username")): new_username = re.sub(config_obj.MIGRATE_USER_PATTERN, config_obj.MIGRATE_USER_REPLACE, user.get("username")) new_user = {"username": new_username, "tokenlist": []} for attr in config_obj.MIGRATE_ATTRIBUTES: new_user[attr] = user.get(attr) tokens = get_tokens( user=User(user.get("username"), realm=config_obj.MIGRATE_USER_FIND.get("realm"))) for token in tokens: new_tokens.append(token_to_dict(token.token)) new_user["tokenlist"].append(token.token.serial) new_users.append(new_user) with to_app.app_context(): # create the new tokens for tok in new_tokens: if config_obj.MIGRATE_SERIAL_PATTERN: tok["serial"] = re.sub(config_obj.MIGRATE_SERIAL_PATTERN, config_obj.MIGRATE_SERIAL_REPLACE, tok["serial"]) info_list = tok.get("info_list") del (tok["info_list"]) toks = get_tokens(serial=tok.get("serial")) if len(toks) > 0: print("New token {0!s} aleady exists.".format( tok.get("serial"))) else: create_token_from_dict(tok, info_list) # create the new users for user in new_users: tokenlist = user.get("tokenlist") del (user["tokenlist"]) ul = get_user_list({ "username": user.get("username"), "realm": config_obj.TO_REALM, "resolver": config_obj.TO_RESOLVER }) if not ul: uid = create_user(config_obj.TO_RESOLVER, user) print("Created user {0!s}".format(uid)) else: print("User already exists!") user_obj = User(login=user.get("username"), realm=config_obj.TO_REALM, resolver=config_obj.TO_RESOLVER) # Assign token for serial in tokenlist: serial = re.sub(config_obj.MIGRATE_SERIAL_PATTERN, config_obj.MIGRATE_SERIAL_REPLACE, serial) print("Assigning token {0!s} to user {1!s}".format( serial, user_obj)) try: assign_token(serial, user_obj) except Exception: print( "Error assigning token - probably the token is already assigned." )
def test_02_validate(self): # assign token to user r = assign_token(self.serial, User("cornelius", self.realm1), pin="u2f") self.assertEqual(r, True) # Issue challenge with self.app.test_request_context('/validate/check', method='POST', data={"user": "******"+self.realm1, "pass": "******"}): res = self.app.full_dispatch_request() self.assertEqual(res.status_code, 200) result = json.loads(res.data).get("result") detail = json.loads(res.data).get("detail") self.assertEqual(result.get("value"), False) transaction_id = detail.get("transaction_id") self.assertEqual(len(transaction_id), len('01350277175811850842')) self.assertTrue("Please confirm with your U2F token" in detail.get("message"), detail.get("message")) attributes = detail.get("attributes") u2f_sign_request = attributes.get("u2fSignRequest") self.assertTrue("appId" in u2f_sign_request) app_id = u2f_sign_request.get("appId") self.assertTrue("challenge" in u2f_sign_request) challenge = u2f_sign_request.get("challenge") self.assertTrue("keyHandle" in u2f_sign_request) key_handle = u2f_sign_request.get("keyHandle") self.assertEqual(u2f_sign_request.get("version"), "U2F_V2") # private key from the registration example privkey = "9a9684b127c5e3a706d618c86401c7cf6fd827fd0bc18d24b0eb842e36d16df1" counter = 1 client_data = '{"typ":"navigator.id.getAssertion",' \ '"challenge":"%s","cid_pubkey":{"kty":"EC",' \ '"crv":"P-256",' \ '"x":"HzQwlfXX7Q4S5MtCCnZUNBw3RMzPO9tOyWjBqRl4tJ8",' \ '"y":"XVguGFLIZx1fXg3wNqfdbn75hi4-_7-BxhMljw42Ht4"},' \ '"origin":"%s"}' % (challenge, app_id) signature_hex = sign_challenge(privkey, app_id, client_data, counter) signature_data_hex = "0100000001" + signature_hex signature_data_url = url_encode(binascii.unhexlify(signature_data_hex)) client_data_url = url_encode(client_data) # Send the response. Unfortunately it does not fit to the # registration, so we create a BadSignatureError with self.app.test_request_context('/validate/check', method='POST', data={"user": "******", "realm": self.realm1, "pass": "", "transaction_id": transaction_id, "clientdata": client_data_url, "signaturedata": signature_data_url}): res = self.app.full_dispatch_request() self.assertEqual(res.status_code, 200) result = json.loads(res.data).get("result") detail = json.loads(res.data).get("detail") self.assertEqual(result.get("status"), True) self.assertEqual(result.get("value"), False)
def test_02_validate(self): # assign token to user r = assign_token(self.serial, User("cornelius", self.realm1), pin="u2f") self.assertEqual(r, True) # Issue challenge with self.app.test_request_context('/validate/check', method='POST', data={ "user": "******" + self.realm1, "pass": "******" }): res = self.app.full_dispatch_request() self.assertEqual(res.status_code, 200) result = json.loads(res.data).get("result") detail = json.loads(res.data).get("detail") self.assertEqual(result.get("value"), False) transaction_id = detail.get("transaction_id") self.assertEqual(len(transaction_id), len('01350277175811850842')) self.assertTrue( "Please confirm with your U2F token" in detail.get("message"), detail.get("message")) attributes = detail.get("attributes") u2f_sign_request = attributes.get("u2fSignRequest") self.assertTrue("appId" in u2f_sign_request) app_id = u2f_sign_request.get("appId") self.assertTrue("challenge" in u2f_sign_request) challenge = u2f_sign_request.get("challenge") self.assertTrue("keyHandle" in u2f_sign_request) key_handle = u2f_sign_request.get("keyHandle") self.assertEqual(u2f_sign_request.get("version"), "U2F_V2") # private key from the registration example privkey = "9a9684b127c5e3a706d618c86401c7cf6fd827fd0bc18d24b0eb842e36d16df1" counter = 1 client_data = '{"typ":"navigator.id.getAssertion",' \ '"challenge":"%s","cid_pubkey":{"kty":"EC",' \ '"crv":"P-256",' \ '"x":"HzQwlfXX7Q4S5MtCCnZUNBw3RMzPO9tOyWjBqRl4tJ8",' \ '"y":"XVguGFLIZx1fXg3wNqfdbn75hi4-_7-BxhMljw42Ht4"},' \ '"origin":"%s"}' % (challenge, app_id) signature_hex = sign_challenge(privkey, app_id, client_data, counter) signature_data_hex = "0100000001" + signature_hex signature_data_url = url_encode(binascii.unhexlify(signature_data_hex)) client_data_url = url_encode(client_data) # Send the response. Unfortunately it does not fit to the # registration, so we create a BadSignatureError with self.app.test_request_context('/validate/check', method='POST', data={ "user": "******", "realm": self.realm1, "pass": "", "transaction_id": transaction_id, "clientdata": client_data_url, "signaturedata": signature_data_url }): res = self.app.full_dispatch_request() self.assertEqual(res.status_code, 200) result = json.loads(res.data).get("result") detail = json.loads(res.data).get("detail") self.assertEqual(result.get("status"), True) self.assertEqual(result.get("value"), False)