2024-04-04 18:09:22 -07:00
|
|
|
|
from flask import abort, flash, redirect, render_template, request, url_for
|
2019-07-01 15:22:08 +01:00
|
|
|
|
from flask_login import current_user
|
2024-04-04 18:09:22 -07:00
|
|
|
|
from markupsafe import Markup
|
2018-02-20 11:22:17 +00:00
|
|
|
|
|
|
|
|
|
|
from app import (
|
|
|
|
|
|
api_key_api_client,
|
|
|
|
|
|
current_service,
|
|
|
|
|
|
notification_api_client,
|
|
|
|
|
|
service_api_client,
|
|
|
|
|
|
)
|
2021-01-06 12:12:01 +00:00
|
|
|
|
from app.formatters import email_safe
|
2016-01-16 10:59:16 +00:00
|
|
|
|
from app.main import main
|
2020-08-03 15:10:02 +01:00
|
|
|
|
from app.main.forms import CallbackForm, CreateKeyForm, GuestList
|
2018-02-20 11:22:17 +00:00
|
|
|
|
from app.notify_client.api_key_api_client import (
|
|
|
|
|
|
KEY_TYPE_NORMAL,
|
|
|
|
|
|
KEY_TYPE_TEAM,
|
|
|
|
|
|
KEY_TYPE_TEST,
|
2017-12-08 10:52:38 +00:00
|
|
|
|
)
|
2021-06-09 13:19:05 +01:00
|
|
|
|
from app.utils.user import user_has_permissions
|
2016-01-19 09:55:13 +00:00
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
dummy_bearer_token = "bearer_token_set" # nosec B105 - this is not a real token
|
2017-12-04 15:07:11 +00:00
|
|
|
|
|
2016-01-19 09:55:13 +00:00
|
|
|
|
|
2019-11-04 11:07:55 +00:00
|
|
|
|
@main.route("/services/<uuid:service_id>/api")
|
2023-08-25 09:12:23 -07:00
|
|
|
|
@user_has_permissions("manage_api_keys")
|
2016-09-20 11:34:37 +01:00
|
|
|
|
def api_integration(service_id):
|
2017-12-08 10:52:38 +00:00
|
|
|
|
callbacks_link = (
|
2023-08-25 09:12:23 -07:00
|
|
|
|
".api_callbacks"
|
|
|
|
|
|
if current_service.has_permission("inbound_sms")
|
|
|
|
|
|
else ".delivery_status_callback"
|
2017-12-08 10:52:38 +00:00
|
|
|
|
)
|
2016-09-20 11:34:37 +01:00
|
|
|
|
return render_template(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"views/api/index.html",
|
2017-12-08 10:52:38 +00:00
|
|
|
|
callbacks_link=callbacks_link,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
api_notifications=notification_api_client.get_api_notifications_for_service(
|
|
|
|
|
|
service_id
|
|
|
|
|
|
),
|
2016-09-20 11:34:37 +01:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2019-11-04 11:07:55 +00:00
|
|
|
|
@main.route("/services/<uuid:service_id>/api/documentation")
|
2023-08-25 09:12:23 -07:00
|
|
|
|
@user_has_permissions("manage_api_keys")
|
2016-09-20 11:38:22 +01:00
|
|
|
|
def api_documentation(service_id):
|
2023-08-25 09:12:23 -07:00
|
|
|
|
return redirect(url_for(".documentation"), code=301)
|
2016-09-20 11:38:22 +01:00
|
|
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
@main.route(
|
|
|
|
|
|
"/services/<uuid:service_id>/api/whitelist",
|
|
|
|
|
|
methods=["GET", "POST"],
|
|
|
|
|
|
endpoint="old_guest_list",
|
|
|
|
|
|
)
|
|
|
|
|
|
@main.route("/services/<uuid:service_id>/api/guest-list", methods=["GET", "POST"])
|
|
|
|
|
|
@user_has_permissions("manage_api_keys")
|
2020-06-12 09:03:40 +01:00
|
|
|
|
def guest_list(service_id):
|
2020-06-12 09:02:40 +01:00
|
|
|
|
form = GuestList()
|
2016-09-20 12:30:00 +01:00
|
|
|
|
if form.validate_on_submit():
|
2023-08-25 09:12:23 -07:00
|
|
|
|
service_api_client.update_guest_list(
|
|
|
|
|
|
service_id,
|
|
|
|
|
|
{
|
|
|
|
|
|
"email_addresses": list(filter(None, form.email_addresses.data)),
|
|
|
|
|
|
"phone_numbers": list(filter(None, form.phone_numbers.data)),
|
|
|
|
|
|
},
|
|
|
|
|
|
)
|
|
|
|
|
|
flash("Guest list updated", "default_with_tick")
|
|
|
|
|
|
return redirect(url_for(".api_integration", service_id=service_id))
|
2016-09-20 12:30:00 +01:00
|
|
|
|
if not form.errors:
|
2020-06-12 09:05:26 +01:00
|
|
|
|
form.populate(**service_api_client.get_guest_list(service_id))
|
2023-08-25 09:12:23 -07:00
|
|
|
|
return render_template("views/api/guest-list.html", form=form)
|
2016-09-20 12:30:00 +01:00
|
|
|
|
|
|
|
|
|
|
|
2019-11-04 11:07:55 +00:00
|
|
|
|
@main.route("/services/<uuid:service_id>/api/keys")
|
2023-08-25 09:12:23 -07:00
|
|
|
|
@user_has_permissions("manage_api_keys")
|
2016-01-16 10:59:16 +00:00
|
|
|
|
def api_keys(service_id):
|
2016-01-19 09:55:13 +00:00
|
|
|
|
return render_template(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"views/api/keys.html",
|
2016-01-19 09:55:13 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
@main.route("/services/<uuid:service_id>/api/keys/create", methods=["GET", "POST"])
|
|
|
|
|
|
@user_has_permissions("manage_api_keys", restrict_admin_usage=True)
|
2016-01-19 09:55:13 +00:00
|
|
|
|
def create_api_key(service_id):
|
2018-11-13 09:57:17 +00:00
|
|
|
|
form = CreateKeyForm(current_service.api_keys)
|
2016-11-01 15:34:04 +00:00
|
|
|
|
form.key_type.choices = [
|
2023-08-25 09:12:23 -07:00
|
|
|
|
(KEY_TYPE_NORMAL, "Live – sends to anyone"),
|
|
|
|
|
|
(KEY_TYPE_TEAM, "Team and guest list – limits who you can send to"),
|
|
|
|
|
|
(KEY_TYPE_TEST, "Test – pretends to send messages"),
|
2016-11-01 15:34:04 +00:00
|
|
|
|
]
|
2021-01-12 15:04:54 +00:00
|
|
|
|
# preserve order of items extended by starting with empty dicts
|
2023-08-25 09:12:23 -07:00
|
|
|
|
form.key_type.param_extensions = {"items": [{}, {}]}
|
2018-07-20 08:42:01 +01:00
|
|
|
|
if current_service.trial_mode:
|
2023-08-25 09:12:23 -07:00
|
|
|
|
form.key_type.param_extensions["items"][0] = {
|
|
|
|
|
|
"disabled": True,
|
|
|
|
|
|
"hint": {
|
|
|
|
|
|
"html": Markup(
|
|
|
|
|
|
"Not available because your service is in "
|
|
|
|
|
|
'<a class="usa-link" href="/features/trial-mode">trial mode</a>'
|
|
|
|
|
|
)
|
|
|
|
|
|
},
|
2021-01-12 15:04:54 +00:00
|
|
|
|
}
|
2016-01-19 09:55:13 +00:00
|
|
|
|
if form.validate_on_submit():
|
2021-01-12 15:04:54 +00:00
|
|
|
|
if current_service.trial_mode and form.key_type.data == KEY_TYPE_NORMAL:
|
2016-11-01 15:34:04 +00:00
|
|
|
|
abort(400)
|
2016-07-06 15:10:36 +01:00
|
|
|
|
secret = api_key_api_client.create_api_key(
|
|
|
|
|
|
service_id=service_id,
|
|
|
|
|
|
key_name=form.key_name.data,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
key_type=form.key_type.data,
|
2016-07-06 15:10:36 +01:00
|
|
|
|
)
|
2016-10-07 10:59:32 +01:00
|
|
|
|
return render_template(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"views/api/keys/show.html",
|
2016-10-07 10:59:32 +01:00
|
|
|
|
secret=secret,
|
|
|
|
|
|
service_id=service_id,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
key_name=email_safe(form.key_name.data, whitespace="_"),
|
2016-10-07 10:59:32 +01:00
|
|
|
|
)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
return render_template("views/api/keys/create.html", form=form)
|
2016-01-19 09:55:13 +00:00
|
|
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
@main.route(
|
|
|
|
|
|
"/services/<uuid:service_id>/api/keys/revoke/<uuid:key_id>", methods=["GET", "POST"]
|
|
|
|
|
|
)
|
|
|
|
|
|
@user_has_permissions("manage_api_keys")
|
2016-01-19 09:55:13 +00:00
|
|
|
|
def revoke_api_key(service_id, key_id):
|
2023-08-25 09:12:23 -07:00
|
|
|
|
key_name = current_service.get_api_key(key_id)["name"]
|
|
|
|
|
|
if request.method == "GET":
|
|
|
|
|
|
flash(
|
|
|
|
|
|
[
|
|
|
|
|
|
"Are you sure you want to revoke ‘{}’?".format(key_name),
|
2023-08-31 08:21:28 -07:00
|
|
|
|
"You will not be able to use this API key to connect to Notify.gov.",
|
2023-08-25 09:12:23 -07:00
|
|
|
|
],
|
|
|
|
|
|
"revoke this API key",
|
|
|
|
|
|
)
|
2016-01-21 12:28:05 +00:00
|
|
|
|
return render_template(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"views/api/keys.html",
|
2016-01-21 12:28:05 +00:00
|
|
|
|
)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
elif request.method == "POST":
|
2016-01-20 17:32:55 +00:00
|
|
|
|
api_key_api_client.revoke_api_key(service_id=service_id, key_id=key_id)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
flash("‘{}’ was revoked".format(key_name), "default_with_tick")
|
|
|
|
|
|
return redirect(url_for(".api_keys", service_id=service_id))
|
2017-12-04 15:07:11 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_apis():
|
|
|
|
|
|
callback_api = None
|
|
|
|
|
|
inbound_api = None
|
2018-07-20 08:42:01 +01:00
|
|
|
|
if current_service.service_callback_api:
|
2017-12-04 15:07:11 +00:00
|
|
|
|
callback_api = service_api_client.get_service_callback_api(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
current_service.id, current_service.service_callback_api[0]
|
2017-12-04 15:07:11 +00:00
|
|
|
|
)
|
2018-07-20 08:42:01 +01:00
|
|
|
|
if current_service.inbound_api:
|
2017-12-04 15:07:11 +00:00
|
|
|
|
inbound_api = service_api_client.get_service_inbound_api(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
current_service.id, current_service.inbound_api[0]
|
2017-12-04 15:07:11 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
return (callback_api, inbound_api)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def check_token_against_dummy_bearer(token):
|
|
|
|
|
|
if token != dummy_bearer_token:
|
|
|
|
|
|
return token
|
|
|
|
|
|
else:
|
2023-08-25 09:12:23 -07:00
|
|
|
|
return ""
|
2017-12-04 15:07:11 +00:00
|
|
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
@main.route("/services/<uuid:service_id>/api/callbacks", methods=["GET"])
|
|
|
|
|
|
@user_has_permissions("manage_api_keys")
|
2017-12-04 15:07:11 +00:00
|
|
|
|
def api_callbacks(service_id):
|
2023-08-25 09:12:23 -07:00
|
|
|
|
if not current_service.has_permission("inbound_sms"):
|
|
|
|
|
|
return redirect(url_for(".delivery_status_callback", service_id=service_id))
|
2017-12-08 10:52:38 +00:00
|
|
|
|
|
2017-12-13 11:42:41 +00:00
|
|
|
|
delivery_status_callback, received_text_messages_callback = get_apis()
|
2017-12-08 10:52:38 +00:00
|
|
|
|
|
|
|
|
|
|
return render_template(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"views/api/callbacks.html",
|
2024-02-29 08:24:53 -08:00
|
|
|
|
received_text_messages_callback=(
|
|
|
|
|
|
received_text_messages_callback["url"]
|
|
|
|
|
|
if received_text_messages_callback
|
|
|
|
|
|
else None
|
|
|
|
|
|
),
|
|
|
|
|
|
delivery_status_callback=(
|
|
|
|
|
|
delivery_status_callback["url"] if delivery_status_callback else None
|
|
|
|
|
|
),
|
2017-12-08 10:52:38 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_delivery_status_callback_details():
|
2018-07-20 08:42:01 +01:00
|
|
|
|
if current_service.service_callback_api:
|
2017-12-08 10:52:38 +00:00
|
|
|
|
return service_api_client.get_service_callback_api(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
current_service.id, current_service.service_callback_api[0]
|
2017-12-08 10:52:38 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
@main.route(
|
|
|
|
|
|
"/services/<uuid:service_id>/api/callbacks/delivery-status-callback",
|
|
|
|
|
|
methods=["GET", "POST"],
|
|
|
|
|
|
)
|
|
|
|
|
|
@user_has_permissions("manage_api_keys")
|
2017-12-08 10:52:38 +00:00
|
|
|
|
def delivery_status_callback(service_id):
|
|
|
|
|
|
delivery_status_callback = get_delivery_status_callback_details()
|
|
|
|
|
|
back_link = (
|
2023-08-25 09:12:23 -07:00
|
|
|
|
".api_callbacks"
|
|
|
|
|
|
if current_service.has_permission("inbound_sms")
|
|
|
|
|
|
else ".api_integration"
|
2017-12-08 10:52:38 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
2020-08-03 15:10:02 +01:00
|
|
|
|
form = CallbackForm(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
url=delivery_status_callback.get("url") if delivery_status_callback else "",
|
|
|
|
|
|
bearer_token=dummy_bearer_token if delivery_status_callback else "",
|
2017-12-04 15:07:11 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
if form.validate_on_submit():
|
2018-04-26 17:23:44 +01:00
|
|
|
|
if delivery_status_callback and form.url.data:
|
|
|
|
|
|
if (
|
2023-08-25 09:12:23 -07:00
|
|
|
|
delivery_status_callback.get("url") != form.url.data
|
|
|
|
|
|
or form.bearer_token.data != dummy_bearer_token
|
2018-04-26 17:23:44 +01:00
|
|
|
|
):
|
2017-12-04 15:07:11 +00:00
|
|
|
|
service_api_client.update_service_callback_api(
|
|
|
|
|
|
service_id,
|
2017-12-08 10:52:38 +00:00
|
|
|
|
url=form.url.data,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
bearer_token=check_token_against_dummy_bearer(
|
|
|
|
|
|
form.bearer_token.data
|
|
|
|
|
|
),
|
2017-12-04 15:07:11 +00:00
|
|
|
|
user_id=current_user.id,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
callback_api_id=delivery_status_callback.get("id"),
|
2017-12-04 15:07:11 +00:00
|
|
|
|
)
|
2018-04-26 17:23:44 +01:00
|
|
|
|
elif delivery_status_callback and not form.url.data:
|
|
|
|
|
|
service_api_client.delete_service_callback_api(
|
|
|
|
|
|
service_id,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
delivery_status_callback["id"],
|
2018-04-26 17:23:44 +01:00
|
|
|
|
)
|
|
|
|
|
|
elif form.url.data:
|
2017-12-04 15:07:11 +00:00
|
|
|
|
service_api_client.create_service_callback_api(
|
|
|
|
|
|
service_id,
|
2017-12-08 10:52:38 +00:00
|
|
|
|
url=form.url.data,
|
|
|
|
|
|
bearer_token=form.bearer_token.data,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
user_id=current_user.id,
|
2017-12-04 15:07:11 +00:00
|
|
|
|
)
|
2018-06-18 12:52:12 +01:00
|
|
|
|
else:
|
|
|
|
|
|
# If no callback is set up and the user chooses to continue
|
|
|
|
|
|
# having no callback (ie both fields empty) then there’s
|
|
|
|
|
|
# nothing for us to do here
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
2017-12-08 10:52:38 +00:00
|
|
|
|
return redirect(url_for(back_link, service_id=service_id))
|
|
|
|
|
|
|
|
|
|
|
|
return render_template(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"views/api/callbacks/delivery-status-callback.html",
|
2017-12-08 10:52:38 +00:00
|
|
|
|
back_link=back_link,
|
|
|
|
|
|
form=form,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2017-12-04 15:07:11 +00:00
|
|
|
|
|
2017-12-08 10:52:38 +00:00
|
|
|
|
def get_received_text_messages_callback():
|
2018-07-20 08:42:01 +01:00
|
|
|
|
if current_service.inbound_api:
|
2017-12-08 10:52:38 +00:00
|
|
|
|
return service_api_client.get_service_inbound_api(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
current_service.id, current_service.inbound_api[0]
|
2017-12-08 10:52:38 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-08-25 09:12:23 -07:00
|
|
|
|
@main.route(
|
|
|
|
|
|
"/services/<uuid:service_id>/api/callbacks/received-text-messages-callback",
|
|
|
|
|
|
methods=["GET", "POST"],
|
|
|
|
|
|
)
|
|
|
|
|
|
@user_has_permissions("manage_api_keys")
|
2017-12-08 10:52:38 +00:00
|
|
|
|
def received_text_messages_callback(service_id):
|
2023-08-25 09:12:23 -07:00
|
|
|
|
if not current_service.has_permission("inbound_sms"):
|
|
|
|
|
|
return redirect(url_for(".api_integration", service_id=service_id))
|
2017-12-04 15:07:11 +00:00
|
|
|
|
|
2017-12-08 10:52:38 +00:00
|
|
|
|
received_text_messages_callback = get_received_text_messages_callback()
|
2020-08-03 15:10:02 +01:00
|
|
|
|
form = CallbackForm(
|
2024-02-29 08:24:53 -08:00
|
|
|
|
url=(
|
|
|
|
|
|
received_text_messages_callback.get("url")
|
|
|
|
|
|
if received_text_messages_callback
|
|
|
|
|
|
else ""
|
|
|
|
|
|
),
|
2023-08-25 09:12:23 -07:00
|
|
|
|
bearer_token=dummy_bearer_token if received_text_messages_callback else "",
|
2017-12-08 10:52:38 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
if form.validate_on_submit():
|
2018-04-26 17:23:44 +01:00
|
|
|
|
if received_text_messages_callback and form.url.data:
|
|
|
|
|
|
if (
|
2023-08-25 09:12:23 -07:00
|
|
|
|
received_text_messages_callback.get("url") != form.url.data
|
|
|
|
|
|
or form.bearer_token.data != dummy_bearer_token
|
2018-04-26 17:23:44 +01:00
|
|
|
|
):
|
2017-12-08 10:52:38 +00:00
|
|
|
|
service_api_client.update_service_inbound_api(
|
|
|
|
|
|
service_id,
|
|
|
|
|
|
url=form.url.data,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
bearer_token=check_token_against_dummy_bearer(
|
|
|
|
|
|
form.bearer_token.data
|
|
|
|
|
|
),
|
2017-12-08 10:52:38 +00:00
|
|
|
|
user_id=current_user.id,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
inbound_api_id=received_text_messages_callback.get("id"),
|
2017-12-08 10:52:38 +00:00
|
|
|
|
)
|
2018-04-26 17:23:44 +01:00
|
|
|
|
elif received_text_messages_callback and not form.url.data:
|
|
|
|
|
|
service_api_client.delete_service_inbound_api(
|
|
|
|
|
|
service_id,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
received_text_messages_callback["id"],
|
2018-04-26 17:23:44 +01:00
|
|
|
|
)
|
|
|
|
|
|
elif form.url.data:
|
2017-12-08 10:52:38 +00:00
|
|
|
|
service_api_client.create_service_inbound_api(
|
|
|
|
|
|
service_id,
|
|
|
|
|
|
url=form.url.data,
|
|
|
|
|
|
bearer_token=form.bearer_token.data,
|
2023-08-25 09:12:23 -07:00
|
|
|
|
user_id=current_user.id,
|
2017-12-08 10:52:38 +00:00
|
|
|
|
)
|
2023-08-25 09:12:23 -07:00
|
|
|
|
return redirect(url_for(".api_callbacks", service_id=service_id))
|
2017-12-04 15:07:11 +00:00
|
|
|
|
return render_template(
|
2023-08-25 09:12:23 -07:00
|
|
|
|
"views/api/callbacks/received-text-messages-callback.html",
|
2017-12-04 15:07:11 +00:00
|
|
|
|
form=form,
|
|
|
|
|
|
)
|