SRNI-2005/ctf
0
1from flask_babel import lazy_gettext as _l2from wtforms import BooleanField, PasswordField, SelectField, StringField3from wtforms.fields.html5 import EmailField4from wtforms.validators import InputRequired5 6from CTFd.constants.config import Configs7from CTFd.constants.languages import SELECT_LANGUAGE_LIST8from CTFd.forms import BaseForm9from CTFd.forms.fields import SubmitField10from CTFd.models import Brackets, UserFieldEntries, UserFields11from CTFd.utils.countries import SELECT_COUNTRIES_LIST12 13 14def build_custom_user_fields(15 form_cls,16 include_entries=False,17 fields_kwargs=None,18 field_entries_kwargs=None,19 blacklisted_items=(),20):21 """22 Function used to reinject values back into forms for accessing by themes23 """24 if fields_kwargs is None:25 fields_kwargs = {}26 if field_entries_kwargs is None:27 field_entries_kwargs = {}28 29 fields = []30 new_fields = UserFields.query.filter_by(**fields_kwargs).all()31 user_fields = {}32 33 # Only include preexisting values if asked34 if include_entries is True:35 for f in UserFieldEntries.query.filter_by(**field_entries_kwargs).all():36 user_fields[f.field_id] = f.value37 38 for field in new_fields:39 if field.name.lower() in blacklisted_items:40 continue41 42 form_field = getattr(form_cls, f"fields[{field.id}]")43 44 # Add the field_type to the field so we know how to render it45 form_field.field_type = field.field_type46 47 # Only include preexisting values if asked48 if include_entries is True:49 initial = user_fields.get(field.id, "")50 form_field.data = initial51 if form_field.render_kw:52 form_field.render_kw["data-initial"] = initial53 else:54 form_field.render_kw = {"data-initial": initial}55 56 fields.append(form_field)57 return fields58 59 60def attach_custom_user_fields(form_cls, **kwargs):61 """62 Function used to attach form fields to wtforms.63 Not really a great solution but is approved by wtforms.64 65 https://wtforms.readthedocs.io/en/2.3.x/specific_problems/#dynamic-form-composition66 """67 new_fields = UserFields.query.filter_by(**kwargs).all()68 for field in new_fields:69 validators = []70 if field.required:71 validators.append(InputRequired())72 73 if field.field_type == "text":74 input_field = StringField(75 field.name, description=field.description, validators=validators76 )77 elif field.field_type == "boolean":78 input_field = BooleanField(79 field.name, description=field.description, validators=validators80 )81 82 setattr(form_cls, f"fields[{field.id}]", input_field)83 84 85def build_registration_code_field(form_cls):86 """87 Build the appropriate field so we can render it via the extra property.88 Add field_type so Jinja knows how to render it.89 """90 if Configs.registration_code:91 field = getattr(form_cls, "registration_code", None) # noqa B00992 field.field_type = "text"93 return [field]94 else:95 return []96 97 98def attach_registration_code_field(form_cls):99 """100 If we have a registration code required, we attach it to the form similar101 to attach_custom_user_fields102 """103 if Configs.registration_code:104 setattr( # noqa B010105 form_cls,106 "registration_code",107 StringField(108 "Registration Code",109 description="Registration code required to create account",110 validators=[InputRequired()],111 ),112 )113 114 115def build_user_bracket_field(form_cls, value=None):116 field = getattr(form_cls, "bracket_id", None) # noqa B009117 if field:118 field.field_type = "select"119 field.process_data(value)120 return [field]121 else:122 return []123 124 125def attach_user_bracket_field(form_cls):126 brackets = Brackets.query.filter_by(type="users").all()127 if brackets:128 choices = [("", "")] + [129 (bracket.id, f"{bracket.name} - {bracket.description}")130 for bracket in brackets131 ]132 select_field = SelectField(133 _l("Bracket"),134 description=_l("Competition bracket for your user"),135 choices=choices,136 validators=[InputRequired()],137 )138 setattr(form_cls, "bracket_id", select_field) # noqa B010139 140 141class UserSearchForm(BaseForm):142 field = SelectField(143 "Search Field",144 choices=[145 ("name", "Name"),146 ("id", "ID"),147 ("email", "Email"),148 ("affiliation", "Affiliation"),149 ("website", "Website"),150 ("ip", "IP Address"),151 ],152 default="name",153 validators=[InputRequired()],154 )155 q = StringField("Parameter", validators=[InputRequired()])156 submit = SubmitField("Search")157 158 159class PublicUserSearchForm(BaseForm):160 field = SelectField(161 _l("Search Field"),162 choices=[163 ("name", _l("Name")),164 ("affiliation", _l("Affiliation")),165 ("website", _l("Website")),166 ],167 default="name",168 validators=[InputRequired()],169 )170 q = StringField(171 _l("Parameter"),172 description=_l("Search for matching users"),173 validators=[InputRequired()],174 )175 submit = SubmitField(_l("Search"))176 177 178class UserBaseForm(BaseForm):179 name = StringField("User Name", validators=[InputRequired()])180 email = EmailField("Email", validators=[InputRequired()])181 language = SelectField(_l("Language"), choices=SELECT_LANGUAGE_LIST)182 password = PasswordField("Password")183 website = StringField("Website")184 affiliation = StringField("Affiliation")185 country = SelectField("Country", choices=SELECT_COUNTRIES_LIST)186 type = SelectField("Type", choices=[("user", "User"), ("admin", "Admin")])187 verified = BooleanField("Verified")188 hidden = BooleanField("Hidden")189 banned = BooleanField("Banned")190 change_password = BooleanField("Require password change on next login")191 submit = SubmitField("Submit")192 193 194def UserEditForm(*args, **kwargs):195 class _UserEditForm(UserBaseForm):196 pass197 198 @property199 def extra(self):200 return build_custom_user_fields(201 self,202 include_entries=True,203 fields_kwargs=None,204 field_entries_kwargs={"user_id": self.obj.id},205 ) + build_user_bracket_field(self, value=self.obj.bracket_id)206 207 def __init__(self, *args, **kwargs):208 """209 Custom init to persist the obj parameter to the rest of the form210 """211 super().__init__(*args, **kwargs)212 obj = kwargs.get("obj")213 if obj:214 self.obj = obj215 216 attach_custom_user_fields(_UserEditForm)217 attach_user_bracket_field(_UserEditForm)218 219 return _UserEditForm(*args, **kwargs)220 221 222def UserCreateForm(*args, **kwargs):223 class _UserCreateForm(UserBaseForm):224 notify = BooleanField("Email account credentials to user", default=True)225 226 @property227 def extra(self):228 return build_custom_user_fields(229 self, include_entries=False230 ) + build_user_bracket_field(self)231 232 attach_custom_user_fields(_UserCreateForm)233 attach_user_bracket_field(_UserCreateForm)234 235 return _UserCreateForm(*args, **kwargs)236 