mirror of
https://github.com/aljazceru/CTFd.git
synced 2025-12-17 05:54:19 +01:00
WIP: Add registration password (#1946)
* Closes #1895 * Add a registration password to account creation (ignoring SSO or API based account creation)
This commit is contained in:
@@ -197,6 +197,7 @@ def register():
|
|||||||
website = request.form.get("website")
|
website = request.form.get("website")
|
||||||
affiliation = request.form.get("affiliation")
|
affiliation = request.form.get("affiliation")
|
||||||
country = request.form.get("country")
|
country = request.form.get("country")
|
||||||
|
registration_code = request.form.get("registration_code", "")
|
||||||
|
|
||||||
name_len = len(name) == 0
|
name_len = len(name) == 0
|
||||||
names = Users.query.add_columns("name", "id").filter_by(name=name).first()
|
names = Users.query.add_columns("name", "id").filter_by(name=name).first()
|
||||||
@@ -210,6 +211,13 @@ def register():
|
|||||||
valid_email = validators.validate_email(email_address)
|
valid_email = validators.validate_email(email_address)
|
||||||
team_name_email_check = validators.validate_email(name)
|
team_name_email_check = validators.validate_email(name)
|
||||||
|
|
||||||
|
if get_config("registration_code"):
|
||||||
|
if (
|
||||||
|
registration_code.lower()
|
||||||
|
!= get_config("registration_code", default="").lower()
|
||||||
|
):
|
||||||
|
errors.append("The registration code you entered was incorrect")
|
||||||
|
|
||||||
# Process additional user fields
|
# Process additional user fields
|
||||||
fields = {}
|
fields = {}
|
||||||
for field in UserFields.query.all():
|
for field in UserFields.query.all():
|
||||||
|
|||||||
@@ -4,7 +4,12 @@ from wtforms.validators import InputRequired
|
|||||||
|
|
||||||
from CTFd.forms import BaseForm
|
from CTFd.forms import BaseForm
|
||||||
from CTFd.forms.fields import SubmitField
|
from CTFd.forms.fields import SubmitField
|
||||||
from CTFd.forms.users import attach_custom_user_fields, build_custom_user_fields
|
from CTFd.forms.users import (
|
||||||
|
attach_custom_user_fields,
|
||||||
|
attach_registration_code_field,
|
||||||
|
build_custom_user_fields,
|
||||||
|
build_registration_code_field,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def RegistrationForm(*args, **kwargs):
|
def RegistrationForm(*args, **kwargs):
|
||||||
@@ -18,9 +23,10 @@ def RegistrationForm(*args, **kwargs):
|
|||||||
def extra(self):
|
def extra(self):
|
||||||
return build_custom_user_fields(
|
return build_custom_user_fields(
|
||||||
self, include_entries=False, blacklisted_items=()
|
self, include_entries=False, blacklisted_items=()
|
||||||
)
|
) + build_registration_code_field(self)
|
||||||
|
|
||||||
attach_custom_user_fields(_RegistrationForm)
|
attach_custom_user_fields(_RegistrationForm)
|
||||||
|
attach_registration_code_field(_RegistrationForm)
|
||||||
|
|
||||||
return _RegistrationForm(*args, **kwargs)
|
return _RegistrationForm(*args, **kwargs)
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ from wtforms import BooleanField, PasswordField, SelectField, StringField
|
|||||||
from wtforms.fields.html5 import EmailField
|
from wtforms.fields.html5 import EmailField
|
||||||
from wtforms.validators import InputRequired
|
from wtforms.validators import InputRequired
|
||||||
|
|
||||||
|
from CTFd.constants.config import Configs
|
||||||
from CTFd.forms import BaseForm
|
from CTFd.forms import BaseForm
|
||||||
from CTFd.forms.fields import SubmitField
|
from CTFd.forms.fields import SubmitField
|
||||||
from CTFd.models import UserFieldEntries, UserFields
|
from CTFd.models import UserFieldEntries, UserFields
|
||||||
@@ -79,6 +80,36 @@ def attach_custom_user_fields(form_cls, **kwargs):
|
|||||||
setattr(form_cls, f"fields[{field.id}]", input_field)
|
setattr(form_cls, f"fields[{field.id}]", input_field)
|
||||||
|
|
||||||
|
|
||||||
|
def build_registration_code_field(form_cls):
|
||||||
|
"""
|
||||||
|
Build the appropriate field so we can render it via the extra property.
|
||||||
|
Add field_type so Jinja knows how to render it.
|
||||||
|
"""
|
||||||
|
if Configs.registration_code:
|
||||||
|
field = getattr(form_cls, "registration_code") # noqa B009
|
||||||
|
field.field_type = "text"
|
||||||
|
return [field]
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def attach_registration_code_field(form_cls):
|
||||||
|
"""
|
||||||
|
If we have a registration code required, we attach it to the form similar
|
||||||
|
to attach_custom_user_fields
|
||||||
|
"""
|
||||||
|
if Configs.registration_code:
|
||||||
|
setattr( # noqa B010
|
||||||
|
form_cls,
|
||||||
|
"registration_code",
|
||||||
|
StringField(
|
||||||
|
"Registration Code",
|
||||||
|
description="Registration code required to create account",
|
||||||
|
validators=[InputRequired()],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class UserSearchForm(BaseForm):
|
class UserSearchForm(BaseForm):
|
||||||
field = SelectField(
|
field = SelectField(
|
||||||
"Search Field",
|
"Search Field",
|
||||||
|
|||||||
@@ -32,6 +32,9 @@
|
|||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link rounded-0" href="#settings" role="tab" data-toggle="tab">Settings</a>
|
<a class="nav-link rounded-0" href="#settings" role="tab" data-toggle="tab">Settings</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link rounded-0" href="#security" role="tab" data-toggle="tab">Security</a>
|
||||||
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link rounded-0" href="#email" role="tab" data-toggle="tab">Email</a>
|
<a class="nav-link rounded-0" href="#email" role="tab" data-toggle="tab">Email</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -73,6 +76,8 @@
|
|||||||
|
|
||||||
{% include "admin/configs/settings.html" %}
|
{% include "admin/configs/settings.html" %}
|
||||||
|
|
||||||
|
{% include "admin/configs/security.html" %}
|
||||||
|
|
||||||
{% include "admin/configs/email.html" %}
|
{% include "admin/configs/email.html" %}
|
||||||
|
|
||||||
{% include "admin/configs/time.html" %}
|
{% include "admin/configs/time.html" %}
|
||||||
|
|||||||
14
CTFd/themes/admin/templates/configs/security.html
Normal file
14
CTFd/themes/admin/templates/configs/security.html
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<div role="tabpanel" class="tab-pane config-section active" id="security">
|
||||||
|
<form method="POST" autocomplete="off" class="w-100">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="ctf_name">
|
||||||
|
Registration Code
|
||||||
|
<small class="form-text text-muted">Registration Code required for account registration (SSO excluded)</small>
|
||||||
|
</label>
|
||||||
|
<input class="form-control" id='registration_code' name='registration_code' type='text' placeholder="Registration Code"
|
||||||
|
{% if registration_code is defined and registration_code != None %}value="{{ registration_code }}"{% endif %}>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-md btn-primary float-right">Update</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
@@ -423,3 +423,44 @@ def test_banned_user():
|
|||||||
r = client.get(route)
|
r = client.get(route)
|
||||||
assert r.status_code == 403
|
assert r.status_code == 403
|
||||||
destroy_ctfd(app)
|
destroy_ctfd(app)
|
||||||
|
|
||||||
|
|
||||||
|
def test_registration_code_required():
|
||||||
|
"""
|
||||||
|
Test that registration code configuration properly blocks logins
|
||||||
|
with missing and incorrect registration codes
|
||||||
|
"""
|
||||||
|
app = create_ctfd()
|
||||||
|
with app.app_context():
|
||||||
|
# Set a registration code
|
||||||
|
set_config("registration_code", "secret-sauce")
|
||||||
|
|
||||||
|
with app.test_client() as client:
|
||||||
|
# Load CSRF nonce
|
||||||
|
r = client.get("/register")
|
||||||
|
resp = r.get_data(as_text=True)
|
||||||
|
assert "Registration Code" in resp
|
||||||
|
with client.session_transaction() as sess:
|
||||||
|
data = {
|
||||||
|
"name": "user",
|
||||||
|
"email": "user1@examplectf.com",
|
||||||
|
"password": "password",
|
||||||
|
"nonce": sess.get("nonce"),
|
||||||
|
}
|
||||||
|
# Attempt registration without password
|
||||||
|
r = client.post("/register", data=data)
|
||||||
|
resp = r.get_data(as_text=True)
|
||||||
|
assert "The registration code you entered was incorrect" in resp
|
||||||
|
|
||||||
|
# Attempt registration with wrong password
|
||||||
|
data["registration_code"] = "wrong-sauce"
|
||||||
|
r = client.post("/register", data=data)
|
||||||
|
resp = r.get_data(as_text=True)
|
||||||
|
assert "The registration code you entered was incorrect" in resp
|
||||||
|
|
||||||
|
# Attempt registration with right password
|
||||||
|
data["registration_code"] = "secret-sauce"
|
||||||
|
r = client.post("/register", data=data)
|
||||||
|
assert r.status_code == 302
|
||||||
|
assert r.location.startswith("http://localhost/challenges")
|
||||||
|
destroy_ctfd(app)
|
||||||
|
|||||||
Reference in New Issue
Block a user