def test_cookie_contains_password(self): self.contest.ip_autologin = False # Cookies are of no use if one cannot login by password. self.contest.allow_password_authentication = False self.assertFailure() # The cookie works with all methods as it holds the plaintext password. self.contest.allow_password_authentication = True self.user.password = hash_password("mypass", method="bcrypt") self.assertSuccessAndCookieRefreshed() self.user.password = hash_password("mypass", method="plaintext") self.assertSuccessAndCookieRefreshed() # Cookies contain the password, which is validated every time. self.user.password = build_password("newpass") self.assertFailure() # Contest-specific passwords take precedence over global ones. self.participation.password = build_password("mypass") self.assertSuccessAndCookieRefreshed() # And they do so in the negative case too. self.user.password = build_password("mypass") self.participation.password = build_password("newpass") self.assertFailure()
def get_password(self, dest, old_password, allow_unset): """Parse a (possibly hashed) password. Parse the value of the password and the method that should be used to hash it (if any) and fill it into the given destination making sure that a hashed password can be left unchanged and, if allowed, unset. dest (dict): a place to store the result in. old_password (string|None): the current password for the object if any, with a "<method>:" prefix. allow_unset (bool): whether the password is allowed to be left unset, which is represented as a value of None in dest. """ # The admin leaving the password field empty could mean one of # two things: they want the password to be unset (this applies # to participations only and it means they inherit the user's # password) or, if a password was set and it was hashed, they # want to keep using that old password. We distinguish between # the two essentially by looking at the method: an empty # plaintext means "unset", an empty hashed means "keep". # Find out whether a password was set and whether it was # plaintext or hashed. if old_password is not None: try: old_method, _ = parse_authentication(old_password) except ValueError: # Treated as if no password was set. old_method = None else: old_method = None password = self.get_argument("password") method = self.get_argument("method") # If a password is given, we use that. if len(password) > 0: dest["password"] = hash_password(password, method) # If the password was set and was hashed, and the admin kept # the method unchanged and didn't specify anything, they must # have meant to keep the old password unchanged. elif old_method is not None and old_method != "plaintext" \ and method == old_method: # Since the content of dest overwrites the current values # of the participation, by not adding anything to dest we # cause the current values to be kept. pass # Otherwise the fact that the password is empty means that the # admin wants the password to be unset. elif allow_unset: dest["password"] = None # Or that they really mean the password to be the empty string. else: dest["password"] = hash_password("", method)
def _admin_attrs(handler): """Return a dictionary with the arguments to define an admin handler (BaseHandler): the handler receiving the arguments. return (dict): a dictionary with the arguments to define an admin, based on those passed to handler. """ attrs = {} handler.get_string(attrs, "username", empty=None) handler.get_string(attrs, "name", empty=None) assert attrs.get("username") is not None, "No username specified." assert attrs.get("name") is not None, "No admin name specified." # Get the password and translate it to an authentication, if present. handler.get_string(attrs, "password", empty=None) if attrs['password'] is not None: attrs["authentication"] = hash_password(attrs["password"]) del attrs["password"] handler.get_bool(attrs, "permission_all") handler.get_bool(attrs, "permission_messaging") handler.get_bool(attrs, "enabled") return attrs
def add_admin(username, password=None): logger.info("Creating the admin on the database.") generated = False if password == EmptyPassword.GENERATE or password is None: password = generate_random_password() generated = True elif password == EmptyPassword.PROMPT: password = getpass.getpass() admin = Admin(username=username, authentication=hash_password(password), name=username, permission_all=True) try: with SessionGen() as session: session.add(admin) session.commit() except IntegrityError: logger.error("An admin with the given username already exists.") return False if generated: logger.info("Admin with complete access added. " "Login with username %s and password %s", username, password) else: logger.info("Admin with complete access added. " "Login with username %s and supplied password", username) return True
def update_password(username, password, method, is_hashed): logger.info("Updating user password in the database.") if password is None: return False # shell will interfere with special characters, so a hashed string must be protected by # single quotes. We must remove them if password[0] == "'": password[0] = ' ' if password[-1] == "'": password[-1] = ' ' password = password.strip() if is_hashed: stored_password = build_password(password, method) else: stored_password = hash_password(password, method) with SessionGen() as session: user = session.query(User)\ .filter(User.username == username).first() if user is None: logger.error("User %s does not exist.", username) return False session.query(User).filter(User.username == username).\ update({"password": stored_password}, synchronize_session="fetch") session.commit() logger.info("User %s password updated, method=%s. " % (username, method)) return True
def add_user(first_name, last_name, username, password, use_bcrypt, is_raw, email, timezone, preferred_languages): logger.info("Creating the user in the database.") if password is None: password = generate_random_password_with_method() elif not is_raw: method = "bcrypt" if use_bcrypt else "text" password = hash_password(password, method) if preferred_languages is None or preferred_languages == "": preferred_languages = "[]" else: preferred_languages = \ "[" + ",".join("\"" + lang + "\"" for lang in preferred_languages.split(",")) + "]" user = User(first_name=first_name, last_name=last_name, username=username, password=password, email=email, timezone=timezone, preferred_languages=preferred_languages) try: with SessionGen() as session: session.add(user) session.commit() except IntegrityError: logger.error("A user with the given username already exists.") return False logger.info("User added. " "Use AddParticipation to add this user to a contest.") return True
def post(self): if not self.contest.allow_registration: raise tornado.web.HTTPError(404) try: first_name = self.get_argument("first_name") last_name = self.get_argument("last_name") username = self.get_argument("username") password = self.get_argument("password") email = self.get_argument("email") if len(email) == 0: email = None if not 1 <= len(first_name) <= self.MAX_INPUT_LENGTH: raise ValueError() if not 1 <= len(last_name) <= self.MAX_INPUT_LENGTH: raise ValueError() if not 1 <= len(username) <= self.MAX_INPUT_LENGTH: raise ValueError() if not re.match(r"^[A-Za-z0-9_-]+$", username): raise ValueError() if not self.MIN_PASSWORD_LENGTH <= len(password) \ <= self.MAX_INPUT_LENGTH: raise ValueError() except (tornado.web.MissingArgumentError, ValueError): raise tornado.web.HTTPError(400) # Override password with its hash password = hash_password(password) # If we have teams, we assume that the 'team' field is mandatory if self.sql_session.query(Team).count() > 0: try: team_code = self.get_argument("team") team = self.sql_session.query(Team)\ .filter(Team.code == team_code)\ .one() except (tornado.web.MissingArgumentError, NoResultFound): raise tornado.web.HTTPError(400) else: team = None # Check if the username is available tot_users = self.sql_session.query(User)\ .filter(User.username == username).count() if tot_users != 0: # HTTP 409: Conflict raise tornado.web.HTTPError(409) # Store new user and participation user = User(first_name, last_name, username, password, email=email) self.sql_session.add(user) participation = Participation(user=user, contest=self.contest, team=team) self.sql_session.add(participation) self.sql_session.commit() self.finish(username)
def post(self): if not self.contest.allow_registration: raise tornado.web.HTTPError(404) try: first_name = self.get_argument("first_name") last_name = self.get_argument("last_name") username = self.get_argument("username") password = self.get_argument("password") if not 1 <= len(first_name) <= self.MAX_INPUT_LENGTH: raise ValueError() if not 1 <= len(last_name) <= self.MAX_INPUT_LENGTH: raise ValueError() if not 1 <= len(username) <= self.MAX_INPUT_LENGTH: raise ValueError() if not re.match(r"^[A-Za-z0-9_-]+$", username): raise ValueError() if not self.MIN_PASSWORD_LENGTH <= len(password) \ <= self.MAX_INPUT_LENGTH: raise ValueError() except (tornado.web.MissingArgumentError, ValueError): raise tornado.web.HTTPError(400) # Override password with its hash password = hash_password(password) # If we have teams, we assume that the 'team' field is mandatory if self.sql_session.query(Team).count() > 0: try: team_code = self.get_argument("team") team = self.sql_session.query(Team)\ .filter(Team.code == team_code)\ .one() except (tornado.web.MissingArgumentError, NoResultFound): raise tornado.web.HTTPError(400) else: team = None # Check if the username is available tot_users = self.sql_session.query(User)\ .filter(User.username == username).count() if tot_users != 0: # HTTP 409: Conflict raise tornado.web.HTTPError(409) # Store new user and participation user = User(first_name, last_name, username, password) self.sql_session.add(user) participation = Participation(user=user, contest=self.contest, team=team) self.sql_session.add(participation) self.sql_session.commit() self.finish(username)
def add_participation(username, contest_id, ip, delay_time, extra_time, password, method, is_hashed, team_code, hidden, unrestricted): logger.info("Creating the user's participation in the database.") delay_time = delay_time if delay_time is not None else 0 extra_time = extra_time if extra_time is not None else 0 if hidden: logger.warning("The participation will be hidden") if unrestricted: logger.warning("The participation will be unrestricted") try: with SessionGen() as session: user = \ session.query(User).filter(User.username == username).first() if user is None: logger.error("No user with username `%s' found.", username) return False contest = Contest.get_from_id(contest_id, session) if contest is None: logger.error("No contest with id `%s' found.", contest_id) return False team = None if team_code is not None: team = \ session.query(Team).filter(Team.code == team_code).first() if team is None: logger.error("No team with code `%s' found.", team_code) return False if password is not None: if is_hashed: password = build_password(password, method) else: password = hash_password(password, method) participation = Participation( user=user, contest=contest, ip=[ipaddress.ip_network(ip)] if ip is not None else None, delay_time=datetime.timedelta(seconds=delay_time), extra_time=datetime.timedelta(seconds=extra_time), password=password, team=team, hidden=hidden, unrestricted=unrestricted) session.add(participation) session.commit() except IntegrityError: logger.error("A participation for this user in this contest " "already exists.") return False logger.info("Participation added.") return True
def add_user(first_name, last_name, username, password, method, is_hashed, email, timezone, preferred_languages, overwrite=False): logger.info("Creating the user in the database.") pwd_generated = False if password is None: assert not is_hashed password = generate_random_password() pwd_generated = True if is_hashed: stored_password = build_password(password, method) else: stored_password = hash_password(password, method) if preferred_languages is None or preferred_languages == "": preferred_languages = "[]" else: preferred_languages = \ "[" + ",".join("\"" + lang + "\"" for lang in preferred_languages.split(",")) + "]" user = User(first_name=first_name, last_name=last_name, username=username, password=stored_password, email=email, timezone=timezone, preferred_languages=preferred_languages) with SessionGen() as session: if overwrite: existing_user = session.query(User) \ .filter(User.username == username).first() if existing_user is not None: user = existing_user user.first_name = first_name user.last_name = last_name user.username = username if not pwd_generated: user.password = stored_password else: pwd_generated = False user.email = email or user.email user.timezone = timezone or user.timezone user.preferred_languages = preferred_languages or \ user.preferred_languages try: session.add(user) session.commit() except IntegrityError: logger.error("A user with the given username already exists.") return False logger.info("User added%s. " "Use AddParticipation to add this user to a contest." % (" with password %s" % password if pwd_generated else "")) return True
def post(self, contest_id, user_id): fallback_page = "/contest/%s/user/%s/edit" % (contest_id, user_id) participation = self.get_participation(contest_id, user_id) # Check that the participation is valid. if participation is None: raise tornado.web.HTTPError(404) try: attrs = participation.get_attrs() self.get_string(attrs, "password", empty=None) self.get_string(attrs, "method", empty="text") if "method" not in attrs: attrs["method"] = "text" method = attrs["method"] password = attrs["password"] if password is not None: attrs["password"] = hash_password(password, method) elif method != "text": # Preserve old password if password is empty # and method is not text attrs["password"] = participation.password del attrs["method"] self.get_ip_address_or_subnet(attrs, "ip") self.get_datetime(attrs, "starting_time") self.get_timedelta_sec(attrs, "delay_time") self.get_timedelta_sec(attrs, "extra_time") self.get_bool(attrs, "hidden") self.get_bool(attrs, "unrestricted") # Update the participation. participation.set_attrs(attrs) # Update the team self.get_string(attrs, "team") team = self.sql_session.query(Team)\ .filter(Team.code == attrs["team"])\ .first() participation.team = team except Exception as error: self.application.service.add_notification( make_datetime(), "Invalid field(s)", repr(error)) self.redirect(fallback_page) return if self.try_commit(): # Update the user on RWS. self.application.service.proxy_service.reinitialize() self.redirect(fallback_page)
def _create_user(self): try: first_name = self.get_argument("first_name") last_name = self.get_argument("last_name") username = self.get_argument("username") password = self.get_argument("password") email = self.get_argument("email") if len(email) == 0: email = None if self.contest.registration_requires_captcha: captcha_input = self.get_argument("captcha") captcha_input_signature = self.signature(captcha_input) captcha_cookie = self.get_secure_cookie("captcha").decode( 'utf-8') captcha_clear_signature, captcha_username = captcha_cookie.split( '_', 1) if not 1 <= len(first_name) <= self.MAX_INPUT_LENGTH: raise ValueError() if not 1 <= len(last_name) <= self.MAX_INPUT_LENGTH: raise ValueError() if not 1 <= len(username) <= self.MAX_INPUT_LENGTH: raise ValueError() if not re.match(r"^[A-Za-z0-9_-]+$", username): raise ValueError() if not self.MIN_PASSWORD_LENGTH <= len(password) \ <= self.MAX_INPUT_LENGTH: raise ValueError() if self.contest.registration_requires_captcha: if not re.match(r"^[0-9]+$", captcha_input): raise ValueError() if not captcha_input_signature == captcha_clear_signature: raise ValueError() if not username == captcha_username: raise ValueError() except (tornado_web.MissingArgumentError, ValueError): raise tornado_web.HTTPError(400) # Override password with its hash password = hash_password(password) # Check if the username is available tot_users = self.sql_session.query(User)\ .filter(User.username == username).count() if tot_users != 0: # HTTP 409: Conflict raise tornado_web.HTTPError(409) # Store new user user = User(first_name, last_name, username, password, email=email) self.sql_session.add(user) return user
def post(self, user_id): fallback_page = "/user/%s" % user_id user = self.safe_get_item(User, user_id) try: attrs = user.get_attrs() self.get_string(attrs, "first_name") self.get_string(attrs, "last_name") self.get_string(attrs, "username", empty=None) self.get_string(attrs, "password") self.get_string(attrs, "method", empty="text") if "method" not in attrs: attrs["method"] = "text" password = attrs["password"] method = attrs["method"] if method != "text" and password == "": # Preserve old password if password is empty # and method is not text attrs["password"] = user.password else: attrs["password"] = hash_password(password, method) del attrs["method"] self.get_string(attrs, "email") self.get_string(attrs, "preferred_languages") self.get_string(attrs, "timezone", empty=None) assert attrs.get("username") is not None, \ "No username specified." # Update the user. user.set_attrs(attrs) except Exception as error: self.application.service.add_notification( make_datetime(), "Invalid field(s)", repr(error)) self.redirect(fallback_page) return if self.try_commit(): # Update the user on RWS. self.application.service.proxy_service.reinitialize() self.redirect(fallback_page)
def add_admin(username, password=None, real_name=None): logger.info("Creating the admin on the database.") if password is None: password = generate_random_password() admin = Admin(username=username, authentication=hash_password(password), name=real_name or username, permission_all=True) try: with SessionGen() as session: session.add(admin) session.commit() except IntegrityError: logger.error("An admin with the given username already exists.") return False logger.info("Admin '%s' with complete access added. ", username) return True
def post(self): fallback_page = "/users/add" try: attrs = dict() self.get_string(attrs, "first_name") self.get_string(attrs, "last_name") self.get_string(attrs, "username", empty=None) self.get_string(attrs, "password") self.get_string(attrs, "method", empty="text") if "method" not in attrs: attrs["method"] = "text" attrs["password"] = hash_password(attrs["password"], method=attrs["method"]) del attrs["method"] self.get_string(attrs, "email") assert attrs.get("username") is not None, \ "No username specified." self.get_string(attrs, "timezone", empty=None) self.get_string(attrs, "preferred_languages") # Create the user. user = User(**attrs) self.sql_session.add(user) except Exception as error: self.application.service.add_notification( make_datetime(), "Invalid field(s)", repr(error)) self.redirect(fallback_page) return if self.try_commit(): # Create the user on RWS. self.application.service.proxy_service.reinitialize() self.redirect("/user/%s" % user.id) else: self.redirect(fallback_page)
def _create_user(self): try: first_name = self.get_argument("first_name") last_name = self.get_argument("last_name") username = self.get_argument("username") password = self.get_argument("password") email = self.get_argument("email") if len(email) == 0: email = None if not 1 <= len(first_name) <= self.MAX_INPUT_LENGTH: raise ValueError() if not 1 <= len(last_name) <= self.MAX_INPUT_LENGTH: raise ValueError() if not 1 <= len(username) <= self.MAX_INPUT_LENGTH: raise ValueError() if not re.match(r"^[A-Za-z0-9_-]+$", username): raise ValueError() if not self.MIN_PASSWORD_LENGTH <= len(password) \ <= self.MAX_INPUT_LENGTH: raise ValueError() except (tornado_web.MissingArgumentError, ValueError): raise tornado_web.HTTPError(400) # Override password with its hash password = hash_password(password) # Check if the username is available tot_users = self.sql_session.query(User)\ .filter(User.username == username).count() if tot_users != 0: # HTTP 409: Conflict raise tornado_web.HTTPError(409) # Store new user user = User(first_name, last_name, username, password, email=email) self.sql_session.add(user) return user
def add_user(first_name, last_name, username, password, method, is_hashed, email, timezone, preferred_languages): logger.info("Creating the user in the database.") pwd_generated = False if password is None: assert not is_hashed password = generate_random_password() pwd_generated = True if is_hashed: stored_password = build_password(password, method) else: stored_password = hash_password(password, method) if preferred_languages is None: preferred_languages = [] else: preferred_languages = list(lang.strip() for lang in preferred_languages.split(",") if lang.strip()) user = User(first_name=first_name, last_name=last_name, username=username, password=stored_password, email=email, timezone=timezone, preferred_languages=preferred_languages) try: with SessionGen() as session: session.add(user) session.commit() except IntegrityError: logger.error("A user with the given username already exists.") return False logger.info("User added%s. " "Use AddParticipation to add this user to a contest." % (" with password %s" % password if pwd_generated else "")) return True
def add_user(first_name, last_name, username, password, method, is_hashed, email, timezone, preferred_languages): logger.info("Creating the user in the database.") pwd_generated = False if password is None: assert not is_hashed password = generate_random_password() pwd_generated = True if is_hashed: stored_password = build_password(password, method) else: stored_password = hash_password(password, method) if preferred_languages is None: preferred_languages = [] else: preferred_languages = list( lang.strip() for lang in preferred_languages.split(",") if lang.strip()) user = User(first_name=first_name, last_name=last_name, username=username, password=stored_password, email=email, timezone=timezone, preferred_languages=preferred_languages) try: with SessionGen() as session: session.add(user) session.commit() except IntegrityError: logger.error("A user with the given username already exists.") return False logger.info("User added%s. " "Use AddParticipation to add this user to a contest." % (" with password %s" % password if pwd_generated else "")) return True
def test_plaintext(self): self.assertTrue( validate_password(hash_password("p你好", method="plaintext"), "p你好")) self.assertFalse( validate_password(hash_password("p你好", method="plaintext"), "你好"))
def test_bcrypt(self): self.assertTrue(validate_password( hash_password("p你好", method="bcrypt"), "p你好")) self.assertFalse(validate_password( hash_password("p你好", method="bcrypt"), "你好"))
def test_invalid_method(self): with self.assertRaises(ValueError): hash_password("test", "pwd") with self.assertRaises(ValueError): validate_password("test:pwd", "pwd")
def post(self): fallback_page = self.url("users", "import") r_params = self.render_params() action = self.get_body_argument('action', 'upload') if action == 'upload': ignore_existing = self.get_body_argument('ignore_existing', False) generate_passwords = self.get_body_argument( 'generate_passwords', False) ignored = 0 try: user_csv = self.request.files["users_csv"][0] users = CsvUserLoader(None, None, user_csv['body']).get_users() processed_users = [] for user in users: if generate_passwords or callable(user.password): user.password = None db_user = self.sql_session.query(User).filter_by( username=user.username).first() if db_user: if ignore_existing: if db_user.password: db_user.password = '******' processed_users.append((False, db_user)) ignored += 1 else: self.application.service.add_notification( make_datetime(), 'Import failed', 'User "%s" already exists' % user.username) self.redirect(fallback_page) return else: processed_users.append((True, user)) if ignored: self.application.service.add_notification( make_datetime(), "User exists", '%d users already exist, ignored' % ignored) r_params['users'] = processed_users self.render("users_import_preview.html", **r_params) return except Exception as error: self.application.service.add_notification( make_datetime(), "Bad CSV file", repr(error)) self.redirect(fallback_page) return elif action == 'save': usernames = self.get_body_arguments('username', False) first_names = self.get_body_arguments('first_name', False) last_names = self.get_body_arguments('last_name', False) passwords = self.get_body_arguments('password', False) emails = self.get_body_arguments('email', False) for i in range(len(usernames)): args = { 'username': usernames[i], 'first_name': first_names[i], 'last_name': last_names[i], 'email': emails[i], } if passwords[i]: args['password'] = passwords[i] else: args['password'] = crypto.generate_random_password() args['password'] = crypto.hash_password(args['password'], method='plaintext') user = User(**args) self.sql_session.add(user) if self.try_commit(): # Create the user on RWS. self.application.service.proxy_service.reinitialize() self.redirect(self.url("users")) return else: self.redirect(fallback_page) return self.redirect(fallback_page)
def post(self): username = self.get_argument("username", "") first_name = self.get_argument("first_name", "") last_name = self.get_argument("last_name", "") email = self.get_argument("email", "") password = self.get_argument("password", "") confirm_password = self.get_argument("confirm_password", "") if not self.contest.allow_signup: return tornado.web.HTTPError(404) if password != confirm_password: self.redirect("/signup?password_no_match=true") return user = self.sql_session.query(User).filter(User.username == username).first() participation = self.sql_session.query(Participation).filter(Participation.contest == self.contest).filter(Participation.user == user).first() if user is not None and participation is not None: self.redirect("/signup?user_exist=true") return if user is None: add_user(first_name=first_name, last_name=last_name, username=username, password=hash_password(password), email=email) add_participation(username=username, contest_id=self.contest.id) self.redirect("/?signup_successful=true") return
def post(self): if not self.contest.allow_registration: raise tornado.web.HTTPError(404) try: first_name = self.get_argument("first_name") last_name = self.get_argument("last_name") username = self.get_argument("username") password = self.get_argument("password") email = self.get_argument("email") if len(email) == 0: email = None if not 1 <= len(first_name) <= self.MAX_INPUT_LENGTH: raise ValueError() if not 1 <= len(last_name) <= self.MAX_INPUT_LENGTH: raise ValueError() if not 1 <= len(username) <= self.MAX_INPUT_LENGTH: raise ValueError() if not re.match(r"^[A-Za-z0-9_-]+$", username): raise ValueError() if not self.MIN_PASSWORD_LENGTH <= len(password) \ <= self.MAX_INPUT_LENGTH: raise ValueError() except (tornado.web.MissingArgumentError, ValueError): raise tornado.web.HTTPError(400) # Override password with its hash password = hash_password(password) # If we have teams, we assume that the 'team' field is mandatory if self.sql_session.query(Team).count() > 0: try: team_code = self.get_argument("team") school = self.get_argument("school") team = self.sql_session.query(Team)\ .filter(Team.code == team_code)\ .one() except (tornado.web.MissingArgumentError, NoResultFound): raise tornado.web.HTTPError(400) else: team = None school = None # Check if the username is available tot_users = self.sql_session.query(User)\ .filter(User.username == username).count() if tot_users != 0: # HTTP 409: Conflict raise tornado.web.HTTPError(409) # Store new user and participation user = User(first_name, last_name, username, password, email=email) self.sql_session.add(user) # # Get contest IDs of all contests which are public # f = open('/home/ubuntu/public_contests') # public_contests = set() # for line in f: # digit_contain = False # if (line[0] == '#'): # continue # for c in line: # if (48 <= ord(c)) and (ord(c) <= 57): # digit_contain = True # break # if digit_contain: # public_contests.add(int(line.strip())) # f.close() # Add participation to all public contests for contest in self.sql_session.query(Contest): # if (contest.id in public_contests): if (contest.allow_registration): self.sql_session.add(Participation(user=user, contest=contest, team=team)) # Make log to add additional school if (school != None) and (len(school) > 0): f = open('/home/ubuntu/logs/TODO_logs', 'a') l = str(datetime.datetime.now()) l += " ADD SCHOOL REQUEST " l += " Username: "******" School: " + school f.write(l+'\n') f.close() self.sql_session.commit() self.finish(username)
def test_bcrypt(self): self.assertTrue( validate_password(hash_password("p你好", method="bcrypt"), "p你好")) self.assertFalse( validate_password(hash_password("p你好", method="bcrypt"), "你好"))
def test_plaintext(self): self.assertTrue(validate_password( hash_password("p你好", method="plaintext"), "p你好")) self.assertFalse(validate_password( hash_password("p你好", method="plaintext"), "你好"))