1
0
mirror of https://github.com/peeringdb/peeringdb.git synced 2024-05-11 05:55:09 +00:00
Files
peeringdb-peeringdb/peeringdb_server/forms.py
Stefan Pratter be9deaf2f8 Support 202309 (#1458)
* fixes #1260 - playwright tests
fixes #1394 - v2 search failing to find some names
fixes #1374 - Search to include new objects: Campus & Carrier
fixes #1164 - better rdap error reporting
fixes #1368 - Facility data export into Google Earth KMZ
fixes #1328 - Support web updates from a source of truth
fixes #1257 - Help text covers non-compliant email addresses
fixes #1313 - Improve email confirmation control - add 3 month option & maybe set new default value
fixes #1380 - Reset 'Social Media' to '[]' if field has no value

* linting

* remove target=_blank

* bump ES version to 8.10

* Cache and ES updates (#1459)

* elasticsearch major version pin and relock

* set decimal fields to python value on client save for load_data

* force use of redis password

* add default_meta to render

* add generated, clean up var names

* run pre-commit

* update ES for https and password

* rm cruft

* isort

---------

Co-authored-by: 20C <code@20c.com>
Co-authored-by: Matt Griswold <grizz@20c.com>
2023-10-24 12:17:03 -05:00

277 lines
7.8 KiB
Python

"""
Custom django forms.
Note: This does not includes forms pointed directly
at the REST api to handle updates (such as /net, /ix, /fac or /org endpoints).
Look in rest.py and serializers.py for those.
"""
import json
import os.path
import re
import uuid
import requests
from captcha.fields import CaptchaField
from captcha.models import CaptchaStore
from django import forms
from django.conf import settings as dj_settings
from django.contrib.auth import forms as auth_forms
from django.core.exceptions import ValidationError
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from grainy.const import PERM_CRUD, PERM_DENY, PERM_READ
from schema import Schema, SchemaError
from peeringdb_server.inet import get_client_ip
from peeringdb_server.models import Organization, User
class OrganizationAPIKeyForm(forms.Form):
name = forms.CharField()
email = forms.EmailField()
org_id = forms.IntegerField()
class OrgAdminUserPermissionForm(forms.Form):
entity = forms.CharField()
perms = forms.IntegerField()
def clean_perms(self):
perms = self.cleaned_data.get("perms")
if not perms & PERM_READ:
perms = perms | PERM_READ
if perms & PERM_DENY:
perms = perms ^ PERM_DENY
if perms > PERM_CRUD or perms < PERM_READ:
raise forms.ValidationError(_("Invalid permission level"))
return perms
class AffiliateToOrgForm(forms.Form):
asn = forms.CharField(required=False)
org = forms.CharField(required=False)
def clean_org(self):
org_id = self.cleaned_data.get("org")
if not org_id:
return 0
# if org id can be inted, an existing org id has been submitted
# otherwise an org name has been submitted that may or may not exist
try:
org_id = int(org_id)
if not Organization.objects.filter(id=org_id).exists():
if self.cleaned_data.get("asn"):
return 0
except ValueError:
try:
org = Organization.objects.get(name=org_id)
return org.id
except Organization.DoesNotExist:
self.cleaned_data["org_name"] = org_id
return 0
return org_id
def clean_asn(self):
asn = self.cleaned_data.get("asn")
if not asn:
return 0
try:
asn = int(re.sub(r"\D", r"", asn))
except ValueError:
raise forms.ValidationError(_("ASN needs to be a number"))
return asn
class PasswordChangeForm(forms.Form):
password = forms.CharField()
password_v = forms.CharField()
def clean_password(self):
password = self.cleaned_data.get("password")
if len(password) < 10:
raise forms.ValidationError(_("Needs to be at least 10 characters long"))
return password
def clean_password_v(self):
password = self.cleaned_data.get("password")
password_v = self.cleaned_data.get("password_v")
if password != password_v:
raise forms.ValidationError(
_("Passwords need to match"), code="password_mismatch"
)
return password_v
class UsernameChangeForm(forms.Form):
username = forms.CharField()
def clean_username(self):
username = self.cleaned_data.get("username")
if User.objects.filter(username=username).exists():
raise forms.ValidationError(_("This username is already taken"))
return username
class PasswordResetForm(forms.Form):
email = forms.EmailField()
class UsernameRetrieveForm(forms.Form):
email = forms.EmailField()
class UserCreationForm(auth_forms.UserCreationForm):
recaptcha = forms.CharField(required=False)
captcha = forms.CharField(required=False)
captcha_generator = CaptchaField(required=False)
require_captcha = True
class Meta:
model = User
fields = (
"username",
"email",
"first_name",
"last_name",
)
def clean(self):
super().clean()
recaptcha = self.cleaned_data.get("recaptcha", "")
captcha = self.cleaned_data.get("captcha", "")
if not self.require_captcha:
return
elif not recaptcha and not captcha:
raise forms.ValidationError(
_("Please fill out the anti-spam challenge (captcha) field")
)
elif recaptcha:
cpt_params = {
"secret": dj_settings.RECAPTCHA_SECRET_KEY,
"response": recaptcha,
"remoteip": get_client_ip(self.request),
}
cpt_response = requests.post(
dj_settings.RECAPTCHA_VERIFY_URL, params=cpt_params
).json()
if not cpt_response.get("success"):
raise forms.ValidationError(_("reCAPTCHA invalid"))
else:
try:
hashkey, value = captcha.split(":")
self.captcha_object = CaptchaStore.objects.get(
response=value, hashkey=hashkey, expiration__gt=timezone.now()
)
except CaptchaStore.DoesNotExist:
raise forms.ValidationError(_("captcha invalid"))
def delete_captcha(self):
captcha_object = getattr(self, "captcha_object", None)
if captcha_object:
captcha_object.delete()
class UserLocaleForm(forms.Form):
locale = forms.CharField()
def clean_locale(self):
loc = self.cleaned_data.get("locale")
# django.utils.translation.check_for_language() #lang_code
if loc:
return loc
return None
class Meta:
model = User
fields = "locale"
class VerifiedUpdateForm(forms.Form):
source = forms.CharField(required=False)
reason = forms.CharField(required=False)
updates = forms.JSONField(required=True)
def clean(self):
cleaned_data = super().clean()
updates = cleaned_data.get("updates")
schema = Schema([{"ref_tag": str, "obj_id": int, "data": dict}])
try:
updates = json.loads(json.dumps(updates))
schema.validate(updates)
except (json.JSONDecodeError, TypeError, SchemaError):
raise ValidationError("Malformed update data.")
class UserOrgForm(forms.Form):
"""
Sets primary organization of the user
"""
organization = forms.CharField()
def clean_org(self):
org = self.cleaned_data.get("organization")
if org:
return org
return None
class Meta:
model = User
fields = "primary_org"
class OrganizationLogoUploadForm(forms.ModelForm):
logo = forms.FileField()
class Meta:
model = Organization
fields = ["logo"]
def clean_logo(self):
logo = self.cleaned_data["logo"]
max_size = dj_settings.ORG_LOGO_MAX_SIZE
# normalize the file name
ext = os.path.splitext(logo.name)[1].lower()
randomize = str(uuid.uuid4())[:8]
logo.name = f"org-{self.instance.id}-{randomize}{ext}"
# validate file type
if ext not in dj_settings.ORG_LOGO_ALLOWED_FILE_TYPE.split(","):
raise ValidationError(
_("File type %(value)s not allowed"),
code="invalid",
params={"value": ext},
)
# validate file size
if logo.size > max_size:
raise ValidationError(
_("File size too big, max. %(value)s"),
code="invalid",
params={"value": f"{max_size / 1024:.0f} kb"},
)
return logo
class OrgUserOptions(forms.ModelForm):
class Meta:
model = Organization
fields = [
"require_2fa",
"restrict_user_emails",
"email_domains",
"periodic_reauth",
"periodic_reauth_period",
]