mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-08-22 07:16:25 -04:00
Compare commits
8 Commits
save_csv
...
11-14-2024
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad50568c10 | ||
|
|
9796d424b5 | ||
|
|
d678659cbe | ||
|
|
49932f6942 | ||
|
|
0091fd0b31 | ||
|
|
683bf893c1 | ||
|
|
a95138f344 | ||
|
|
cc5c4114e0 |
@@ -35,6 +35,8 @@ def index():
|
||||
if current_user and current_user.is_authenticated:
|
||||
return redirect(url_for("main.choose_account"))
|
||||
|
||||
ttl = 24 * 60 * 60
|
||||
|
||||
# make and store the state
|
||||
state = generate_token(
|
||||
str(request.remote_addr),
|
||||
@@ -42,12 +44,12 @@ def index():
|
||||
current_app.config["DANGEROUS_SALT"],
|
||||
)
|
||||
state_key = f"login-state-{unquote(state)}"
|
||||
redis_client.set(state_key, state)
|
||||
redis_client.set(state_key, state, ex=ttl)
|
||||
|
||||
# make and store the nonce
|
||||
nonce = secrets.token_urlsafe()
|
||||
nonce_key = f"login-nonce-{unquote(nonce)}"
|
||||
redis_client.set(nonce_key, nonce)
|
||||
redis_client.set(nonce_key, nonce, ex=ttl)
|
||||
|
||||
url = os.getenv("LOGIN_DOT_GOV_INITIAL_SIGNIN_URL")
|
||||
if url is not None:
|
||||
|
||||
@@ -10,12 +10,10 @@ from flask import (
|
||||
stream_with_context,
|
||||
url_for,
|
||||
)
|
||||
from flask_login import current_user
|
||||
|
||||
from app import current_service, job_api_client, notification_api_client
|
||||
from app.main import main
|
||||
from app.notify_client.api_key_api_client import KEY_TYPE_TEST
|
||||
from app.s3_client.s3_csv_client import delete_report, report_upload
|
||||
from app.utils import (
|
||||
DELIVERED_STATUSES,
|
||||
FAILURE_STATUSES,
|
||||
@@ -146,36 +144,6 @@ def download_notifications_csv(service_id):
|
||||
file_time = datetime.now().strftime("%Y-%m-%d %I:%M:%S %p")
|
||||
file_time = f"{file_time} {get_user_preferred_timezone()}"
|
||||
|
||||
csv = generate_notifications_csv(
|
||||
service_id=service_id,
|
||||
job_id=None,
|
||||
status=filter_args.get("status"),
|
||||
page=request.args.get("page", 1),
|
||||
page_size=10000,
|
||||
format_for_csv=True,
|
||||
template_type=filter_args.get("message_type"),
|
||||
limit_days=service_data_retention_days,
|
||||
)
|
||||
|
||||
# START asynchronous reporting block
|
||||
csv_file = "".join(csv)
|
||||
file_location = f"reports/{service_id}/{current_user.id}/{service_data_retention_days}/report.csv"
|
||||
|
||||
# TODO these are some capabilities we will probably need when
|
||||
# report generation becomes asynchronous.
|
||||
|
||||
# old_content = report_download(file_location)
|
||||
# current_app.logger.info(f"OLD CONTENT IS {old_content}")
|
||||
# reports = get_downloadable_reports(current_user.id, service_id)
|
||||
|
||||
# TODO these are to support asynchronous report generation.
|
||||
# Leaving them commented in so they get exercised.
|
||||
delete_report(file_location)
|
||||
report_upload(file_location, csv_file)
|
||||
# END asynchronous reporting block
|
||||
|
||||
# TODO eventually we want to remove this, when reports become fully asynchronous
|
||||
# The UI should be retrieving the report elsewhere via the download_report() method call
|
||||
return Response(
|
||||
stream_with_context(
|
||||
generate_notifications_csv(
|
||||
|
||||
@@ -25,7 +25,8 @@ from app.main.views.verify import activate_user
|
||||
from app.models.user import User
|
||||
from app.utils import hide_from_search_engines
|
||||
from app.utils.login import get_id_token, is_safe_redirect_url
|
||||
from app.utils.time import is_less_than_days_ago
|
||||
|
||||
# from app.utils.time import is_less_than_days_ago
|
||||
from app.utils.user import is_gov_user
|
||||
from notifications_utils.url_safe_token import generate_token
|
||||
|
||||
@@ -108,11 +109,15 @@ def _do_login_dot_gov(): # $ pragma: no cover
|
||||
)
|
||||
raise Exception(f"Could not login with login.gov {login_gov_error}")
|
||||
elif code and state:
|
||||
state_key = f"login-state-{unquote(state)}"
|
||||
stored_state = unquote(redis_client.get(state_key).decode("utf8"))
|
||||
if state != stored_state:
|
||||
current_app.logger.error(f"State Error: {state} != {stored_state}")
|
||||
abort(403)
|
||||
verify_key = f"login-verify_email-{unquote(state)}"
|
||||
verify_path = bool(redis_client.get(verify_key))
|
||||
|
||||
if not verify_path:
|
||||
state_key = f"login-state-{unquote(state)}"
|
||||
stored_state = unquote(redis_client.get(state_key).decode("utf8"))
|
||||
if state != stored_state:
|
||||
current_app.logger.error(f"State Error: {state} != {stored_state}")
|
||||
abort(403)
|
||||
|
||||
# activate the user
|
||||
try:
|
||||
@@ -130,12 +135,17 @@ def _do_login_dot_gov(): # $ pragma: no cover
|
||||
f"Retrieved user {user['id']} from db #notify-admin-1505"
|
||||
)
|
||||
|
||||
# Check if the email needs to be revalidated
|
||||
is_fresh_email = is_less_than_days_ago(
|
||||
user["email_access_validated_at"], 90
|
||||
)
|
||||
if not is_fresh_email:
|
||||
return verify_email(user, redirect_url)
|
||||
# Temporary disabling of this until we figure out what is happening.
|
||||
# # Check if the email needs to be revalidated
|
||||
# is_fresh_email = is_less_than_days_ago(
|
||||
# user["email_access_validated_at"], 90
|
||||
# )
|
||||
# if not is_fresh_email:
|
||||
# # send email verify
|
||||
# ttl = 24 * 60 * 60
|
||||
# verify_key = f"login-verify_email-{unquote(state)}"
|
||||
# redis_client.set(verify_key, state, ex=ttl)
|
||||
# return verify_email(user, redirect_url)
|
||||
|
||||
usr = User.from_email_address(user["email_address"])
|
||||
current_app.logger.info(f"activating user {usr.id} #notify-admin-1505")
|
||||
@@ -209,17 +219,19 @@ def sign_in(): # pragma: no cover
|
||||
return redirect(redirect_url)
|
||||
return redirect(url_for("main.show_accounts_or_dashboard"))
|
||||
|
||||
ttl = 24 * 60 * 60
|
||||
|
||||
state = generate_token(
|
||||
str(request.remote_addr),
|
||||
current_app.config["SECRET_KEY"],
|
||||
current_app.config["DANGEROUS_SALT"],
|
||||
)
|
||||
state_key = f"login-state-{unquote(state)}"
|
||||
redis_client.set(state_key, state)
|
||||
redis_client.set(state_key, state, ex=ttl)
|
||||
|
||||
nonce = secrets.token_urlsafe()
|
||||
nonce_key = f"login-nonce-{unquote(nonce)}"
|
||||
redis_client.set(nonce_key, nonce)
|
||||
redis_client.set(nonce_key, nonce, ex=ttl)
|
||||
|
||||
url = os.getenv("LOGIN_DOT_GOV_INITIAL_SIGNIN_URL")
|
||||
# handle unit tests
|
||||
|
||||
@@ -41,6 +41,8 @@ class InviteApiClient(NotifyAdminAPIClient):
|
||||
}
|
||||
data = _attach_current_user(data)
|
||||
|
||||
ttl = 24 * 60 * 60
|
||||
|
||||
# make and store the state
|
||||
state = generate_token(
|
||||
str(request.remote_addr),
|
||||
@@ -48,12 +50,12 @@ class InviteApiClient(NotifyAdminAPIClient):
|
||||
current_app.config["DANGEROUS_SALT"],
|
||||
)
|
||||
state_key = f"login-state-{unquote(state)}"
|
||||
redis_client.set(state_key, state)
|
||||
redis_client.set(state_key, state, ex=ttl)
|
||||
|
||||
# make and store the nonce
|
||||
nonce = secrets.token_urlsafe()
|
||||
nonce_key = f"login-nonce-{unquote(nonce)}"
|
||||
redis_client.set(nonce_key, nonce) # save the nonce to redis.
|
||||
redis_client.set(nonce_key, nonce, ex=ttl) # save the nonce to redis.
|
||||
|
||||
data["nonce"] = nonce # This is passed to api for the invite url.
|
||||
data["state"] = state # This is passed to api for the invite url.
|
||||
@@ -64,7 +66,7 @@ class InviteApiClient(NotifyAdminAPIClient):
|
||||
invite_data_key = f"invitedata-{unquote(state)}"
|
||||
redis_invite_data = resp["invite"]
|
||||
redis_invite_data = json.dumps(redis_invite_data)
|
||||
redis_client.set(invite_data_key, redis_invite_data)
|
||||
redis_client.set(invite_data_key, redis_invite_data, ex=ttl)
|
||||
|
||||
return resp_data
|
||||
|
||||
@@ -97,6 +99,8 @@ class InviteApiClient(NotifyAdminAPIClient):
|
||||
self.post(url=f"/service/{service_id}/invite/{invited_user_id}", data=data)
|
||||
|
||||
def resend_invite(self, service_id, invited_user_id):
|
||||
ttl = 24 * 60 * 60
|
||||
|
||||
# make and store the state
|
||||
state = generate_token(
|
||||
str(request.remote_addr),
|
||||
@@ -104,12 +108,12 @@ class InviteApiClient(NotifyAdminAPIClient):
|
||||
current_app.config["DANGEROUS_SALT"],
|
||||
)
|
||||
state_key = f"login-state-{unquote(state)}"
|
||||
redis_client.set(state_key, state)
|
||||
redis_client.set(state_key, state, ex=ttl)
|
||||
|
||||
# make and store the nonce
|
||||
nonce = secrets.token_urlsafe()
|
||||
nonce_key = f"login-nonce-{unquote(nonce)}"
|
||||
redis_client.set(nonce_key, nonce)
|
||||
redis_client.set(nonce_key, nonce, ex=ttl)
|
||||
|
||||
data = {
|
||||
"nonce": nonce,
|
||||
@@ -122,7 +126,7 @@ class InviteApiClient(NotifyAdminAPIClient):
|
||||
invite_data_key = f"invitedata-{unquote(state)}"
|
||||
redis_invite_data = resp["invite"]
|
||||
redis_invite_data = json.dumps(redis_invite_data)
|
||||
redis_client.set(invite_data_key, redis_invite_data)
|
||||
redis_client.set(invite_data_key, redis_invite_data, ex=ttl)
|
||||
|
||||
@cache.delete("service-{service_id}")
|
||||
@cache.delete("user-{invited_user_id}")
|
||||
|
||||
@@ -116,7 +116,7 @@ class UserApiClient(NotifyAdminAPIClient):
|
||||
data["next"] = next_string
|
||||
if code_type == "email":
|
||||
data["email_auth_link_host"] = self.admin_url
|
||||
endpoint = "/user/{0}/{1}-code".format(user_id, code_type)
|
||||
endpoint = f"/user/{user_id}/{code_type}-code"
|
||||
current_app.logger.warn(hilite(f"Sending verify_code {code_type} to {user_id}"))
|
||||
self.post(endpoint, data=data)
|
||||
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import os
|
||||
import uuid
|
||||
|
||||
import boto3
|
||||
from flask import current_app
|
||||
|
||||
from app.s3_client import (
|
||||
AWS_CLIENT_CONFIG,
|
||||
get_s3_contents,
|
||||
get_s3_metadata,
|
||||
get_s3_object,
|
||||
@@ -65,70 +63,3 @@ def set_metadata_on_csv_upload(service_id, upload_id, **kwargs):
|
||||
|
||||
def get_csv_metadata(service_id, upload_id):
|
||||
return get_s3_metadata(get_csv_upload(service_id, upload_id))
|
||||
|
||||
|
||||
def report_upload(file_location, report_content):
|
||||
bucket_name = current_app.config["CSV_UPLOAD_BUCKET"]["bucket"]
|
||||
access_key = current_app.config["CSV_UPLOAD_BUCKET"]["access_key_id"]
|
||||
secret_key = current_app.config["CSV_UPLOAD_BUCKET"]["secret_access_key"]
|
||||
region = current_app.config["CSV_UPLOAD_BUCKET"]["region"]
|
||||
|
||||
utils_s3upload(
|
||||
filedata=report_content,
|
||||
region=region,
|
||||
bucket_name=bucket_name,
|
||||
file_location=file_location,
|
||||
access_key=access_key,
|
||||
secret_key=secret_key,
|
||||
)
|
||||
current_app.logger.info(f"Succcessfully uploaded report to {file_location}")
|
||||
|
||||
|
||||
def report_download(file_location):
|
||||
current_app.logger.info(f"Downloading report from {file_location}")
|
||||
bucket_name = current_app.config["CSV_UPLOAD_BUCKET"]["bucket"]
|
||||
access_key = current_app.config["CSV_UPLOAD_BUCKET"]["access_key_id"]
|
||||
secret_key = current_app.config["CSV_UPLOAD_BUCKET"]["secret_access_key"]
|
||||
region = current_app.config["CSV_UPLOAD_BUCKET"]["region"]
|
||||
|
||||
return get_s3_contents(
|
||||
get_s3_object(bucket_name, file_location, access_key, secret_key, region)
|
||||
)
|
||||
|
||||
|
||||
def delete_report(file_location):
|
||||
current_app.logger.info(f"Deleting report from {file_location}")
|
||||
bucket_name = current_app.config["CSV_UPLOAD_BUCKET"]["bucket"]
|
||||
access_key = current_app.config["CSV_UPLOAD_BUCKET"]["access_key_id"]
|
||||
secret_key = current_app.config["CSV_UPLOAD_BUCKET"]["secret_access_key"]
|
||||
region = current_app.config["CSV_UPLOAD_BUCKET"]["region"]
|
||||
|
||||
obj = get_s3_object(bucket_name, file_location, access_key, secret_key, region)
|
||||
if obj is None:
|
||||
return None
|
||||
return obj.delete()
|
||||
|
||||
|
||||
def get_downloadable_reports(user_id, service_id):
|
||||
prefix = f"reports/{service_id}/{user_id}/"
|
||||
bucket_name = current_app.config["CSV_UPLOAD_BUCKET"]["bucket"]
|
||||
access_key = current_app.config["CSV_UPLOAD_BUCKET"]["access_key_id"]
|
||||
secret_key = current_app.config["CSV_UPLOAD_BUCKET"]["secret_access_key"]
|
||||
region = current_app.config["CSV_UPLOAD_BUCKET"]["region"]
|
||||
|
||||
session = boto3.Session(
|
||||
aws_access_key_id=access_key,
|
||||
aws_secret_access_key=secret_key,
|
||||
region_name=region,
|
||||
)
|
||||
s3 = session.client(
|
||||
"s3",
|
||||
config=AWS_CLIENT_CONFIG,
|
||||
)
|
||||
|
||||
response = s3.list_objects_v2(Bucket=bucket_name, Prefix=prefix)
|
||||
object_keys = []
|
||||
if "Contents" in response:
|
||||
for obj in response["Contents"]:
|
||||
object_keys.append(obj["Key"])
|
||||
return object_keys
|
||||
|
||||
Reference in New Issue
Block a user