Пример #1
0
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)
Пример #2
0
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)
Пример #3
0
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)
Пример #4
0
    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)
Пример #5
0
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)
Пример #6
0
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)
Пример #7
0
    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
Пример #8
0
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)
Пример #9
0
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)
Пример #10
0
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)
Пример #11
0
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)
Пример #12
0
    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)
Пример #13
0
def test_verify_password():
    assert verify_password(
        "asdf",
        "$bcrypt-sha256$2b,12$I0CNXRkGD2Bi/lbC4vZ7Y.$1WoilsadKpOjXa/be9x3dyu7p.mslZ6",
    )
Пример #14
0
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)