Loosen team password confirmation to also accept team captain password (#1598)

* Loosen team password confirmation to also accept team captain password
* Make team settings modal larger in the core theme
* Add help text in team settings form
* Closes #1545
This commit is contained in:
Kevin Chung
2020-08-13 14:08:44 -04:00
committed by GitHub
parent da4357b07b
commit 92a40b6eff
4 changed files with 82 additions and 21 deletions

View File

@@ -20,12 +20,28 @@ class TeamRegisterForm(BaseForm):
class TeamSettingsForm(BaseForm): class TeamSettingsForm(BaseForm):
name = StringField("Team Name") name = StringField(
confirm = PasswordField("Current Password") "Team Name", description="Your team's public name shown to other competitors"
password = PasswordField("Team Password") )
affiliation = StringField("Affiliation") password = PasswordField(
website = URLField("Website") "New Team Password", description="Set a new team join password"
country = SelectField("Country", choices=SELECT_COUNTRIES_LIST) )
confirm = PasswordField(
"Confirm Password",
description="Provide your current team password (or your password) to update your team's password",
)
affiliation = StringField(
"Affiliation",
description="Your team's affiliation publicly shown to other competitors",
)
website = URLField(
"Website", description="Your team's website publicly shown to other competitors"
)
country = SelectField(
"Country",
choices=SELECT_COUNTRIES_LIST,
description="Your team's country publicly shown to other competitors",
)
submit = SubmitField("Submit") submit = SubmitField("Submit")

View File

@@ -142,10 +142,13 @@ class TeamSchema(ma.ModelSchema):
) )
if password and confirm: if password and confirm:
test = verify_password( test_team = verify_password(
plaintext=confirm, ciphertext=current_team.password plaintext=confirm, ciphertext=current_team.password
) )
if test is True: test_captain = verify_password(
plaintext=confirm, ciphertext=current_user.password
)
if test_team is True or test_captain is True:
return data return data
else: else:
raise ValidationError( raise ValidationError(

View File

@@ -5,7 +5,7 @@
{% block content %} {% block content %}
<div id="team-edit-modal" class="modal fade"> <div id="team-edit-modal" class="modal fade">
<div class="modal-dialog"> <div class="modal-dialog modal-lg">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h2 class="modal-action text-center w-100">Edit Team</h2> <h2 class="modal-action text-center w-100">Edit Team</h2>
@@ -17,28 +17,46 @@
{% with form = Forms.teams.TeamSettingsForm(obj=team) %} {% with form = Forms.teams.TeamSettingsForm(obj=team) %}
<form id="team-info-form" method="POST"> <form id="team-info-form" method="POST">
<div class="form-group"> <div class="form-group">
{{ form.name.label }} <b>{{ form.name.label }}</b>
{{ form.name(class="form-control") }} {{ form.name(class="form-control") }}
<small class="form-text text-muted">
{{ form.name.description }}
</small>
</div> </div>
<div class="form-group"> <div class="form-group">
{{ form.confirm.label }} <b>{{ form.password.label }}</b>
{{ form.confirm(class="form-control") }}
</div>
<div class="form-group">
{{ form.password.label }}
{{ form.password(class="form-control") }} {{ form.password(class="form-control") }}
<small class="form-text text-muted">
{{ form.password.description }}
</small>
</div> </div>
<div class="form-group"> <div class="form-group">
{{ form.website.label }} <b>{{ form.confirm.label }}</b>
{{ form.confirm(class="form-control") }}
<small class="form-text text-muted">
{{ form.confirm.description }}
</small>
</div>
<div class="form-group">
<b>{{ form.website.label }}</b>
{{ form.website(class="form-control") }} {{ form.website(class="form-control") }}
<small class="form-text text-muted">
{{ form.website.description }}
</small>
</div> </div>
<div class="form-group"> <div class="form-group">
{{ form.affiliation.label }} <b>{{ form.affiliation.label }}</b>
{{ form.affiliation(class="form-control") }} {{ form.affiliation(class="form-control") }}
<small class="form-text text-muted">
{{ form.affiliation.description }}
</small>
</div> </div>
<div class="form-group"> <div class="form-group">
{{ form.country.label }} <b>{{ form.country.label }}</b>
{{ form.country(class="form-control custom-select") }} {{ form.country(class="form-control custom-select") }}
<small class="form-text text-muted">
{{ form.country.description }}
</small>
</div> </div>
<div id="results"> <div id="results">

View File

@@ -633,7 +633,9 @@ def test_api_team_patch_password():
"""Can a user change their team password /api/v1/teams/me if logged in as the captain""" """Can a user change their team password /api/v1/teams/me if logged in as the captain"""
app = create_ctfd(user_mode="teams") app = create_ctfd(user_mode="teams")
with app.app_context(): with app.app_context():
user1 = gen_user(app.db, name="user1", email="user1@ctfd.io") # ID 2 user1 = gen_user(
app.db, name="user1", email="user1@ctfd.io", password="captain"
) # ID 2
user2 = gen_user(app.db, name="user2", email="user2@ctfd.io") # ID 3 user2 = gen_user(app.db, name="user2", email="user2@ctfd.io") # ID 3
team = gen_team(app.db) team = gen_team(app.db)
team.members.append(user1) team.members.append(user1)
@@ -660,16 +662,38 @@ def test_api_team_patch_password():
is False is False
) )
with login_as_user(app, name="user1") as client: with login_as_user(app, name="user1", password="captain") as client:
# Test that invalid passwords aren't accepted
r = client.patch(
"/api/v1/teams/me",
json={"confirm": "incorrect_password", "password": "new_password"},
)
assert r.status_code == 400
assert (
verify_password(plaintext="new_password", ciphertext=team.password)
is False
)
# Test that the team's password is accepted
r = client.patch( r = client.patch(
"/api/v1/teams/me", "/api/v1/teams/me",
json={"confirm": "password", "password": "new_password"}, json={"confirm": "password", "password": "new_password"},
) )
assert r.status_code == 200 assert r.status_code == 200
team = Teams.query.filter_by(id=1).first() team = Teams.query.filter_by(id=1).first()
assert verify_password(plaintext="new_password", ciphertext=team.password) assert verify_password(plaintext="new_password", ciphertext=team.password)
# Test that the captain's password is also accepted
r = client.patch(
"/api/v1/teams/me",
json={"confirm": "captain", "password": "captain_password"},
)
assert r.status_code == 200
team = Teams.query.filter_by(id=1).first()
assert verify_password(
plaintext="captain_password", ciphertext=team.password
)
def test_api_accessing_hidden_banned_users(): def test_api_accessing_hidden_banned_users():
"""Hidden/Banned users should not be visible to normal users, only to admins""" """Hidden/Banned users should not be visible to normal users, only to admins"""