2016-10-13 17:05:37 +01:00
|
|
|
import json
|
|
|
|
|
|
2019-06-14 12:32:47 +01:00
|
|
|
from flask import (
|
|
|
|
|
abort,
|
|
|
|
|
current_app,
|
2021-05-14 18:42:57 +01:00
|
|
|
flash,
|
2019-06-14 12:32:47 +01:00
|
|
|
redirect,
|
|
|
|
|
render_template,
|
2021-05-14 18:42:57 +01:00
|
|
|
request,
|
2019-06-14 12:32:47 +01:00
|
|
|
session,
|
|
|
|
|
url_for,
|
|
|
|
|
)
|
2019-07-01 15:22:08 +01:00
|
|
|
from flask_login import current_user
|
2016-03-17 13:07:52 +00:00
|
|
|
|
2018-02-20 11:22:17 +00:00
|
|
|
from app import user_api_client
|
2023-04-26 11:14:25 -04:00
|
|
|
from app.event_handlers import (
|
|
|
|
|
create_email_change_event,
|
|
|
|
|
create_mobile_number_change_event,
|
|
|
|
|
)
|
2016-01-12 09:52:00 +00:00
|
|
|
from app.main import main
|
2016-03-17 13:07:52 +00:00
|
|
|
from app.main.forms import (
|
|
|
|
|
ChangeEmailForm,
|
|
|
|
|
ChangeMobileNumberForm,
|
2018-02-20 11:22:17 +00:00
|
|
|
ChangeNameForm,
|
|
|
|
|
ChangePasswordForm,
|
2023-12-04 14:52:48 -08:00
|
|
|
ChangePreferredTimezoneForm,
|
2018-02-20 11:22:17 +00:00
|
|
|
ConfirmPasswordForm,
|
2019-06-13 19:00:17 +01:00
|
|
|
ServiceOnOffSettingForm,
|
2018-05-07 22:53:36 +01:00
|
|
|
TwoFactorForm,
|
2016-03-17 13:07:52 +00:00
|
|
|
)
|
2019-05-23 15:27:35 +01:00
|
|
|
from app.models.user import User
|
2024-08-26 11:11:59 -07:00
|
|
|
from app.utils import hilite
|
2021-06-30 15:30:29 +01:00
|
|
|
from app.utils.user import user_is_gov_user, user_is_logged_in
|
2024-05-16 10:37:37 -04:00
|
|
|
from notifications_utils.url_safe_token import check_token
|
2016-10-28 11:45:05 +01:00
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
NEW_EMAIL = "new-email"
|
|
|
|
|
NEW_MOBILE = "new-mob"
|
|
|
|
|
NEW_MOBILE_PASSWORD_CONFIRMED = (
|
|
|
|
|
"new-mob-password-confirmed" # nosec B105 - this is not a password
|
|
|
|
|
)
|
2016-01-25 10:47:27 +00:00
|
|
|
|
2016-01-12 09:52:00 +00:00
|
|
|
|
|
|
|
|
@main.route("/user-profile")
|
2019-07-01 15:22:08 +01:00
|
|
|
@user_is_logged_in
|
2016-01-15 17:46:09 +00:00
|
|
|
def user_profile():
|
2016-10-28 11:45:05 +01:00
|
|
|
return render_template(
|
2023-08-25 09:12:23 -07:00
|
|
|
"views/user-profile.html",
|
2018-12-12 12:29:08 +00:00
|
|
|
can_see_edit=current_user.is_gov_user,
|
2016-10-28 11:45:05 +01:00
|
|
|
)
|
2016-01-12 10:28:14 +00:00
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
@main.route("/user-profile/name", methods=["GET", "POST"])
|
2019-07-01 15:22:08 +01:00
|
|
|
@user_is_logged_in
|
2016-01-15 17:46:09 +00:00
|
|
|
def user_profile_name():
|
2016-01-22 16:34:36 +00:00
|
|
|
form = ChangeNameForm(new_name=current_user.name)
|
2016-01-12 10:28:14 +00:00
|
|
|
|
2016-01-22 16:34:36 +00:00
|
|
|
if form.validate_on_submit():
|
2019-05-23 15:27:35 +01:00
|
|
|
current_user.update(name=form.new_name.data)
|
2023-08-25 09:12:23 -07:00
|
|
|
return redirect(url_for(".user_profile"))
|
2016-01-12 10:28:14 +00:00
|
|
|
|
2016-01-22 16:34:36 +00:00
|
|
|
return render_template(
|
2023-08-25 09:12:23 -07:00
|
|
|
"views/user-profile/change.html", thing="name", form_field=form.new_name
|
2016-01-22 16:34:36 +00:00
|
|
|
)
|
|
|
|
|
|
2016-01-12 10:28:14 +00:00
|
|
|
|
2023-12-04 14:52:48 -08:00
|
|
|
@main.route("/user-profile/preferred_timezone", methods=["GET", "POST"])
|
|
|
|
|
@user_is_logged_in
|
|
|
|
|
def user_profile_preferred_timezone():
|
|
|
|
|
form = ChangePreferredTimezoneForm(new_name=current_user.preferred_timezone)
|
|
|
|
|
|
|
|
|
|
if form.validate_on_submit():
|
|
|
|
|
current_user.update(preferred_timezone=form.new_preferred_timezone.data)
|
|
|
|
|
current_user.preferred_timezone = form.new_preferred_timezone.data
|
|
|
|
|
return redirect(url_for(".user_profile"))
|
|
|
|
|
return render_template(
|
|
|
|
|
"views/user-profile/change.html",
|
|
|
|
|
thing="preferred timezone",
|
|
|
|
|
form_field=form.new_preferred_timezone,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
@main.route("/user-profile/email", methods=["GET", "POST"])
|
2019-07-01 15:22:08 +01:00
|
|
|
@user_is_logged_in
|
2018-12-12 13:10:46 +00:00
|
|
|
@user_is_gov_user
|
2016-01-15 17:46:09 +00:00
|
|
|
def user_profile_email():
|
2023-08-25 09:12:23 -07:00
|
|
|
form = ChangeEmailForm(
|
|
|
|
|
User.already_registered, email_address=current_user.email_address
|
|
|
|
|
)
|
2016-01-12 10:28:14 +00:00
|
|
|
|
2016-01-22 16:34:36 +00:00
|
|
|
if form.validate_on_submit():
|
2016-01-25 10:47:27 +00:00
|
|
|
session[NEW_EMAIL] = form.email_address.data
|
2023-08-25 09:12:23 -07:00
|
|
|
return redirect(url_for(".user_profile_email_authenticate"))
|
2016-01-22 16:34:36 +00:00
|
|
|
return render_template(
|
2023-08-25 09:12:23 -07:00
|
|
|
"views/user-profile/change.html",
|
|
|
|
|
thing="email address",
|
|
|
|
|
form_field=form.email_address,
|
2016-01-22 16:34:36 +00:00
|
|
|
)
|
|
|
|
|
|
2016-01-12 11:25:46 +00:00
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
@main.route("/user-profile/email/authenticate", methods=["GET", "POST"])
|
2019-07-01 15:22:08 +01:00
|
|
|
@user_is_logged_in
|
2016-01-15 17:46:09 +00:00
|
|
|
def user_profile_email_authenticate():
|
2016-01-22 16:34:36 +00:00
|
|
|
# Validate password for form
|
|
|
|
|
def _check_password(pwd):
|
2016-03-30 09:58:10 +01:00
|
|
|
return user_api_client.verify_password(current_user.id, pwd)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
2016-01-22 16:34:36 +00:00
|
|
|
form = ConfirmPasswordForm(_check_password)
|
2016-01-12 11:25:46 +00:00
|
|
|
|
2016-01-25 10:47:27 +00:00
|
|
|
if NEW_EMAIL not in session:
|
2023-08-25 09:12:23 -07:00
|
|
|
return redirect("main.user_profile_email")
|
2016-01-25 10:47:27 +00:00
|
|
|
|
2016-01-22 16:34:36 +00:00
|
|
|
if form.validate_on_submit():
|
2023-08-25 09:12:23 -07:00
|
|
|
user_api_client.send_change_email_verification(
|
|
|
|
|
current_user.id, session[NEW_EMAIL]
|
|
|
|
|
)
|
2023-04-25 19:36:38 -04:00
|
|
|
create_email_change_event(
|
|
|
|
|
user_id=current_user.id,
|
|
|
|
|
updated_by_id=current_user.id,
|
2023-04-26 11:14:25 -04:00
|
|
|
original_email_address=current_user.email_address,
|
2023-04-25 19:36:38 -04:00
|
|
|
new_email_address=session[NEW_EMAIL],
|
|
|
|
|
)
|
2023-08-25 09:12:23 -07:00
|
|
|
return render_template(
|
|
|
|
|
"views/change-email-continue.html", new_email=session[NEW_EMAIL]
|
|
|
|
|
)
|
2016-01-12 10:28:14 +00:00
|
|
|
|
2016-01-22 16:34:36 +00:00
|
|
|
return render_template(
|
2023-08-25 09:12:23 -07:00
|
|
|
"views/user-profile/authenticate.html",
|
|
|
|
|
thing="email address",
|
2016-01-22 16:34:36 +00:00
|
|
|
form=form,
|
2023-08-25 09:12:23 -07:00
|
|
|
back_link=url_for(".user_profile_email"),
|
2016-01-22 16:34:36 +00:00
|
|
|
)
|
|
|
|
|
|
2016-01-12 10:28:14 +00:00
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
@main.route("/user-profile/email/confirm/<token>", methods=["GET"])
|
2019-07-01 15:22:08 +01:00
|
|
|
@user_is_logged_in
|
2016-10-13 17:05:37 +01:00
|
|
|
def user_profile_email_confirm(token):
|
2023-08-25 09:12:23 -07:00
|
|
|
token_data = check_token(
|
|
|
|
|
token,
|
|
|
|
|
current_app.config["SECRET_KEY"],
|
|
|
|
|
current_app.config["DANGEROUS_SALT"],
|
|
|
|
|
current_app.config["EMAIL_EXPIRY_SECONDS"],
|
|
|
|
|
)
|
2016-10-13 17:05:37 +01:00
|
|
|
token_data = json.loads(token_data)
|
2023-08-25 09:12:23 -07:00
|
|
|
user = User.from_id(token_data["user_id"])
|
|
|
|
|
user.update(email_address=token_data["email"])
|
2016-10-14 14:46:31 +01:00
|
|
|
session.pop(NEW_EMAIL, None)
|
2016-10-13 17:05:37 +01:00
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
return redirect(url_for(".user_profile"))
|
2016-10-13 17:05:37 +01:00
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
@main.route("/user-profile/mobile-number", methods=["GET", "POST"])
|
2022-02-23 18:31:00 +00:00
|
|
|
@main.route(
|
|
|
|
|
"/user-profile/mobile-number/delete",
|
2023-08-25 09:12:23 -07:00
|
|
|
methods=["GET"],
|
|
|
|
|
endpoint="user_profile_confirm_delete_mobile_number",
|
2022-02-23 18:31:00 +00:00
|
|
|
)
|
2019-07-01 15:22:08 +01:00
|
|
|
@user_is_logged_in
|
2016-01-15 17:46:09 +00:00
|
|
|
def user_profile_mobile_number():
|
2022-02-23 18:31:00 +00:00
|
|
|
user = User.from_id(current_user.id)
|
2016-01-22 16:34:36 +00:00
|
|
|
form = ChangeMobileNumberForm(mobile_number=current_user.mobile_number)
|
2016-01-12 10:28:14 +00:00
|
|
|
|
2016-01-22 16:34:36 +00:00
|
|
|
if form.validate_on_submit():
|
2016-01-25 10:47:27 +00:00
|
|
|
session[NEW_MOBILE] = form.mobile_number.data
|
2023-08-25 09:12:23 -07:00
|
|
|
return redirect(url_for(".user_profile_mobile_number_authenticate"))
|
2016-01-12 11:25:46 +00:00
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
if request.endpoint == "main.user_profile_confirm_delete_mobile_number":
|
|
|
|
|
flash(
|
|
|
|
|
"Are you sure you want to delete your mobile number from Notify?", "delete"
|
|
|
|
|
)
|
2022-02-23 18:31:00 +00:00
|
|
|
|
2016-01-22 16:34:36 +00:00
|
|
|
return render_template(
|
2023-08-25 09:12:23 -07:00
|
|
|
"views/user-profile/change.html",
|
|
|
|
|
thing="mobile number",
|
2022-02-23 18:31:00 +00:00
|
|
|
form_field=form.mobile_number,
|
2023-08-25 09:12:23 -07:00
|
|
|
user_auth=user.auth_type,
|
2016-01-22 16:34:36 +00:00
|
|
|
)
|
|
|
|
|
|
2016-01-12 11:25:46 +00:00
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
@main.route("/user-profile/mobile-number/delete", methods=["POST"])
|
2022-02-23 18:31:00 +00:00
|
|
|
@user_is_logged_in
|
|
|
|
|
def user_profile_mobile_number_delete():
|
2023-08-25 09:12:23 -07:00
|
|
|
if current_user.auth_type != "email_auth":
|
2022-02-23 18:31:00 +00:00
|
|
|
abort(403)
|
|
|
|
|
|
|
|
|
|
current_user.update(mobile_number=None)
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
return redirect(url_for(".user_profile"))
|
2022-02-23 18:31:00 +00:00
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
@main.route("/user-profile/mobile-number/authenticate", methods=["GET", "POST"])
|
2019-07-01 15:22:08 +01:00
|
|
|
@user_is_logged_in
|
2016-01-15 17:46:09 +00:00
|
|
|
def user_profile_mobile_number_authenticate():
|
2016-01-12 11:25:46 +00:00
|
|
|
|
2016-01-25 10:47:27 +00:00
|
|
|
if NEW_MOBILE not in session:
|
2023-08-25 09:12:23 -07:00
|
|
|
return redirect(url_for(".user_profile_mobile_number"))
|
2016-01-25 10:47:27 +00:00
|
|
|
|
2024-08-19 10:57:08 -07:00
|
|
|
session[NEW_MOBILE_PASSWORD_CONFIRMED] = True
|
|
|
|
|
current_user.send_verify_code(to=session[NEW_MOBILE])
|
|
|
|
|
create_mobile_number_change_event(
|
|
|
|
|
user_id=current_user.id,
|
|
|
|
|
updated_by_id=current_user.id,
|
|
|
|
|
original_mobile_number=current_user.mobile_number,
|
|
|
|
|
new_mobile_number=session[NEW_MOBILE],
|
2016-01-22 16:34:36 +00:00
|
|
|
)
|
2024-08-19 10:57:08 -07:00
|
|
|
return redirect(url_for(".user_profile_mobile_number_confirm"))
|
|
|
|
|
|
2016-01-12 10:28:14 +00:00
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
@main.route("/user-profile/mobile-number/confirm", methods=["GET", "POST"])
|
2019-07-01 15:22:08 +01:00
|
|
|
@user_is_logged_in
|
2016-01-15 17:46:09 +00:00
|
|
|
def user_profile_mobile_number_confirm():
|
2016-01-27 12:22:32 +00:00
|
|
|
# Validate verify code for form
|
|
|
|
|
def _check_code(cde):
|
2023-08-25 09:12:23 -07:00
|
|
|
return user_api_client.check_verify_code(current_user.id, cde, "sms")
|
2016-01-27 12:22:32 +00:00
|
|
|
|
|
|
|
|
if NEW_MOBILE_PASSWORD_CONFIRMED not in session:
|
2023-08-25 09:12:23 -07:00
|
|
|
return redirect(url_for(".user_profile_mobile_number"))
|
2016-01-27 12:22:32 +00:00
|
|
|
|
2018-05-07 22:53:36 +01:00
|
|
|
form = TwoFactorForm(_check_code)
|
2016-01-12 10:28:14 +00:00
|
|
|
|
2016-01-22 16:34:36 +00:00
|
|
|
if form.validate_on_submit():
|
2019-05-23 15:27:35 +01:00
|
|
|
current_user.refresh_session_id()
|
2016-11-03 11:20:24 +00:00
|
|
|
mobile_number = session[NEW_MOBILE]
|
2016-01-25 10:47:27 +00:00
|
|
|
del session[NEW_MOBILE]
|
|
|
|
|
del session[NEW_MOBILE_PASSWORD_CONFIRMED]
|
2019-05-23 15:27:35 +01:00
|
|
|
current_user.update(mobile_number=mobile_number)
|
2023-08-25 09:12:23 -07:00
|
|
|
return redirect(url_for(".user_profile"))
|
2016-01-12 10:28:14 +00:00
|
|
|
|
2016-01-22 16:34:36 +00:00
|
|
|
return render_template(
|
2023-08-25 09:12:23 -07:00
|
|
|
"views/user-profile/confirm.html",
|
2016-01-22 16:34:36 +00:00
|
|
|
form_field=form.sms_code,
|
2023-08-25 09:12:23 -07:00
|
|
|
thing="mobile number",
|
2016-01-22 16:34:36 +00:00
|
|
|
)
|
|
|
|
|
|
2016-01-12 10:28:14 +00:00
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
@main.route("/user-profile/password", methods=["GET", "POST"])
|
2019-07-01 15:22:08 +01:00
|
|
|
@user_is_logged_in
|
2016-01-15 17:46:09 +00:00
|
|
|
def user_profile_password():
|
2016-01-27 12:22:32 +00:00
|
|
|
# Validate password for form
|
|
|
|
|
def _check_password(pwd):
|
2016-03-30 09:58:10 +01:00
|
|
|
return user_api_client.verify_password(current_user.id, pwd)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
2016-01-27 12:22:32 +00:00
|
|
|
form = ChangePasswordForm(_check_password)
|
2016-01-12 10:28:14 +00:00
|
|
|
|
2016-01-22 16:34:36 +00:00
|
|
|
if form.validate_on_submit():
|
2023-08-25 09:12:23 -07:00
|
|
|
user_api_client.update_password(
|
|
|
|
|
current_user.id, password=form.new_password.data
|
|
|
|
|
)
|
|
|
|
|
return redirect(url_for(".user_profile"))
|
2016-01-22 16:34:36 +00:00
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
return render_template("views/user-profile/change-password.html", form=form)
|
2019-06-13 19:00:17 +01:00
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
@main.route("/user-profile/disable-platform-admin-view", methods=["GET", "POST"])
|
2019-07-01 15:22:08 +01:00
|
|
|
@user_is_logged_in
|
2019-06-14 12:32:47 +01:00
|
|
|
def user_profile_disable_platform_admin_view():
|
2023-08-25 09:12:23 -07:00
|
|
|
if not current_user.platform_admin and not session.get(
|
|
|
|
|
"disable_platform_admin_view"
|
|
|
|
|
):
|
2019-06-13 19:00:17 +01:00
|
|
|
abort(403)
|
|
|
|
|
|
|
|
|
|
form = ServiceOnOffSettingForm(
|
2021-01-15 11:10:00 +00:00
|
|
|
name="Use platform admin view",
|
2023-08-25 09:12:23 -07:00
|
|
|
enabled=not session.get("disable_platform_admin_view"),
|
|
|
|
|
truthy="Yes",
|
|
|
|
|
falsey="No",
|
2019-06-13 19:00:17 +01:00
|
|
|
)
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
form.enabled.param_extensions = {
|
|
|
|
|
"hint": {"text": "Signing in again clears this setting"}
|
|
|
|
|
}
|
2021-01-15 11:10:00 +00:00
|
|
|
|
2019-06-13 19:00:17 +01:00
|
|
|
if form.validate_on_submit():
|
2023-08-25 09:12:23 -07:00
|
|
|
session["disable_platform_admin_view"] = not form.enabled.data
|
|
|
|
|
return redirect(url_for(".user_profile"))
|
2019-06-13 19:00:17 +01:00
|
|
|
|
|
|
|
|
return render_template(
|
2023-08-25 09:12:23 -07:00
|
|
|
"views/user-profile/disable-platform-admin-view.html", form=form
|
2019-06-13 19:00:17 +01:00
|
|
|
)
|
2024-08-26 09:02:39 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def set_timezone():
|
|
|
|
|
# Cookie is set in dashboard.html on page load
|
|
|
|
|
try:
|
|
|
|
|
timezone = request.cookies.get("timezone", "US/Eastern")
|
2024-08-27 12:47:15 -07:00
|
|
|
current_app.logger.debug(hilite(f"User's timezone is {timezone}"))
|
2024-08-26 09:02:39 -07:00
|
|
|
serialized_user = current_user.serialize()
|
|
|
|
|
if serialized_user["preferred_timezone"] is not timezone:
|
|
|
|
|
current_user.update(preferred_timezone=timezone)
|
|
|
|
|
except Exception:
|
|
|
|
|
current_app.logger.exception(hilite("Can't get timezone"))
|