def generate_sample_user(self, confirmed: bool = True) \ -> Tuple[User, str, str]: """ Generates a random user for use in tests :param confirmed: Whether or not the user should be confirmed :return: The User object, the password and the confirmation key """ if self.user_cred_count >= \ len(_TestFramework.GENERATED_USER_CREDENTIALS): password = generate_random(20) password_hash = generate_hash(password) confirm_key = generate_random(20) confirmation_hash = generate_hash(confirm_key) _TestFramework.GENERATED_USER_CREDENTIALS.append( (password, password_hash, confirm_key, confirmation_hash)) else: password, password_hash, confirm_key, confirmation_hash = \ _TestFramework.GENERATED_USER_CREDENTIALS[self.user_cred_count] self.user_cred_count += 1 user = User(username=generate_random(12), password_hash=password_hash, email=generate_random(8) + "@example.com", confirmed=confirmed, confirmation_hash=confirmation_hash) self.db.session.add(user) self.db.session.commit() return user, password, confirm_key
def register() -> Union[Response, str]: """ Page that allows a new user to register :return: The response """ if request.method == "POST": username = request.form["username"] email = request.form["email"] password = request.form["password"] password_repeat = request.form["password-repeat"] recaptcha_result = verify_recaptcha( request.remote_addr, request.form.get("g-recaptcha-response", ""), Config.RECAPTCHA_SECRET_KEY) all_users = User.query.all() usernames = [user.username for user in all_users] emails = [user.email for user in all_users] _min, _max = Config.MIN_USERNAME_LENGTH, Config.MAX_USERNAME_LENGTH if len(username) < _min or len(username) > _max: flash(Config.STRINGS["username_length"].format(_min, _max), "danger") elif password != password_repeat: flash(Config.STRINGS["passwords_do_not_match"], "danger") elif username in usernames: flash(Config.STRINGS["username_already_exists"], "danger") elif email in emails: flash(Config.STRINGS["email_already_in_use"], "danger") elif not recaptcha_result: flash(Config.STRINGS["recaptcha_incorrect"], "danger") else: confirmation_key = generate_random(32) confirmation_hash = generate_hash(confirmation_key) user = User(username=username, email=email, password_hash=generate_hash(password), confirmation_hash=confirmation_hash) db.session.add(user) db.session.commit() email_msg = render_template( "email/registration.html", domain_name=Config.DOMAIN_NAME, host_url=Config.base_url(), target_url=os.path.join(Config.base_url(), "confirm"), username=username, user_id=user.id, confirm_key=confirmation_key, **Config.TEMPLATE_EXTRAS["registration_email"]()) send_email(email, Config.STRINGS["registration_email_title"], email_msg, Config.SMTP_HOST, Config.SMTP_ADDRESS, Config.SMTP_PASSWORD, Config.SMTP_PORT) app.logger.info("User {} registered.".format(user.username)) flash(Config.STRINGS["registration_successful"], "info") return redirect(url_for("static.index")) return redirect(url_for("user_management.register")) else: return render_template("user_management/register.html", **Config.TEMPLATE_EXTRAS["register"]())
def test_hashing_strings(self): """ Tests that password hashing and verifying works with string as well :return: None """ password = str(generate_random(30)) _hash = generate_hash(password) self.assertTrue(verify_password(password, _hash))
def test_hashing(self): """ Tests that passwords can be hashed successfully :return: None """ password_one = generate_random(30) password_two = generate_random(20) hash_one = generate_hash(password_one) hash_two = generate_hash(password_two) self.assertEqual(len(hash_one), len(hash_two)) self.assertNotEqual(hash_one, hash_two) self.assertTrue(verify_password(password_one, hash_one)) self.assertTrue(verify_password(password_two, hash_two)) self.assertFalse(verify_password(password_one, hash_two)) self.assertFalse(verify_password(password_two, hash_one))
def test_salt(self): """ Tests that password hashes are salted :return: None """ password = generate_random(30) hash_one = generate_hash(password) hash_two = generate_hash(password) self.assertNotEqual(hash_one, hash_two) self.assertTrue(verify_password(password, hash_one)) self.assertTrue(verify_password(password, hash_two))
def forgot() -> Union[Response, str]: """ Allows a user to reset their password :return: The response """ if request.method == "POST": email = request.form["email"] recaptcha_result = verify_recaptcha( request.remote_addr, request.form.get("g-recaptcha-response", ""), Config.RECAPTCHA_SECRET_KEY) user: User = User.query.filter_by(email=email).first() if not recaptcha_result: flash(Config.STRINGS["recaptcha_incorrect"], "danger") return redirect(url_for("user_management.forgot")) else: if user is None: # Fail silently to ensure that a potential attacker can't # use the response to figure out information # on registered users pass else: new_pass = generate_random(20) user.password_hash = generate_hash(new_pass) db.session.commit() email_msg = render_template( "email/forgot_password.html", domain_name=Config.DOMAIN_NAME, host_url=Config.base_url(), target_url=os.path.join(Config.base_url(), "login"), password=new_pass, username=user.username, **Config.TEMPLATE_EXTRAS["forgot_email"]()) try: send_email( email, Config.STRINGS["password_reset_email_title"], email_msg, Config.SMTP_HOST, Config.SMTP_ADDRESS, Config.SMTP_PASSWORD, Config.SMTP_PORT) except SMTPAuthenticationError: # pragma: no cover app.logger.error("SMTP Authentication failed") flash("SMTP AUTHENTICATION FAILED", "info") flash(Config.STRINGS["password_was_reset"], "success") return redirect(url_for("static.index")) else: return render_template("user_management/forgot.html", **Config.TEMPLATE_EXTRAS["forgot"]())
def api_key() -> Dict[str, Any]: """ Allows users to request a new API key or revoke an existing API key :return: The JSON response """ data = request.get_json() assert data is not None if request.method == "POST": username = data["username"] password = data["password"] user = User.query.filter_by(username=username).first() if user is None: raise ApiException("user does not exist", 401) elif not user.confirmed: raise ApiException("user is not confirmed", 401) elif not user.verify_password(password): raise ApiException("password is incorrect", 401) else: key = generate_random(32) hashed = generate_hash(key) _api_key = ApiKey(user=user, key_hash=hashed) db.session.add(_api_key) db.session.commit() return { "api_key": "{}:{}".format(_api_key.id, key), "expiration": (int(_api_key.creation_time) + Config.MAX_API_KEY_AGE), "user": user.__json__(False) } else: # request.method == "DELETE" key = data["api_key"] _api_key = ApiKey.query.get(key.split(":", 1)[0]) if _api_key is None: raise ApiException("api key does not exist", 401) elif not _api_key.verify_key(key): raise ApiException("api key not valid", 401) else: db.session.delete(_api_key) db.session.commit() return {}
def setUp(self): """ Sets up the flask application and a temporary database to test with :return: None """ self.user_cred_count = 0 self.api_cred_count = 0 self.temp_templates_dir = "templates" self.templates_sample_dir = os.path.join( os.path.dirname(os.path.abspath(__file__)), "templates") os.environ["FLASK_TESTING"] = "1" os.environ["DB_MODE"] = "sqlite" os.environ["FLASK_SECRET"] = generate_random(20) self.cleanup(True) if self.module_name == "jerrycan": shutil.copytree(self.templates_sample_dir, self.temp_templates_dir) os.environ["RECAPTCHA_SITE_KEY"] = "" os.environ["RECAPTCHA_SECRET_KEY"] = "" os.environ["SMTP_HOST"] = "" os.environ["SMTP_PORT"] = "0" os.environ["SMTP_ADDRESS"] = "" os.environ["SMTP_PASSWORD"] = "" os.environ["TELEGRAM_API_KEY"] = "" else: load_env_file() self.config.load_config(self.root_path, self.module_name, "") self.db_path = Config.DB_URI.split("sqlite:///")[1] self.cleanup(False) self.app = app self.db = db init_flask(self.module_name, "", self.root_path, self.config, self.models, self.blueprint_generators, self.extra_jinja_vars) self.app.app_context().push() self.client = self.app.test_client() self.context = self.app.test_request_context()
def generate_api_key(self, user: User) \ -> Tuple[ApiKey, str, Dict[str, str]]: """ Generates an API key and base64 encoded headers for requests :param user: The user for which to create the key :return: The API key object, the actual API key, the headers """ if self.api_cred_count >= \ len(_TestFramework.GENERATED_API_KEY_CREDENTIALS): key = generate_random(20) hashed = generate_hash(key) _TestFramework.GENERATED_API_KEY_CREDENTIALS.append((key, hashed)) else: key, hashed = _TestFramework.GENERATED_API_KEY_CREDENTIALS[ self.api_cred_count] self.api_cred_count += 1 api_key_obj = ApiKey(user=user, key_hash=hashed) self.db.session.add(api_key_obj) self.db.session.commit() api_key = "{}:{}".format(api_key_obj.id, key) return api_key_obj, api_key, self.generate_api_key_headers(api_key)