Skip to content

Commit

Permalink
Merge pull request #1101 from charmander/streamlined-signup-part-1
Browse files Browse the repository at this point in the history
Streamline signup (part 1)

1. **remove repeat/redundant inputs** (you are here)
2. replace date of birth entry with checkboxes
3. log in automatically after confirming e-mail address
4. handle repeated registration attempts/failure to receive verification e-mail better

Reviewed-by: Kyra Kitsune <kfkitsune@users.noreply.github.com>
  • Loading branch information
charmander committed Jul 28, 2021
2 parents 8c7b675 + 9b7e8be commit 7e63c6d
Show file tree
Hide file tree
Showing 13 changed files with 146 additions and 251 deletions.
18 changes: 9 additions & 9 deletions weasyl/controllers/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,10 +259,12 @@ def control_username_post_(request):
@login_required
@disallow_api
def control_editemailpassword_get_(request):
profile_info = profile.select_manage(request.userid)

return Response(define.webpage(
request.userid,
"control/edit_emailpassword.html",
[profile.select_manage(request.userid)["email"]],
(profile_info["email"], profile_info["username"]),
title="Edit Password and Email Address"
))

Expand All @@ -271,18 +273,16 @@ def control_editemailpassword_get_(request):
@disallow_api
@token_checked
def control_editemailpassword_post_(request):
form = request.web_input(newemail="", newemailcheck="", newpassword="", newpasscheck="", password="")

newemail = emailer.normalize_address(form.newemail)
newemailcheck = emailer.normalize_address(form.newemailcheck)
newemail = emailer.normalize_address(request.POST["newemail"])

# Check if the email was invalid; Both fields must be valid (not None), and have the form fields set
if not newemail and not newemailcheck and form.newemail != "" and form.newemailcheck != "":
if not newemail and request.POST["newemail"] != "":
raise WeasylError("emailInvalid")

return_message = profile.edit_email_password(
request.userid, form.username, form.password, newemail, newemailcheck,
form.newpassword, form.newpasscheck
userid=request.userid,
password=request.POST["password"],
newemail=newemail,
newpassword=request.POST["newpassword"],
)

if not return_message: # No changes were made
Expand Down
17 changes: 2 additions & 15 deletions weasyl/controllers/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,26 +194,14 @@ def signout_(request):

@guest_required
def signup_get_(request):
form = request.web_input(email="")

return Response(define.webpage(request.userid, "etc/signup.html", [
# Signup data
{
"email": form.email,
"username": None,
"day": None,
"month": None,
"year": None,
"error": None,
},
], title="Create a Weasyl Account"))
return Response(define.webpage(request.userid, "etc/signup.html", (), title="Create a Weasyl Account"))


@guest_required
@token_checked
def signup_post_(request):
form = request.web_input(
username="", password="", passcheck="", email="", emailcheck="",
username="", password="", email="",
day="", month="", year="")

login.create(form)
Expand Down Expand Up @@ -288,7 +276,6 @@ def resetpassword_post_(request):
resetpassword.reset(
token=request.POST['token'],
password=request.POST['password'],
passcheck=request.POST['passcheck'],
expect_userid=expect_userid,
address=request.client_addr,
)
Expand Down
3 changes: 0 additions & 3 deletions weasyl/errorcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@
"emailExists": "The email you entered is already taken by another user.",
"emailIncorrect": "The email you entered is not associated with the account you specified.",
"emailInvalid": "The email you entered does not appear to be valid.",
"emailMismatch": "The email you entered did not match the email confirmation.",
"embedlinkInvalid": "The embed link you entered does not point to a valid resource or supported service.",
"FeatureDisabled": "This feature has been temporarily disabled.",
"FileType": "The file you uploaded is not of a valid type.",
Expand All @@ -90,7 +89,6 @@
"insufficientActionPermissions": "You do not have permission to perform this action.",
"journalRecordMissing": journalid,
"logincreateRecordMissing": "That's not a valid token!",
"loginInvalid": "The login credentials you entered were not correct.",
"loginRecordMissing": (
"The username you entered does not appear to be correct. Check to make sure that you entered the "
"name correctly and that the account you are trying to recover the password for actually exists."),
Expand All @@ -110,7 +108,6 @@
"parentidInvalid": "The specified parent folder does not exist.",
"passwordIncorrect": "The password you entered was incorrect.",
"passwordInsecure": "Passwords must be at least 10 characters in length.",
"passwordMismatch": "The password you entered did not match the password confirmation.",
"priceidInvalid": "You did not specify a price to edit.",
"RatingExceeded": rating,
"ratingInvalid": "The specified rating is invalid.",
Expand Down
32 changes: 19 additions & 13 deletions weasyl/login.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,21 +200,14 @@ def _delete_expired():
d.engine.execute("DELETE FROM logincreate WHERE created_at < now() - INTERVAL '2 days'")


# form
# username email month
# password emailcheck year
# passcheck day

def create(form):
# Normalize form data
username = clean_display_name(form.username)
sysname = d.get_sysname(username)

email = emailer.normalize_address(form.email)
emailcheck = emailer.normalize_address(form.emailcheck)

password = form.password
passcheck = form.passcheck
if form.day and form.month and form.year:
try:
birthday = arrow.Arrow(int(form.year), int(form.month), int(form.day))
Expand All @@ -223,12 +216,6 @@ def create(form):
else:
birthday = None

# Check mismatched form data
if password != passcheck:
raise WeasylError("passwordMismatch")
if email != emailcheck:
raise WeasylError("emailMismatch")

# Check invalid form data
if birthday is None or d.age_in_years(birthday) < 13:
raise WeasylError("birthdayInvalid")
Expand Down Expand Up @@ -494,6 +481,25 @@ def is_email_blacklisted(address):
)


def authenticate_account_change(*, userid, password):
"""
Check a password against an account, throwing WeasylError('passwordIncorrect') if it doesn’t match.
Bans/suspensions and two-factor authentication aren’t checked.
Returns the account’s e-mail address, because it’s convenient.
"""
row = d.engine.execute(
"SELECT email, hashsum FROM login INNER JOIN authbcrypt USING (userid) WHERE userid = %(user)s",
{"user": userid},
).first()

if not bcrypt.checkpw(password.encode('utf-8'), row.hashsum.encode('ascii')):
raise WeasylError('passwordIncorrect')

return row.email


def verify_email_change(userid, token):
"""
Verify a user's email change request, updating the `login` record if it validates.
Expand Down
34 changes: 10 additions & 24 deletions weasyl/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -508,8 +508,7 @@ def edit_userinfo(userid, form):
d._get_all_config.invalidate(userid)


def edit_email_password(userid, username, password, newemail, newemailcheck,
newpassword, newpasscheck):
def edit_email_password(*, userid, password, newemail, newpassword):
"""
Edit the email address and/or password for a given Weasyl account.
Expand All @@ -519,40 +518,27 @@ def edit_email_password(userid, username, password, newemail, newemailcheck,
Parameters:
userid: The `userid` of the Weasyl account to modify.
username: User-entered username for password-based authentication.
password: The user's current plaintext password.
newemail: If changing the email on the account, the new email address. Optional.
newemailcheck: A verification field for the above to serve as a typo-check. Optional,
but mandatory if `newemail` provided.
newpassword: If changing the password, the user's new password. Optional.
newpasscheck: Verification field for `newpassword`. Optional, but mandatory if
`newpassword` provided.
"""
from weasyl import login

# Track if any changes were made for later display back to the user.
changes_made = ""

# Check that credentials are correct
logid, logerror = login.authenticate_bcrypt(username, password, request=None)

# Run checks prior to modifying anything...
if userid != logid or logerror is not None:
raise WeasylError("loginInvalid")
current_email = login.authenticate_account_change(
userid=userid,
password=password,
)

if newemail:
if newemail != newemailcheck:
raise WeasylError("emailMismatch")
# Track if any changes were made for later display back to the user.
changes_made = ""

if newpassword:
if newpassword != newpasscheck:
raise WeasylError("passwordMismatch")
elif not login.password_secure(newpassword):
raise WeasylError("passwordInsecure")
if newpassword and not login.password_secure(newpassword):
raise WeasylError("passwordInsecure")

# If we are setting a new email, then write the email into a holding table pending confirmation
# that the email is valid.
if newemail:
if newemail and newemail.lower() != current_email.lower():
# Only actually attempt to change the email if unused; prevent finding out if an email is already registered
if not login.email_exists(newemail):
token = security.generate_key(40)
Expand Down
6 changes: 2 additions & 4 deletions weasyl/resetpassword.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,12 @@ def prepare(token):
return reset_target


def reset(token, password, passcheck, expect_userid, address):
def reset(*, token, password, expect_userid, address):
from weasyl import login

token_sha256 = _hash_token(token)

if password != passcheck:
raise WeasylError("passwordMismatch")
elif not login.password_secure(password):
if not login.password_secure(password):
raise WeasylError("passwordInsecure")

with d.engine.begin() as db:
Expand Down
23 changes: 6 additions & 17 deletions weasyl/templates/control/edit_emailpassword.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
$def with (email)
$def with (email, username)
$:{TITLE("Edit Password and Email Address", "Settings", "/control")}
<div class="content">
<p style="padding-top:2em;font-size:1.2em;">On this page you can change your associated email address, password, or both; to change only one of the items, leave the other pair of fields blank. If you are changing your password, please remember to follow the security guidelines described below.</p>
<p style="padding-top:2em;font-size:1.2em;">On this page you can change your associated email address, password, or both; to change only one of the items, leave the other field blank.</p>

<form name="controleditemailpassword" class="form skinny clear" action="/control/editemailpassword" method="post">
$:{CSRF()}
Expand All @@ -10,31 +10,20 @@
<p id="currentemail">${email}</p>

<label for="emailpwnewemail">New Email Address</label>
<input type="email" class="input" name="newemail" id="emailpwnewemail" />

<label for="emailpwcheckemail">Retype New Email Address</label>
<input type="email" class="input last-input" name="newemailcheck" id="emailpwcheckemail" />

<div style="height:1em;"></div><hr /><div style="height:1em;"></div>
<input type="email" class="input" name="newemail" id="emailpwnewemail" autocomplete="email" autofocus />

<label for="emailpwnewpw">New Password</label>
<input type="password" class="input" name="newpassword" id="emailpwnewpw" maxlength="72" />
<input type="password" class="input" name="newpassword" id="emailpwnewpw" minlength="10" maxlength="72" autocomplete="new-password" />
<p class="color-lighter" style="padding-top: 0.5em;"><i>Passwords must be a minimum of 10 characters.</i></p>

<div id="password-strength" class="password-strength password-strength-empty" data-password-input="emailpwnewpw" data-personal-inputs="emailpwnewemail,emailpwuser"></div>

<label for="emailpwcheckpw">Retype New Password</label>
<input type="password" class="input last-input" name="newpasscheck" id="emailpwcheckpw" />
<div id="password-strength" class="password-strength password-strength-empty" data-password-input="emailpwnewpw" data-personal-inputs="emailpwnewemail" data-personal-data="${username};${email}"></div>

<div style="height:1em;"></div><hr /><div style="height:2em;"></div>

<p>In order to verify your identity, you must enter your current login information to continue.</p>

<label for="emailpwuser">Username</label>
<input type="text" class="input" name="username" id="emailpwuser" />

<label for="emailpwpw">Password</label>
<input type="password" class="input last-input" name="password" id="emailpwpw" />
<input type="password" class="input last-input" name="password" id="emailpwpw" autocomplete="current-password" required />

<script src="${resource_path('js/zxcvbn.js')}" async></script>
<script src="${resource_path('js/zxcvbn-check.js')}" async></script>
Expand Down
14 changes: 3 additions & 11 deletions weasyl/templates/etc/resetpassword.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,9 @@

<p>Resetting password for user <strong><a href="/~${LOGIN(reset_target.username)}">${reset_target.username}</a></strong>.</p>

<div class="form-2up clear">
<div class="form-2up-1">
<label for="password">New Password</label>
<input type="password" class="input" id="password" name="password" minlength="10" maxlength="72" required />
<p class="color-lighter" style="padding-top: 0.5em;"><i>Passwords must be a minimum of 10 characters.</i></p>
</div>
<div class="form-2up-2">
<label for="passcheck" class="color-lighter">Confirm Password</label>
<input type="password" class="input last-input" id="passcheck" name="passcheck" minlength="10" maxlength="72" required />
</div>
</div>
<label for="password">New Password</label>
<input type="password" class="input" id="password" name="password" minlength="10" maxlength="72" required />
<p class="color-lighter" style="padding-top: 0.5em;"><i>Passwords must be a minimum of 10 characters.</i></p>

<div id="password-strength" class="password-strength password-strength-empty" data-password-input="password" data-personal-data="${reset_target.username};${reset_target.email}"></div>

Expand Down
30 changes: 7 additions & 23 deletions weasyl/templates/etc/signup.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
$def with (query)
$def with ()
$:{RENDER("common/stage_title.html", ["Create a Weasyl Account"])}

<div id="signup-content" class="content">
Expand All @@ -7,32 +7,16 @@
$:{CSRF()}

<label for="signup-username">Username</label>
<input type="text" id="signup-username" class="input" name="username" autofocus required maxlength="25" />
<input type="text" id="signup-username" class="input" name="username" autofocus required maxlength="25" autocomplete="username" />

<div class="form-2up clear">
<div class="form-2up-1">
<label for="signup-password">Password</label>
<input type="password" id="signup-password" class="input" name="password" maxlength="72" required />
</div>
<div class="form-2up-2">
<label for="signup-passcheck" class="color-lighter">Confirm</label>
<input type="password" id="signup-passcheck" class="input" name="passcheck" maxlength="72" required />
</div>
</div>
<label for="signup-password">Password</label>
<input type="password" id="signup-password" class="input" name="password" minlength="10" maxlength="72" required autocomplete="new-password" />
<p class="color-lighter" style="padding-top: 0.5em;"><i>Passwords must be a minimum of 10 characters.</i></p>

<div id="password-strength" class="password-strength password-strength-empty" data-password-input="signup-password" data-personal-inputs="signup-username,signup-email,year"></div>

<div class="form-2up clear">
<div class="form-2up-1">
<label for="signup-email">Email Address</label>
<input type="email" id="signup-email" class="input" name="email" placeholder="john@doe.com" required="required" />
</div>
<div class="form-2up-2">
<label for="signup-emailcheck" class="color-lighter">Confirm</label>
<input type="email" id="signup-emailcheck" class="input" name="emailcheck" placeholder="john@doe.com" required="required" />
</div>
</div>
<label for="signup-email">Email Address</label>
<input type="email" id="signup-email" class="input" name="email" placeholder="john@doe.com" required autocomplete="email" />

<label for="signup-birthday">Date of Birth</label>
<div class="form-date clear">
Expand Down Expand Up @@ -83,7 +67,7 @@
By registering an account on Weasyl, you agree that you have fully read and will abide by both the
<a href="/policy/tos">Terms of Service</a> and the <a href="/policy/community">Community Guidelines</a>,
and that you agree to the terms outlined in the <a href="/policy/privacy">Privacy Policy</a>.
If you accept these policies and conditions, click the <strong>Create Account</strong> button to register;
If you accept these policies and conditions, click the <strong>Join Weasyl</strong> button to register;
otherwise, click <strong>Cancel</strong> to continue browsing as a guest.
</div>

Expand Down
Loading

0 comments on commit 7e63c6d

Please sign in to comment.