def test_api_users_post_admin(): """Can a user post /api/v1/users if admin""" app = create_kmactf() with app.app_context(): with login_as_user(app, "admin") as client: # Create user r = client.post( "/api/v1/users", json={ "name": "user", "email": "*****@*****.**", "password": "******" }, ) assert r.status_code == 200 # Make sure password was hashed properly user = Users.query.filter_by(email="*****@*****.**").first() assert user assert verify_password("password", user.password) # Make sure user can login with the creds client = login_as_user(app) r = client.get("/profile") assert r.status_code == 200 destroy_kmactf(app)
def test_email_cannot_be_changed_without_password(): """Test that a user can't update their email address without current password""" app = create_kmactf() with app.app_context(): register_user(app) client = login_as_user(app) data = {"name": "user", "email": "*****@*****.**"} r = client.patch("/api/v1/users/me", json=data) assert r.status_code == 400 user = Users.query.filter_by(id=2).first() assert user.email == "*****@*****.**" data = {"name": "user", "email": "*****@*****.**", "confirm": "asdf"} r = client.patch("/api/v1/users/me", json=data) assert r.status_code == 400 user = Users.query.filter_by(id=2).first() assert user.email == "*****@*****.**" data = {"name": "user", "email": "*****@*****.**", "confirm": "password"} r = client.patch("/api/v1/users/me", json=data) assert r.status_code == 200 user = Users.query.filter_by(id=2).first() assert user.email == "*****@*****.**" assert verify_password(plaintext="password", ciphertext=user.password) destroy_kmactf(app)
def test_api_users_post_admin_with_attributes(): """Can a user post /api/v1/users with user settings""" app = create_kmactf() with app.app_context(): with login_as_user(app, "admin") as client: # Create user r = client.post( "/api/v1/users", json={ "name": "user", "email": "*****@*****.**", "password": "******", "banned": True, "hidden": True, "verified": True, }, ) assert r.status_code == 200 # Make sure password was hashed properly user = Users.query.filter_by(email="*****@*****.**").first() assert user assert verify_password("password", user.password) assert user.banned assert user.hidden assert user.verified destroy_kmactf(app)
def validate_password_confirmation(self, data): password = data.get("password") confirm = data.get("confirm") if is_admin(): pass else: current_team = get_current_team() current_user = get_current_user() if current_team.captain_id != current_user.id: raise ValidationError( "Only the captain can change the team password", field_names=["captain_id"], ) if password and (bool(confirm) is False): raise ValidationError( "Please confirm your current password", field_names=["confirm"] ) if password and confirm: test = verify_password( plaintext=confirm, ciphertext=current_team.password ) if test is True: return data else: raise ValidationError( "Your previous password is incorrect", field_names=["confirm"] ) else: data.pop("password", None) data.pop("confirm", None)
def test_user_can_change_password(): """Test that a user can change their password and is prompted properly""" app = create_kmactf() with app.app_context(): register_user(app) client = login_as_user(app) data = { "name": "user", "email": "*****@*****.**", "confirm": "", "password": "******", "affiliation": "", "website": "", "country": "", } r = client.patch("/api/v1/users/me", json=data) user = Users.query.filter_by(id=2).first() assert verify_password(data["password"], user.password) is False assert r.status_code == 400 assert r.get_json() == { "errors": { "confirm": ["Please confirm your current password"] }, "success": False, } data["confirm"] = "wrong_password" r = client.patch("/api/v1/users/me", json=data) user = Users.query.filter_by(id=2).first() assert verify_password(data["password"], user.password) is False assert r.status_code == 400 assert r.get_json() == { "errors": { "confirm": ["Your previous password is incorrect"] }, "success": False, } data["confirm"] = "password" r = client.patch("/api/v1/users/me", json=data) assert r.status_code == 200 user = Users.query.filter_by(id=2).first() assert verify_password(data["password"], user.password) is True destroy_kmactf(app)
def test_api_team_patch_password(): """Can a user change their team password /api/v1/teams/me if logged in as the captain""" app = create_kmactf(user_mode="teams") with app.app_context(): user1 = gen_user(app.db, name="user1", email="*****@*****.**") # ID 2 user2 = gen_user(app.db, name="user2", email="*****@*****.**") # ID 3 team = gen_team(app.db) team.members.append(user1) team.members.append(user2) team.captain_id = 2 user1.team_id = team.id user2.team_id = team.id app.db.session.commit() with login_as_user(app, name="user2") as client: r = client.patch( "/api/v1/teams/me", json={ "confirm": "password", "password": "******" }, ) assert r.status_code == 403 assert r.get_json() == { "errors": { "": ["Only team captains can edit team information"] }, "success": False, } team = Teams.query.filter_by(id=1).first() assert (verify_password(plaintext="new_password", ciphertext=team.password) is False) with login_as_user(app, name="user1") as client: r = client.patch( "/api/v1/teams/me", json={ "confirm": "password", "password": "******" }, ) assert r.status_code == 200 team = Teams.query.filter_by(id=1).first() assert verify_password(plaintext="new_password", ciphertext=team.password)
def validate_email(self, data): email = data.get("email") if email is None: return email = email.strip() existing_user = Users.query.filter_by(email=email).first() current_user = get_current_user() if is_admin(): user_id = data.get("id") if user_id: if existing_user and existing_user.id != user_id: raise ValidationError( "Email address has already been used", field_names=["email"]) else: if existing_user: if current_user: if current_user.id != existing_user.id: raise ValidationError( "Email address has already been used", field_names=["email"], ) else: raise ValidationError( "Email address has already been used", field_names=["email"]) else: if email == current_user.email: return data else: confirm = data.get("confirm") if bool(confirm) is False: raise ValidationError( "Please confirm your current password", field_names=["confirm"]) test = verify_password(plaintext=confirm, ciphertext=current_user.password) if test is False: raise ValidationError( "Your previous password is incorrect", field_names=["confirm"]) if existing_user: raise ValidationError( "Email address has already been used", field_names=["email"]) if check_email_is_whitelisted(email) is False: raise ValidationError( "Only email addresses under {domains} may register". format(domains=get_config("domain_whitelist")), field_names=["email"], ) if get_config("verify_emails"): current_user.verified = False
def login(): errors = get_errors() if request.method == "POST": name = request.form["name"] captcha_response = request.form['g-recaptcha-response'] if not is_human(captcha_response): # This user exists but the password is wrong error_captcha = "The response parameter is missing" return render_template("login.html", error_captcha=error_captcha) # Check if the user submitted an email address or a team name if validators.validate_email(name) is True: user = Users.query.filter_by(email=name).first() else: user = Users.query.filter_by(name=name).first() if user: if user and verify_password(request.form["password"], user.password): session.regenerate() login_user(user) log("logins", "[{date}] {ip} - {name} logged in") db.session.close() if request.args.get("next") and validators.is_safe_url( request.args.get("next")): return redirect(request.args.get("next")) return redirect(url_for("challenges.listing")) else: # This user exists but the password is wrong log("logins", "[{date}] {ip} - submitted invalid password for {name}") errors.append("Your username or password is incorrect") db.session.close() return render_template("login.html", errors=errors) else: # This user just doesn't exist log("logins", "[{date}] {ip} - submitted invalid account information") errors.append("Your username or password is incorrect") db.session.close() return render_template("login.html", errors=errors) else: db.session.close() return render_template("login.html", errors=errors)
def join(): infos = get_infos() errors = get_errors() if request.method == "GET": team_size_limit = get_config("team_size", default=0) if team_size_limit: plural = "" if team_size_limit == 1 else "s" infos.append( "Teams are limited to {limit} member{plural}".format( limit=team_size_limit, plural=plural ) ) return render_template("teams/join_team.html", infos=infos, errors=errors) if request.method == "POST": teamname = request.form.get("name") passphrase = request.form.get("password", "").strip() team = Teams.query.filter_by(name=teamname).first() user = get_current_user() if team and verify_password(passphrase, team.password): team_size_limit = get_config("team_size", default=0) if team_size_limit and len(team.members) >= team_size_limit: errors.append( "{name} has already reached the team size limit of {limit}".format( name=team.name, limit=team_size_limit ) ) return render_template( "teams/join_team.html", infos=infos, errors=errors ) user.team_id = team.id db.session.commit() if len(team.members) == 1: team.captain_id = user.id db.session.commit() return redirect(url_for("challenges.listing")) else: errors.append("That information is incorrect") return render_template("teams/join_team.html", infos=infos, errors=errors)
def test_api_team_patch_admin(): """Can a user patch /api/v1/teams/<team_id> if admin""" app = create_kmactf(user_mode="teams") with app.app_context(): gen_team(app.db) with login_as_user(app, "admin") as client: r = client.patch( "/api/v1/teams/1", json={ "name": "team_name", "email": "*****@*****.**", "password": "******", "affiliation": "changed", }, ) team = Teams.query.filter_by(id=1).first() assert r.status_code == 200 assert r.get_json()["data"]["affiliation"] == "changed" assert verify_password("password", team.password) destroy_kmactf(app)
def test_api_teams_post_admin(): """Can a user post /api/v1/teams if admin""" app = create_kmactf(user_mode="teams") with app.app_context(): with login_as_user(app, "admin") as client: # Create team r = client.post( "/api/v1/teams", json={ "website": "http://www.team.com", "name": "team", "country": "TW", "email": "*****@*****.**", "affiliation": "team", "password": "******", }, ) assert r.status_code == 200 # Make sure password was hashed properly team = Teams.query.filter_by(email="*****@*****.**").first() assert team assert verify_password("password", team.password) # Make sure team can actually be joined register_user(app) client = login_as_user(app) with client.session_transaction() as sess: data = { "name": "team", "password": "******", "nonce": sess.get("nonce"), } r = client.post("/teams/join", data=data) user = Users.query.filter_by(id=2).first() assert user.team_id == 1 destroy_kmactf(app)
def validate_password_confirmation(self, data): password = data.get("password") confirm = data.get("confirm") target_user = get_current_user() if is_admin(): pass else: if password and (bool(confirm) is False): raise ValidationError("Please confirm your current password", field_names=["confirm"]) if password and confirm: test = verify_password(plaintext=confirm, ciphertext=target_user.password) if test is True: return data else: raise ValidationError( "Your previous password is incorrect", field_names=["confirm"]) else: data.pop("password", None) data.pop("confirm", None)
def test_verify_password(): assert verify_password( "asdf", "$bcrypt-sha256$2b,12$I0CNXRkGD2Bi/lbC4vZ7Y.$1WoilsadKpOjXa/be9x3dyu7p.mslZ6", )
def test_user_can_reset_password(mock_smtp): """Test that a user is capable of resetting their password""" from email.mime.text import MIMEText app = create_kmactf() with app.app_context(), freeze_time("2012-01-14 03:21:34"): # Set KMActf to send emails set_config("mail_server", "localhost") set_config("mail_port", 25) set_config("mail_useauth", True) set_config("mail_username", "username") set_config("mail_password", "password") # Create a user register_user(app, name="user1", email="*****@*****.**") with app.test_client() as client: client.get("/reset_password") # Build reset password data with client.session_transaction() as sess: data = {"nonce": sess.get("nonce"), "email": "*****@*****.**"} # Issue the password reset request client.post("/reset_password", data=data) ctf_name = get_config("ctf_name") from_addr = get_config("mailfrom_addr") or app.config.get( "MAILFROM_ADDR") from_addr = "{} <{}>".format(ctf_name, from_addr) to_addr = "*****@*****.**" # Build the email msg = ( "Did you initiate a password reset? If you didn't initiate this request you can ignore this email. " "\n\nClick the following link to reset your password:\n" "http://localhost/reset_password/InVzZXJAdXNlci5jb20i.TxD0vg.28dY_Gzqb1TH9nrcE_H7W8YFM-U" ) ctf_name = get_config("ctf_name") email_msg = MIMEText(msg) email_msg[ "Subject"] = "Password Reset Request from {ctf_name}".format( ctf_name=ctf_name) email_msg["From"] = from_addr email_msg["To"] = to_addr # Make sure that the reset password email is sent mock_smtp.return_value.sendmail.assert_called_with( from_addr, [to_addr], email_msg.as_string()) # Get user's original password user = Users.query.filter_by(email="*****@*****.**").first() # Build the POST data with client.session_transaction() as sess: data = {"nonce": sess.get("nonce"), "password": "******"} # Do the password reset client.get( "/reset_password/InVzZXJAdXNlci5jb20i.TxD0vg.28dY_Gzqb1TH9nrcE_H7W8YFM-U" ) client.post( "/reset_password/InVzZXJAdXNlci5jb20i.TxD0vg.28dY_Gzqb1TH9nrcE_H7W8YFM-U", data=data, ) # Make sure that the user's password changed user = Users.query.filter_by(email="*****@*****.**").first() assert verify_password("passwordtwo", user.password) destroy_kmactf(app)