From d66be8685ae3f46483ccf6bf7c1ed1d9aba60cf6 Mon Sep 17 00:00:00 2001 From: Dan Date: Fri, 2 Oct 2020 15:16:28 -0400 Subject: [PATCH] restrict access to a small set of netbadge users. fixing saliva mis-spelling Drop "Be Safe" use "Prevalance Testing" --- communicator/__init__.py | 25 +++++++++++++++++-- communicator/services/user_service.py | 26 ++++++++++++++------ communicator/templates/invitation_email.html | 2 +- communicator/templates/result_email.txt | 6 ++--- config/default.py | 1 + 5 files changed, 46 insertions(+), 14 deletions(-) diff --git a/communicator/__init__.py b/communicator/__init__.py index 1d0f0cb..3cd6e2c 100644 --- a/communicator/__init__.py +++ b/communicator/__init__.py @@ -1,9 +1,10 @@ import logging import os +from functools import wraps import connexion import sentry_sdk -from flask import render_template, request, redirect, url_for +from flask import render_template, request, redirect, url_for, flash, abort from flask_assets import Environment from flask_cors import CORS from flask_mail import Mail @@ -14,6 +15,7 @@ from flask_sqlalchemy import SQLAlchemy from sentry_sdk.integrations.flask import FlaskIntegration from webassets import Bundle + logging.basicConfig(level=logging.INFO) # API, fully defined in api.yml @@ -73,15 +75,27 @@ cors = CORS(connexion_app.app, origins=origins_re) if app.config['SENTRY_ENVIRONMENT']: sentry_sdk.init( environment=app.config['SENTRY_ENVIRONMENT'], - dsn="https://25342ca4e2d443c6a5c49707d68e9f40@o401361.ingest.sentry.io/5260915", + dsn="https://c37225ab38de49749acfbb9c7381f065@o401361.ingest.sentry.io/5449288", integrations=[FlaskIntegration()] ) ### HTML Pages BASE_HREF = app.config['APPLICATION_ROOT'].strip('/') +def superuser(f): + @wraps(f) + def decorated_function(*args, **kwargs): + from communicator.services.user_service import UserService + if not UserService().is_valid_user(): + flash("You do not have permission to view that page", "warning") + logging.info("Permission Denied to user " + UserService.get_user_info()) + abort(404) + return f(*args, **kwargs) + return decorated_function + @app.route('/', methods=['GET']) +@superuser def index(): from communicator.models import Sample from communicator.tables import SampleTable @@ -99,6 +113,7 @@ def index(): ) @app.route('/invitation', methods=['GET', 'POST']) +@superuser def send_invitation(): from communicator.models.invitation import Invitation from communicator.tables import InvitationTable @@ -130,6 +145,7 @@ def send_invitation(): ) @app.route('/imported_files', methods=['GET']) +@superuser def list_imported_files_from_ivy(): from communicator.models.ivy_file import IvyFile from communicator.tables import IvyFileTable @@ -155,6 +171,10 @@ def sso(): response += f"

Current User: {user.display_name} ({user.uid})

" return response +@app.route('/debug-sentry') +def trigger_error(): + division_by_zero = 1 / 0 + # Access tokens @app.cli.command() def globus_token(): @@ -182,3 +202,4 @@ def delete(): from communicator.services.ivy_service import IvyService ivy_service = IvyService() ivy_service.delete_file() + diff --git a/communicator/services/user_service.py b/communicator/services/user_service.py index 3bdff63..35930d6 100644 --- a/communicator/services/user_service.py +++ b/communicator/services/user_service.py @@ -1,5 +1,8 @@ +import re + from flask import request +from communicator import app from communicator.errors import CommError from communicator.models.user import User @@ -25,10 +28,19 @@ class UserService(object): # Connection: Keep-Alive def get_user_info(self): - uid = request.headers.get("Uid") - cn = request.headers.get("Cn") - if not uid: - uid = request.headers.get("X-Remote-Uid") - if not uid: - raise CommError(1100, "invalid_sso_credentials", r"'Uid' nor 'X-Remote-Uid' were present in the headers: %s"% str(request.headers)) - return User(uid, cn) \ No newline at end of file + if app.config['PRODUCTION']: + uid = request.headers.get("Uid") + cn = request.headers.get("Cn") + if not uid: + uid = request.headers.get("X-Remote-Uid") + if not uid: + raise CommError(1100, "invalid_sso_credentials", r"'Uid' nor 'X-Remote-Uid' were present in the headers: %s"% str(request.headers)) + return User(uid, cn) + else: + return User('testUser', "Test User") + + def is_valid_user(self): + user = self.get_user_info() + valid_ids = [x for x in re.compile('\s*[,|\s+]\s*').split(app.config['ADMINS'])] + return user.uid in valid_ids + diff --git a/communicator/templates/invitation_email.html b/communicator/templates/invitation_email.html index 4029380..2e3e946 100644 --- a/communicator/templates/invitation_email.html +++ b/communicator/templates/invitation_email.html @@ -13,7 +13,7 @@ spread of the virus.

- You have been selected for a salvia screening on {{date}}. + You have been selected for a saliva screening on {{date}}.

diff --git a/communicator/templates/result_email.txt b/communicator/templates/result_email.txt index aa3b408..2e1f5aa 100644 --- a/communicator/templates/result_email.txt +++ b/communicator/templates/result_email.txt @@ -1,6 +1,4 @@ -You have a new notification from the University of Virginia BE SAFE System. +You have a new notification from UVA Prevalence Testing -Please follow this link, and log in with Netbadge to securely view your notification: +Please follow this link, and log in with Netbadge, to securely view your notification: {{link}} - -Please note that email is not a secure form of communication and should not be used to discuss any confidential matters, including personal health information, given its confidentiality cannot be assured. diff --git a/config/default.py b/config/default.py index 29925e9..b5e8a1e 100644 --- a/config/default.py +++ b/config/default.py @@ -11,6 +11,7 @@ FLASK_PORT = environ.get('PORT0') or environ.get('FLASK_PORT', default="5000") CORS_ALLOW_ORIGINS = re.split(r',\s*', environ.get('CORS_ALLOW_ORIGINS', default="localhost:4200, localhost:5002")) TESTING = environ.get('TESTING', default="false") == "true" PRODUCTION = (environ.get('PRODUCTION', default="false") == "true") +ADMINS = environ.get('ADMINS', default="testUser") # Sentry flag ENABLE_SENTRY = environ.get('ENABLE_SENTRY', default="false") == "true" # To be removed soon