Source code for timApp.user.contacts
from dataclasses import field
from flask import Response
from sqlalchemy import func
from sqlalchemy.orm import load_only
from timApp.auth.accesshelper import verify_logged_in
from timApp.auth.sessioninfo import get_current_user_object
from timApp.messaging.messagelist.listinfo import Channel
from timApp.timdb.sqa import db
from timApp.user.usercontact import UserContact, ContactOrigin, PrimaryContact
from timApp.user.verification.verification import (
resend_verification,
ContactAddVerification,
request_verification,
SetPrimaryContactVerification,
)
from timApp.util.flask.requesthelper import RouteException
from timApp.util.flask.responsehelper import json_response, ok_response
from timApp.util.flask.typedblueprint import TypedBlueprint
from timApp.util.utils import is_valid_email
contacts = TypedBlueprint("contacts", __name__, url_prefix="/contacts")
[docs]@contacts.post("/add")
def add_contact(
contact_info: str,
contact_info_type: Channel = field(metadata={"by_value": True}),
resend_if_exists: bool = False,
) -> Response:
"""Add a new contact information for current user.
:param contact_info_type: The channel user wishes to add a new contact information.
:param contact_info: The contact information.
:param resend_if_exists: If True and verification already exists, resend the verification message.
:return: OK response.
"""
verify_logged_in()
user = get_current_user_object()
# Check for duplicate contact information.
existing_contact_info = user.get_contact(
contact_info_type,
contact_info,
[load_only(UserContact.id, UserContact.verified)],
)
if existing_contact_info:
# If the contact info already exists and is verified by the user, resend verification
if existing_contact_info.verified:
raise RouteException("The contact is already added")
if not resend_if_exists:
raise RouteException(
"The contact is already added but is pending verification"
)
verification = ContactAddVerification.query.filter_by(
contact=existing_contact_info, reacted_at=None
).first()
if verification:
resend_verification(verification, existing_contact_info.contact)
else:
# Verification was cleaned after it wasn't used for a while, create a new one
request_verification(
ContactAddVerification(user=user, contact=existing_contact_info),
existing_contact_info.contact,
)
db.session.commit()
return json_response({"requireVerification": False})
# Add appropriate contact info.
require_verification = False
if contact_info_type == Channel.EMAIL:
require_verification = True
if not is_valid_email(contact_info):
raise RouteException("Email format is invalid")
uc = UserContact(
user=user,
contact=contact_info,
channel=Channel.EMAIL,
verified=False,
contact_origin=ContactOrigin.Custom,
)
db.session.add(uc)
request_verification(ContactAddVerification(user=user, contact=uc), uc.contact)
db.session.commit()
return json_response({"requireVerification": require_verification})
[docs]@contacts.post("/remove")
def remove_contact(
contact_info: str, contact_info_type: Channel = field(metadata={"by_value": True})
) -> Response:
"""Remove a contact information from current user.
:param contact_info_type: The channel user wishes to add a new contact information.
:param contact_info: The contact information.
:return: OK response.
"""
verify_logged_in()
user = get_current_user_object()
if contact_info_type == Channel.EMAIL and user.email == contact_info:
raise RouteException("Cannot remove primary email")
contact = user.get_contact(
contact_info_type,
contact_info,
[load_only(UserContact.id, UserContact.channel)],
)
if not contact:
raise RouteException("The contact does not exist for the user")
if contact.primary:
raise RouteException(
"Cannot remove a primary contact. Set a new primary contact before removing this one."
)
if contact.contact_origin != ContactOrigin.Custom:
raise RouteException("Cannot remove managed contacts")
verified_emails_count = (
db.session.query(func.count(UserContact.id))
.filter_by(
user_id=user.id,
channel=Channel.EMAIL,
verified=True,
contact_origin=ContactOrigin.Custom,
)
.scalar()
)
if contact.verified and verified_emails_count == 1:
raise RouteException(
"Removing the email will leave the user with no verified user-added emails. "
"Add at least one verified email before removing this one."
)
db.session.delete(contact)
db.session.commit()
return ok_response()
[docs]@contacts.post("/primary")
def set_primary(
contact: str, channel: Channel = field(metadata={"by_value": True})
) -> Response:
"""Set the primary contact.
:param contact: Primary contact value.
:param channel: Primary contact channel.
:return: OK response.
"""
verify_logged_in()
user = get_current_user_object()
existing_contact = user.get_contact(
channel,
contact,
[load_only(UserContact.id, UserContact.contact, UserContact.primary)],
)
if not existing_contact:
raise RouteException("The contact does not exist for the user")
if existing_contact.primary:
json_response({"verify": False})
primary_contact_exists = db.session.query(
UserContact.query.filter_by(
channel=channel, contact=contact, primary=PrimaryContact.true
).exists()
).scalar()
if primary_contact_exists:
raise RouteException(
"Another user already has this contact set to primary, please choose another contact"
)
existing_verification = SetPrimaryContactVerification.query.filter_by(
contact=existing_contact,
reacted_at=None,
).first()
if existing_verification:
resend_verification(existing_verification)
return json_response({"verify": True})
current_primary = user.primary_email_contact
need_verify = False
if not current_primary:
user.email = existing_contact.contact
else:
request_verification(
SetPrimaryContactVerification(user=user, contact=existing_contact)
)
need_verify = True
db.session.commit()
return json_response({"verify": need_verify})