def test_07_generate_password(self): # test given default characters pass_numeric = generate_password(size=12, characters=string.digits) self.assertTrue(pass_numeric.isdigit()) self.assertEqual(len(pass_numeric), 12) # test requirements, we loop to get some statistics default_chars = string.ascii_uppercase + string.ascii_lowercase + string.digits for i in range(10): password_req = generate_password(size=3, characters=default_chars, requirements=["AB", "12"]) # a character from each requirement must be found self.assertTrue( any(char in "AB" for char in password_req) and any(char in "12" for char in password_req)) self.assertEqual(len(password_req),3) # use letters for base and numbers for requirements # this cannot be achieved with a pin policy password = generate_password(size=10, characters=string.ascii_letters, requirements=[string.digits, string.digits, string.digits]) self.assertEqual(10, len(password)) self.assertEqual(3, sum(c.isdigit() for c in password)) self.assertEqual(7, sum(c.isalpha() for c in password)) # requirements define the minimum length of a password password = generate_password(size=0, characters='ABC', requirements=['1', '2', '3']) self.assertEqual(3, len(password)) # empty characters variable raises an IndexError self.assertRaises(IndexError, generate_password, characters='') # negative size without requirements results in an empty password password = generate_password(size=-1) self.assertEqual(password, '')
def update(self, param): """ This method is called during the initialization process. :param param: parameters from the token init :type param: dict :return: None """ if "genkey" in param: # We do not need the genkey! We generate anyway. # Otherwise genkey and otpkey will raise an exception in # PasswordTokenClass del param["genkey"] param["otpkey"] = generate_password(size=self.otp_len) PasswordTokenClass.update(self, param)
def update(self, param): """ This method is called during the initialization process. :param param: parameters from the token init :type param: dict :return: None """ if "genkey" in param: # We do not need the genkey! We generate anyway. # Otherwise genkey and otpkey will raise an exception in # PasswordTokenClass del param["genkey"] param["otpkey"] = generate_password(size=self.otp_len) PasswordTokenClass.update(self, param)
def create_recoverycode(user, email=None, expiration_seconds=3600, recoverycode=None, base_url=""): """ Create and send a password recovery code :param user: User for whom the password reset code should be sent :type user: User Object :param email: The optional email of the user :param recoverycode: Only used for testing purpose :return: bool """ base_url = base_url.strip("recover") base_url += "#" recoverycode = recoverycode or generate_password(size=24) hash_code = hash_with_pepper(recoverycode) # send this recoverycode # pwreset = PasswordReset(hash_code, username=user.login, realm=user.realm, expiration_seconds=expiration_seconds) pwreset.save() res = False if not user: raise UserError("User required for recovery token.") user_email = user.info.get("email") if email and email.lower() != user_email.lower(): raise UserError("The email does not match the users email.") identifier = get_from_config("recovery.identifier") if identifier: # send email r = send_email_identifier( identifier, user_email, "Your password reset", BODY.format(base_url, user.login, user.realm, recoverycode)) if not r: raise privacyIDEAError("Failed to send email. {0!s}".format(r)) else: raise ConfigAdminError("Missing configuration " "recovery.identifier.") res = True return res
def create_recoverycode(user, email=None, expiration_seconds=3600, recoverycode=None, base_url=""): """ Create and send a password recovery code :param user: User for whom the password reset code should be sent :type user: User Object :param email: The optional email of the user :param recoverycode: Only used for testing purpose :return: bool """ base_url = base_url.strip("recover") base_url += "#" recoverycode = recoverycode or generate_password(size=24) hash_code = hash_with_pepper(recoverycode) # send this recoverycode # pwreset = PasswordReset(hash_code, username=user.login, realm=user.realm, expiration_seconds=expiration_seconds) pwreset.save() res = False if not user: raise UserError("User required for recovery token.") user_email = user.info.get("email") if email and email.lower() != user_email.lower(): raise UserError("The email does not match the users email.") identifier = get_from_config("recovery.identifier") if identifier: # send email r = send_email_identifier(identifier, user_email, "Your password reset", BODY.format(base_url, user.login, user.realm, recoverycode)) if not r: raise privacyIDEAError("Failed to send email. {0!s}".format(r)) else: raise ConfigAdminError("Missing configuration " "recovery.identifier.") res = True return res
def create_token(serial, username): app = create_app(config_name="production", config_file="/etc/privacyidea/pi.cfg", silent=True) with app.app_context(): # Set global values params = {"type": TOKENTYPE} if username: user = User(username, REALM) else: user = User() if serial: params["serial"] = serial password = generate_password(size=PW_LEN, characters=BASE58) params["otplen"] = PW_LEN params["otpkey"] = password tok = init_token(params, user) return tok.token.serial, password
def do(self, action, options=None): """ This method executes the defined action in the given event. :param action: :param options: Contains the flask parameters g, request, response and the handler_def configuration :type options: dict :return: """ ret = True g = options.get("g") request = options.get("request") response = options.get("response") content = self._get_response_content(response) handler_def = options.get("handler_def") handler_options = handler_def.get("options", {}) serial = request.all_data.get("serial") or \ content.get("detail", {}).get("serial") or \ g.audit_object.audit_data.get("serial") if action.lower() in [ACTION_TYPE.SET_TOKENREALM, ACTION_TYPE.SET_DESCRIPTION, ACTION_TYPE.DELETE, ACTION_TYPE.DISABLE, ACTION_TYPE.ENABLE, ACTION_TYPE.UNASSIGN, ACTION_TYPE.SET_VALIDITY, ACTION_TYPE.SET_COUNTWINDOW, ACTION_TYPE.SET_TOKENINFO, ACTION_TYPE.SET_FAILCOUNTER, ACTION_TYPE.CHANGE_FAILCOUNTER, ACTION_TYPE.SET_RANDOM_PIN, ACTION_TYPE.DELETE_TOKENINFO]: if serial: log.info("{0!s} for token {1!s}".format(action, serial)) if action.lower() == ACTION_TYPE.SET_TOKENREALM: realm = handler_options.get("realm") only_realm = is_true(handler_options.get("only_realm")) # Set the realm.. log.info("Setting realm of token {0!s} to {1!s}".format( serial, realm)) # Add the token realm set_realms(serial, [realm], add=not only_realm) elif action.lower() == ACTION_TYPE.SET_RANDOM_PIN: # If for any reason we have no value, we default to 6 length = int(handler_options.get("length") or 6) pin = generate_password(size=length) if set_pin(serial, pin): content.setdefault("detail", {})["pin"] = pin options.get("response").data = json.dumps(content) elif action.lower() == ACTION_TYPE.DELETE: remove_token(serial=serial) elif action.lower() == ACTION_TYPE.DISABLE: enable_token(serial, enable=False) elif action.lower() == ACTION_TYPE.ENABLE: enable_token(serial, enable=True) elif action.lower() == ACTION_TYPE.UNASSIGN: unassign_token(serial) elif action.lower() == ACTION_TYPE.SET_DESCRIPTION: description = handler_options.get("description") or "" description, td = parse_time_offset_from_now(description) s_now = (datetime.datetime.now(tzlocal()) + td).strftime( AUTH_DATE_FORMAT) set_description(serial, description.format( current_time=s_now, now=s_now, client_ip=g.client_ip, ua_browser=request.user_agent.browser, ua_string=request.user_agent.string)) elif action.lower() == ACTION_TYPE.SET_COUNTWINDOW: set_count_window(serial, int(handler_options.get("count window", 50))) elif action.lower() == ACTION_TYPE.SET_TOKENINFO: tokeninfo = handler_options.get("value") or "" tokeninfo, td = parse_time_offset_from_now(tokeninfo) s_now = (datetime.datetime.now(tzlocal()) + td).strftime( AUTH_DATE_FORMAT) try: username = request.User.loginname realm = request.User.realm except Exception: username = "******" realm = "N/A" add_tokeninfo(serial, handler_options.get("key"), tokeninfo.format( current_time=s_now, now=s_now, client_ip=g.client_ip, username=username, realm=realm, ua_browser=request.user_agent.browser, ua_string=request.user_agent.string)) elif action.lower() == ACTION_TYPE.DELETE_TOKENINFO: delete_tokeninfo(serial, handler_options.get("key")) elif action.lower() == ACTION_TYPE.SET_VALIDITY: start_date = handler_options.get(VALIDITY.START) end_date = handler_options.get(VALIDITY.END) if start_date: d = parse_date(start_date) set_validity_period_start(serial, None, d.strftime(DATE_FORMAT)) if end_date: d = parse_date(end_date) set_validity_period_end(serial, None, d.strftime(DATE_FORMAT)) elif action.lower() == ACTION_TYPE.SET_FAILCOUNTER: try: set_failcounter(serial, int(handler_options.get("fail counter"))) except Exception as exx: log.warning("Misconfiguration: Failed to set fail " "counter!") elif action.lower() == ACTION_TYPE.CHANGE_FAILCOUNTER: try: token_obj = get_one_token(serial=serial) token_obj.set_failcount( token_obj.token.failcount + int(handler_options.get("change fail counter"))) except Exception as exx: log.warning("Misconfiguration: Failed to increase or decrease fail " "counter!") else: log.info("Action {0!s} requires serial number. But no serial " "number could be found in request.") if action.lower() == ACTION_TYPE.INIT: log.info("Initializing new token") init_param = {"type": handler_options.get("tokentype"), "genkey": 1, "realm": handler_options.get("realm", "")} user = None if is_true(handler_options.get("user")): user = self._get_tokenowner(request) tokentype = handler_options.get("tokentype") # Some tokentypes need additional parameters if handler_options.get("additional_params"): add_params = yaml.safe_load(handler_options.get("additional_params")) if type(add_params) == dict: init_param.update(add_params) if tokentype == "sms": if handler_options.get("dynamic_phone"): init_param["dynamic_phone"] = 1 else: init_param['phone'] = user.get_user_phone( phone_type='mobile', index=0) if not init_param['phone']: log.warning("Enrolling SMS token. But the user " "{0!r} has no mobile number!".format(user)) if handler_options.get("sms_identifier"): init_param["sms.identifier"] = handler_options.get("sms_identifier") elif tokentype == "email": if handler_options.get("dynamic_email"): init_param["dynamic_email"] = 1 else: init_param['email'] = user.info.get("email", "") if not init_param['email']: log.warning("Enrolling EMail token. But the user {0!s}" "has no email address!".format(user)) if handler_options.get("smtp_identifier"): init_param["email.identifier"] = handler_options.get("smtp_identifier") elif tokentype == "motp": init_param['motppin'] = handler_options.get("motppin") t = init_token(param=init_param, user=user) log.info("New token {0!s} enrolled.".format(t.token.serial)) return ret
def test_20_pin_policy(self): # Unspecified character specifier self.assertRaises(PolicyError, check_pin_contents, "1234", "+o") r, c = check_pin_contents("1234", "n") self.assertTrue(r) r, c = check_pin_contents(r"[[[", "n") self.assertFalse(r) r, c = check_pin_contents(r"[[[", "c") self.assertFalse(r) r, c = check_pin_contents(r"[[[", "s") self.assertTrue(r) # check the validation of a generated password with square brackets password = generate_password(size=3, requirements=['[', '[', '[']) r, c = check_pin_contents(password, "s") self.assertTrue(r) r, c = check_pin_contents("abc", "nc") self.assertFalse(r) self.assertEqual( "Missing character in PIN: {}".format(CHARLIST_CONTENTPOLICY['n']), c) r, c = check_pin_contents("123", "nc") self.assertFalse(r) self.assertEqual( r"Missing character in PIN: {}".format( CHARLIST_CONTENTPOLICY['c']), c) r, c = check_pin_contents("123", "ncs") self.assertFalse(r) self.assertTrue( r"Missing character in PIN: {}".format( CHARLIST_CONTENTPOLICY['c'] in c), c) self.assertTrue( r"Missing character in PIN: {}".format( CHARLIST_CONTENTPOLICY['s'] in c), c) r, c = check_pin_contents("1234", "") self.assertFalse(r) self.assertEqual(c, "No policy given.") # check for either number or character r, c = check_pin_contents("1234", "+cn") self.assertTrue(r) r, c = check_pin_contents("1234xxxx", "+cn") self.assertTrue(r) r, c = check_pin_contents("xxxx", "+cn") self.assertTrue(r) self.assertTrue(check_pin_contents("test1234", "+cn")[0]) self.assertTrue(check_pin_contents("test12$$", "+cn")[0]) self.assertTrue(check_pin_contents("test12", "+cn")[0]) self.assertTrue(check_pin_contents("1234", "+cn")[0]) r, c = check_pin_contents("@@@@", "+cn") self.assertFalse(r) self.assertEqual( c, "Missing character in PIN: {}{}".format( CHARLIST_CONTENTPOLICY['c'], CHARLIST_CONTENTPOLICY['n'])) # check for exclusion # No special character r, c = check_pin_contents("1234", "-s") self.assertTrue(r) r, c = check_pin_contents("1234aaaa", "-s") self.assertTrue(r) r, c = check_pin_contents("1234aaaa//", "-s") self.assertFalse(r) # A pin that falsely contains a number r, c = check_pin_contents("1234aaa", "-sn") self.assertFalse(r) self.assertEqual(c, "Not allowed character in PIN!") r, c = check_pin_contents("///aaa", "-sn") self.assertFalse(r) # A pin without a number and without a special r, c = check_pin_contents("xxxx", "-sn") self.assertTrue(r) r, c = check_pin_contents("1234@@@@", "-c") self.assertTrue(r) # A pin with only digits allowed r, c = check_pin_contents("1234", "-cs") self.assertTrue(r) r, c = check_pin_contents("a1234", "-cs") self.assertFalse(r) # A pin with only a specified list of chars r, c = check_pin_contents("1234111", "[1234]") self.assertTrue(r) r, c = check_pin_contents("12345", "[1234]") self.assertFalse(r)