mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-08-19 14:09:20 -04:00
merge main and fixed conflicts
This commit is contained in:
@@ -605,6 +605,15 @@ class RegisterUserForm(StripWhitespaceForm):
|
||||
auth_type = HiddenField("auth_type", default="sms_auth")
|
||||
|
||||
|
||||
class SetupUserProfileForm(StripWhitespaceForm):
|
||||
name = GovukTextInputField(
|
||||
"Full name", validators=[DataRequired(message="Cannot be empty")]
|
||||
)
|
||||
mobile_number = international_phone_number()
|
||||
# TODO This should be replaced with a select widget when one is available.
|
||||
preferred_timezone = HiddenField("preferred_timezone", default="US/Eastern")
|
||||
|
||||
|
||||
class RegisterUserFromInviteForm(RegisterUserForm):
|
||||
def __init__(self, invited_user):
|
||||
super().__init__(
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import os
|
||||
|
||||
from flask import abort, redirect, render_template, request, url_for
|
||||
from flask_login import current_user
|
||||
|
||||
@@ -8,6 +10,8 @@ from app.main.views.pricing import CURRENT_SMS_RATE
|
||||
from app.main.views.sub_navigation_dictionaries import features_nav, using_notify_nav
|
||||
from app.utils.user import user_is_logged_in
|
||||
|
||||
login_dot_gov_url = os.getenv("LOGIN_DOT_GOV_INITIAL_SIGNIN_URL")
|
||||
|
||||
|
||||
@main.route("/")
|
||||
def index():
|
||||
@@ -18,6 +22,7 @@ def index():
|
||||
"views/signedout.html",
|
||||
sms_rate=CURRENT_SMS_RATE,
|
||||
counts=status_api_client.get_count_of_live_services_and_organizations(),
|
||||
login_dot_gov_url=login_dot_gov_url,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ from notifications_utils.template import EmailPreviewTemplate, SMSBodyPreviewTem
|
||||
|
||||
from app import (
|
||||
current_service,
|
||||
format_datetime_short,
|
||||
format_datetime_table,
|
||||
notification_api_client,
|
||||
service_api_client,
|
||||
)
|
||||
@@ -94,7 +94,7 @@ def view_job_csv(service_id, job_id):
|
||||
mimetype="text/csv",
|
||||
headers={
|
||||
"Content-Disposition": 'inline; filename="{} - {}.csv"'.format(
|
||||
job.template["name"], format_datetime_short(job.created_at)
|
||||
job.template["name"], format_datetime_table(job.created_at)
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
@@ -1,14 +1,26 @@
|
||||
import uuid
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from flask import abort, redirect, render_template, session, url_for
|
||||
from flask import (
|
||||
abort,
|
||||
current_app,
|
||||
redirect,
|
||||
render_template,
|
||||
request,
|
||||
session,
|
||||
url_for,
|
||||
)
|
||||
from flask_login import current_user
|
||||
|
||||
from app import user_api_client
|
||||
from app.main import main
|
||||
from app.main.forms import (
|
||||
RegisterUserForm,
|
||||
RegisterUserFromInviteForm,
|
||||
RegisterUserFromOrgInviteForm,
|
||||
SetupUserProfileForm,
|
||||
)
|
||||
from app.main.views import sign_in
|
||||
from app.main.views.verify import activate_user
|
||||
from app.models.user import InvitedOrgUser, InvitedUser, User
|
||||
from app.utils import hide_from_search_engines
|
||||
@@ -120,4 +132,44 @@ def _do_registration(form, send_sms=True, send_email=True, organization_id=None)
|
||||
def registration_continue():
|
||||
if not session.get("user_details"):
|
||||
return redirect(url_for(".show_accounts_or_dashboard"))
|
||||
return render_template("views/registration-continue.html")
|
||||
else:
|
||||
raise Exception("Unexpected routing in registration_continue")
|
||||
|
||||
|
||||
@main.route("/set-up-your-profile", methods=["GET", "POST"])
|
||||
@hide_from_search_engines
|
||||
def set_up_your_profile():
|
||||
|
||||
form = SetupUserProfileForm()
|
||||
|
||||
if form.validate_on_submit():
|
||||
# start login.gov
|
||||
code = request.args.get("code")
|
||||
state = request.args.get("state")
|
||||
login_gov_error = request.args.get("error")
|
||||
if code and state:
|
||||
access_token = sign_in._get_access_token(code, state)
|
||||
user_email, user_uuid = sign_in._get_user_email_and_uuid(access_token)
|
||||
redirect_url = request.args.get("next")
|
||||
|
||||
elif login_gov_error:
|
||||
current_app.logger.error(f"login.gov error: {login_gov_error}")
|
||||
raise Exception(f"Could not login with login.gov {login_gov_error}")
|
||||
# end login.gov
|
||||
|
||||
# create the user
|
||||
# TODO we have to provide something for password until that column goes away
|
||||
# TODO ideally we would set the user's preferred timezone here as well
|
||||
user = User.register(
|
||||
name=form.name.data,
|
||||
email_address=user_email,
|
||||
mobile_number=form.mobile_number.data,
|
||||
password=str(uuid.uuid4()),
|
||||
auth_type="sms_auth",
|
||||
)
|
||||
|
||||
# activate the user
|
||||
user = user_api_client.get_user_by_uuid_or_email(user_uuid, user_email)
|
||||
activate_user(user["id"])
|
||||
return redirect(url_for("main.show_accounts_or_dashboard", next=redirect_url))
|
||||
return render_template("views/set-up-your-profile.html", form=form)
|
||||
|
||||
@@ -20,6 +20,7 @@ from flask_login import current_user
|
||||
from app import login_manager, user_api_client
|
||||
from app.main import main
|
||||
from app.main.forms import LoginForm
|
||||
from app.main.views.index import error
|
||||
from app.main.views.verify import activate_user
|
||||
from app.models.user import InvitedUser, User
|
||||
from app.utils import hide_from_search_engines
|
||||
@@ -77,9 +78,7 @@ def _get_user_email_and_uuid(access_token):
|
||||
return user_email, user_uuid
|
||||
|
||||
|
||||
@main.route("/sign-in", methods=(["GET", "POST"]))
|
||||
@hide_from_search_engines
|
||||
def sign_in():
|
||||
def _do_login_dot_gov():
|
||||
# start login.gov
|
||||
code = request.args.get("code")
|
||||
state = request.args.get("state")
|
||||
@@ -90,8 +89,13 @@ def sign_in():
|
||||
redirect_url = request.args.get("next")
|
||||
|
||||
# activate the user
|
||||
user = user_api_client.get_user_by_uuid_or_email(user_uuid, user_email)
|
||||
activate_user(user["id"])
|
||||
try:
|
||||
user = user_api_client.get_user_by_uuid_or_email(user_uuid, user_email)
|
||||
activate_user(user["id"])
|
||||
except BaseException as be: # noqa B036
|
||||
current_app.logger.error(be)
|
||||
error(401)
|
||||
|
||||
return redirect(url_for("main.show_accounts_or_dashboard", next=redirect_url))
|
||||
|
||||
elif login_gov_error:
|
||||
@@ -99,6 +103,11 @@ def sign_in():
|
||||
raise Exception(f"Could not login with login.gov {login_gov_error}")
|
||||
# end login.gov
|
||||
|
||||
|
||||
@main.route("/sign-in", methods=(["GET", "POST"]))
|
||||
@hide_from_search_engines
|
||||
def sign_in():
|
||||
_do_login_dot_gov()
|
||||
redirect_url = request.args.get("next")
|
||||
|
||||
if os.getenv("NOTIFY_E2E_TEST_EMAIL"):
|
||||
@@ -165,38 +174,15 @@ def sign_in():
|
||||
)
|
||||
|
||||
other_device = current_user.logged_in_elsewhere()
|
||||
notify_env = os.getenv("NOTIFY_ENVIRONMENT")
|
||||
current_app.logger.info("should render the sign in template")
|
||||
|
||||
# TODO REMOVE THIS INFO ONCE STAGING WORKS WITH LOGIN DOT GOV
|
||||
current_app.logger.info(f"NOTIFY ENV = {notify_env}")
|
||||
current_app.logger.info(
|
||||
f"LOGIN_DOT_GOV_CLIENT_ID={os.getenv('LOGIN_DOT_GOV_CLIENT_ID')}"
|
||||
)
|
||||
current_app.logger.info(
|
||||
f"LOGIN_DOT_GOV_USER_INFO_URL={os.getenv('LOGIN_DOT_GOV_USER_INFO_URL')}"
|
||||
)
|
||||
current_app.logger.info(
|
||||
f"LOGIN_DOT_GOV_ACCESS_TOKEN_URL={os.getenv('LOGIN_DOT_GOV_ACCESS_TOKEN_URL')}"
|
||||
)
|
||||
current_app.logger.info(
|
||||
f"LOGIN_DOT_GOV_LOGOUT_URL={os.getenv('LOGIN_DOT_GOV_LOGOUT_URL')}"
|
||||
)
|
||||
current_app.logger.info(
|
||||
f"LOGIN_DOT_GOV_BASE_LOGOUT_URL={os.getenv('LOGIN_DOT_GOV_BASE_LOGOUT_URL')}"
|
||||
)
|
||||
current_app.logger.info(
|
||||
f"LOGIN_DOT_GOV_SIGNOUT_REDIRECT={os.getenv('LOGIN_DOT_GOV_SIGNOUT_REDIRECT')}"
|
||||
)
|
||||
initial_signin_url = os.getenv("LOGIN_DOT_GOV_INITIAL_SIGNIN_URL")
|
||||
current_app.logger.info(f"LOGIN_DOT_GOV_INITIAL_SIGNIN_URL={initial_signin_url}")
|
||||
|
||||
return render_template(
|
||||
"views/signin.html",
|
||||
form=form,
|
||||
again=bool(redirect_url),
|
||||
other_device=other_device,
|
||||
login_gov_enabled=bool(notify_env in ["development", "staging", "demo"]),
|
||||
login_gov_enabled=True,
|
||||
password_reset_url=password_reset_url,
|
||||
initial_signin_url=initial_signin_url,
|
||||
)
|
||||
|
||||
@@ -6,10 +6,6 @@ from flask_login import current_user
|
||||
|
||||
from app.main import main
|
||||
|
||||
# ask login.gov if we really need manual logout and what's up with one hour sessions
|
||||
# ask login.gov how they recommend approaching dev environment
|
||||
# ask Tim Donaworth the same for #2
|
||||
|
||||
|
||||
def _sign_out_at_login_dot_gov():
|
||||
base_url = os.getenv("LOGIN_DOT_GOV_BASE_LOGOUT_URL")
|
||||
|
||||
@@ -5,6 +5,7 @@ from itsdangerous import SignatureExpired
|
||||
from notifications_utils.url_safe_token import check_token
|
||||
|
||||
from app import user_api_client
|
||||
from app.extensions import redis_client
|
||||
from app.main import main
|
||||
from app.main.forms import TwoFactorForm
|
||||
from app.models.user import InvitedOrgUser, InvitedUser, User
|
||||
@@ -64,20 +65,44 @@ def verify_email(token):
|
||||
|
||||
def activate_user(user_id):
|
||||
user = User.from_id(user_id)
|
||||
# the user will have a new current_session_id set by the API - store it in the cookie for future requests
|
||||
|
||||
# This is the login.gov path
|
||||
login_gov_invite_data = redis_client.get(f"service-invite-{user.email_address}")
|
||||
if login_gov_invite_data:
|
||||
login_gov_invite_data = json.loads(login_gov_invite_data.decode("utf8"))
|
||||
|
||||
# This is the deprecated path for organization invites where we get id from session
|
||||
session["current_session_id"] = user.current_session_id
|
||||
organization_id = session.get("organization_id")
|
||||
|
||||
activated_user = user.activate()
|
||||
activated_user.login()
|
||||
|
||||
# TODO when login.gov is mandatory, get rid of the if clause, it is deprecated.
|
||||
invited_user = InvitedUser.from_session()
|
||||
if invited_user:
|
||||
service_id = _add_invited_user_to_service(invited_user)
|
||||
return redirect(url_for("main.service_dashboard", service_id=service_id))
|
||||
elif login_gov_invite_data:
|
||||
service_id = login_gov_invite_data["service_id"]
|
||||
|
||||
user.add_to_service(
|
||||
service_id,
|
||||
login_gov_invite_data["permissions"],
|
||||
login_gov_invite_data["folder_permissions"],
|
||||
login_gov_invite_data["from_user_id"],
|
||||
)
|
||||
return redirect(url_for("main.service_dashboard", service_id=service_id))
|
||||
|
||||
# TODO when login.gov is mandatory, git rid of the if clause, it is deprecated.
|
||||
invited_org_user = InvitedOrgUser.from_session()
|
||||
if invited_org_user:
|
||||
user_api_client.add_user_to_organization(invited_org_user.organization, user_id)
|
||||
elif redis_client.get(f"organization-invite-{user.email_address}"):
|
||||
organization_id = redis_client.get(f"organization-invite-{user.email_address}")
|
||||
user_api_client.add_user_to_organization(
|
||||
organization_id.decode("utf8"), user_id
|
||||
)
|
||||
|
||||
if organization_id:
|
||||
return redirect(url_for("main.organization_dashboard", org_id=organization_id))
|
||||
|
||||
Reference in New Issue
Block a user