def get(self, request, *args, **kwargs): # Get the study study = kwargs['study'] try: # Get the patient email and ensure they exist patient_email = get_jwt_email(request=request, verify=False) # Check FHIR if not PPMFHIR.get_consent_document_reference( patient=patient_email, study=study, flatten_return=True): # Save it APIConsentView.create_consent_document_reference( request=request, study=study) # Get their ID ppm_id = PPMFHIR.query_patient_id(email=patient_email) return HttpResponseRedirect( redirect_to=P2MD.get_consent_url(study=study, ppm_id=ppm_id)) except Exception as e: logger.error("Error while rendering consent: {}".format(e), exc_info=True, extra={ 'request': request, 'project': study, }) raise SystemError('Could not render consent document')
def update_dbmi_user(request, **profile): logger.debug("Update DBMI user") # Get the JWT email = authn.get_jwt_email(request, verify=False) # Get their profile first reg_profile = get_dbmi_user(request=request, email=email) if not reg_profile: # Ensure email is in their profile if "email" not in profile: profile["email"] = email # Create the profile return create_dbmi_user(request, **profile) else: # Build the URL (needs trailing slash) url = furl(dbmi_settings.REG_URL) url.path.segments.extend(["api", "register", reg_profile["id"], ""]) response = requests.put(url.url, headers=authn.dbmi_http_headers(request), data=json.dumps(profile)) if not response.ok: logger.error("Update user response: {}".format(response.content)) return response.json()
def check_email_confirmation(request): logger.debug("Checking email confirmation") # Build the URL (needs trailing slash) url = furl(dbmi_settings.REG_URL) url.path.segments.extend(["api", "register", ""]) url.query.params.add("email", authn.get_jwt_email(request, verify=False)) # Make the call response = requests.get(url.url, headers=authn.dbmi_http_headers(request)) if not response.ok: logger.error("Confirmation email response: {}".format( response.content)) return None try: # Parse the profile for the status email_status = response.json()["results"][0]["email_confirmed"] logger.debug("Email confirmation status: {}".format(email_status)) return email_status except (KeyError, IndexError) as e: logger.error("Failed parsing profile: {}".format(e)) return None
def wrap(request, *args, **kwargs): # Check for current user if not request.user or not request.user.is_authenticated: return unauthorized_response(request) # Get the payload payload = authn.get_jwt_payload(request, verify=False) # Check claims in the JWT first, as it is least costly. if authz.jwt_has_authz(payload, authz.JWT_AUTHZ_GROUPS, dbmi_settings.AUTHZ_ADMIN_GROUP): return view(request, *args, **kwargs) # Get their email address email = authn.get_jwt_email(request, verify=False) # Now consult the AuthZ server if authz.has_permission(request, email, dbmi_settings.CLIENT, dbmi_settings.AUTHZ_ADMIN_PERMISSION): return view(request, *args, **kwargs) # Possibly store these elsewhere for records # TODO: Figure out a better way to flag failed access attempts logger.warning("{} Failed {} permission on {}".format( email, dbmi_settings.AUTHZ_ADMIN_PERMISSION, dbmi_settings.CLIENT)) raise PermissionDenied
def create_consent_document_reference(request, study, ppm_id=None): """ Accepts the context of a participant's request and renders their signed and accepted consent as a PDF and then sends the PDF details to P2MD to create a file location, and finally uploads the contents of the file to the datalake for storage. :param request: The current request :type request: HttpRequest :param study: The study for which the consent was signed :type study: str :param ppm_id: The participant ID for which the consent render is being generated :type ppm_id: str """ # Get the participant ID if needed if not ppm_id: ppm_id = FHIR.query_patient_id(get_jwt_email(request=request, verify=False)) # Pull their record bundle = FHIR.query_participant(patient=ppm_id, flatten_return=True) # Get study title study_title = PPM.Study.title(study) # Check for study-specific PDF try: template_name = f"consent/pdf/{PPM.Study.get(study).value}.html" loader.get_template(template_name) except TemplateDoesNotExist: template_name = 'consent/pdf/consent.html' # Submit consent PDF logger.debug(f"PPM/{study}: Rendering consent with template: {template_name}") response = render_pdf(f'People-Powered Medicine {study_title} Consent', request, template_name, context=bundle.get('composition'), options={}) # Hash the content hash = hashlib.md5(response.content).hexdigest() size = len(response.content) # Create the file through P2MD uuid, upload_data = P2MD.create_consent_file(request, study, ppm_id, hash, size) # Pull the needed bits to upload the PDF location = upload_data['locationid'] post = upload_data['post'] # Set the files dictionary files = {'file': response.content} # Now that we have the file locally, send it to S3 response = requests.post(post['url'], data=post['fields'], files=files) response.raise_for_status() # Set request data P2MD.uploaded_consent(request, study, ppm_id, uuid, location) return True
def get_user_info(self, request): # Get info from token header return { "ip_address": get_client_ip(request.META), "username": get_jwt_username(request, verify=False), "email": get_jwt_email(request, verify=False), "client": get_jwt_value(request, "aud", verify=False), "tenant": get_jwt_value(request, "iss", verify=False), }
def get_jwt_user(request): # Check for a valid token token = authn.get_jwt(request) if not token or not authn.validate_rs256_jwt(token): return AnonymousUser() # Get their username username = authn.get_jwt_username(request, verify=False) # Use the usual routine to get the currently cached user user = django_auth.get_user(request) if user.is_authenticated: logger.debug("Found existing User session: {}".format(username)) # A cached user is present. We need to double-check JWT user to ensure # they are the same as the cached user. username = authn.get_jwt_username(request, verify=False) email = authn.get_jwt_email(request, verify=False) if username and email: if not user.username.lower() == username.lower( ) or not user.email.lower() == email.lower(): logger.debug( "User session does not match JWT, logging out") # TODO: Figure out if its necessary to person any session invalidation here return AnonymousUser() else: logger.debug( "No existing User, attempting to login: {}".format(username)) # No user is logged in but we have a JWT token. Attempt to authenticate # the current JWT and if it succeeds, login and cache the user. user = django_auth.authenticate(request, token=token) if user and user.is_authenticated: logger.debug("User has authenticated: {}".format(username)) # Store this user in session django_auth.login(request, user) else: logger.debug( "User could not be authenticated: {}".format(username)) # Whatever token this user has, it's not valid OR their account would/could not # be created, deny permission. This will likely be the case for instances where # automatic user creation is disabled and a user with a valid JWT is not being # granted an account. raise PermissionDenied return user
def get(self, request, *args, **kwargs): logger.debug("Generating contact form") # Set initial values. initial = { 'email': get_jwt_email(request=request, verify=False) } # Generate and render the form. form = ContactForm(initial=initial) if request.is_ajax(): return render(request, 'contact/modal.html', {'contact_form': form}) else: return render(request, 'contact/contact.html', {'contact_form': form})
def create_dbmi_user(request, **profile): logger.debug("Creating DBMI user") # Get the JWT email = authn.get_jwt_email(request, verify=False) # Update kwargs profile["email"] = email # Build the URL (needs trailing slash) url = furl(dbmi_settings.REG_URL) url.path.segments.extend(["api", "register", ""]) response = requests.post(url.url, headers=authn.dbmi_http_headers(request), data=json.dumps(profile)) if not response.ok: logger.error("Create user response: {}".format(response.content)) return response.json()
def wrap(request, *args, **kwargs): # Check for current user if not request.user or not request.user.is_authenticated: return unauthorized_response(request) # Get their email address email = authn.get_jwt_email(request, verify=False) # Check permission if authz.has_permission(request, email, item, permission): return view(request, *args, **kwargs) # Possibly store these elsewhere for records # TODO: Figure out a better way to flag failed access attempts logger.warning("{} Failed {} permission on {}".format( email, permission, dbmi_settings.CLIENT)) # Forbid if nothing else raise PermissionDenied
def get_dbmi_user(request, email=None): logger.debug("Get DBMI user") # Get the JWT if not email: email = authn.get_jwt_email(request, verify=False) # Build the URL (needs trailing slash) url = furl(dbmi_settings.REG_URL) url.path.segments.extend(["api", "register", ""]) # Add email url.query.params.add("email", email) # Requests for profiles are limited to the profile for the requesting user response = requests.get(url.url, headers=authn.dbmi_http_headers(request)) if not response.ok: logger.error("Get user response: {}".format(response.content)) # Return the profile profiles = response.json()["results"] return next(iter(profiles), None)
def get_jwt_user(request): # Use super's implementation user = DBMIAuthenticationMiddleware.get_jwt_user(request) if user: # Check if they've been granted admin level privileges if user.is_staff or user.is_superuser: # Get details token = authn.get_jwt(request) username = authn.get_jwt_username(request, verify=False) email = authn.get_jwt_email(request, verify=False) logger.debug( f'User "{username}":"{email}" is currently admin; rerunning sync...' ) # Run their sync again to make absolutely sure they're still an admin user = django_auth.authenticate(request, token=token) if user and user.is_authenticated: logger.debug( "User has re-authenticated: {}".format(username)) # Check updated status if user.is_superuser or user.is_staff: logger.debug( f'User "{username}":"{email}" is still admin') else: logger.debug( "User could not be authenticated: {}".format(username)) # Whatever token this user has, it's not valid OR their account would/could not # be created, deny permission. This will likely be the case for instances where # automatic user creation is disabled and a user with a valid JWT is not being # granted an account. raise PermissionDenied return user
def get(self, request, *args, **kwargs): logger.debug(f'PPM/{self.study}: GET questionnaire') # Get the patient email and ensure they exist patient_email = get_jwt_email(request=request, verify=False) try: # If demo mode, disable checks for participant and past submissions if not self.demo(request): # Check the current patient FHIR.check_patient(patient_email) # Check response FHIR.check_response(self.questionnaire_id, patient_email) # Create the form form = self.Form(self.questionnaire_id) # Prepare the context context = { 'study': self.study, 'questionnaire_id': self.questionnaire_id, 'form': form, 'return_url': self.return_url, } # Get the passed parameters return render(request, template_name='questionnaire/{}.html'.format( self.study), context=context) except FHIR.PatientDoesNotExist: logger.warning( 'Patient does not exist: {}'.format(patient_email[:3] + '****' + patient_email[-4:])) return render_error( request, title='Patient Does Not Exist', message= 'A FHIR resource does not yet exist for the current user. ' 'Please sign into the People-Powered dashboard to ' 'create your user.', support=False) except FHIR.QuestionnaireDoesNotExist: logger.warning('Questionnaire does not exist: {}'.format( self.questionnaire_id)) return render_error( request, title='Questionnaire Does Not Exist', message='The requested questionnaire does not exist!', support=False) except FHIR.QuestionnaireResponseAlreadyExists: logger.warning('Questionnaire already finished') return render_error( request, title='Questionnaire Already Completed', message='You have already filled out and submitted this ' 'questionnaire.', support=False) except Exception as e: logger.error("Error while rendering questionnaire: {}".format(e), exc_info=True, extra={ 'request': request, 'project': self.study, }) return render_error( request, title='Application Error', message='The application has experienced an unknown error {}'. format(': {}'.format(e) if settings.DEBUG else '.'), support=False)
def post(self, request, *args, **kwargs): logger.debug("Processing contact form POST") # Process the form. form = ContactForm(request.POST) if form.is_valid(): logger.debug("Form is valid") # Get the user's browser user_agent = request.META.get('HTTP_USER_AGENT', 'unknown') # Form the context. context = { 'from_email': form.cleaned_data['email'], 'from_name': form.cleaned_data['name'], 'message': form.cleaned_data['message'], 'user_agent': user_agent, } # List out the recipients. recipients = settings.CONTACT_FORM_RECIPIENTS.split(',') # Check for test accounts. test_admin = ContactView.check_test_account(email=get_jwt_email(request=request, verify=False)) if test_admin is not None: recipients = [test_admin] logger.debug("Found recipients {}".format(recipients)) # Send it out. msg_html = render_to_string('contact/email/contact.html', context) msg_plain = render_to_string('contact/email/contact.txt', context) try: msg = EmailMultiAlternatives('PPM Contact Form Inquiry', msg_plain, settings.DEFAULT_FROM_EMAIL, recipients) msg.attach_alternative(msg_html, "text/html") msg.send() logger.debug("Contact email sent!") # Check how the request was made. if request.is_ajax(): return HttpResponse('SUCCESS', status=200) else: return render(request, 'contact/success.html', context={'return_url': settings.RETURN_URL}) except Exception as e: logger.error('Contact form error: {}'.format(e), exc_info=True, extra={'request': request, 'context': context}) if request.is_ajax(): return HttpResponse('ERROR', status=500) else: messages.error(request, 'An unexpected error occurred, please try again') return ContactView.render_error(request) else: logger.error("Form is invalid: {}".format(request.POST)) # Check how the request was made. if request.is_ajax(): return HttpResponse('INVALID', status=500) else: messages.error(request, 'An unexpected error occurred, please try again') return HttpResponseRedirect(reverse('contact:contact'))
def post(self, request, *args, **kwargs): logger.debug('Signature view') # Get the patient's email patient_email = get_jwt_email(request=request, verify=False) # Process the form try: # Check form type if request.session['individual']: logger.debug('Individual signature') # Get the form form = ASDIndividualSignatureForm(request.POST) if not form.is_valid(): logger.debug('Individual signature invalid: {}'.format( form.errors.as_json())) # Return the form context = {'form': form, 'return_url': self.return_url} return render( request, template_name= 'consent/asd/individual-signature-part-1.html', context=context) # Build the data user_forms = dict({ 'individual': form.cleaned_data, 'quiz': request.session['quiz'] }) # Submit the data FHIR.submit_asd_individual(patient_email, user_forms, dry=self.demo(request)) # If in demo mode, do not create PDF if not self.demo(request): # Submit consent PDF in the background threading.Thread(target=APIConsentView. create_consent_document_reference, args=(request, PPM.Study.ASD.value)).start() # Get the return URL context = { 'return_url': self.return_url, 'demo': self.demo(request) } # Get the passed parameters return render(request, template_name='consent/success.html', context=context) else: logger.debug('Guardian/ward signature') # Check which signature if request.session.get('guardian'): logger.debug('Ward signature') # Get the form form = ASDWardSignatureForm(request.POST) if not form.is_valid(): logger.debug('Ward signature invalid: {}'.format( form.errors.as_json())) # Return the form context = {'form': form, 'return_url': self.return_url} return render( request, template_name= 'consent/asd/guardian-signature-part-3.html', context=context) # Build the data user_forms = dict({ 'ward': form.cleaned_data, 'guardian': request.session['guardian'], 'quiz': request.session['quiz'] }) # Submit the data FHIR.submit_asd_guardian(patient_email, user_forms, dry=self.demo(request)) # If in demo mode, do not create PDF if not self.demo(request): # Submit consent PDF in the background threading.Thread(target=APIConsentView. create_consent_document_reference, args=(request, PPM.Study.ASD.value)).start() # Get the return URL context = { 'return_url': self.return_url, 'demo': self.demo(request) } # Get the passed parameters return render(request, template_name='consent/success.html', context=context) else: logger.debug('Guardian signature') # Get the form form = ASDGuardianSignatureForm(request.POST) if not form.is_valid(): logger.debug('Guardian signature invalid: {}'.format( form.errors.as_json())) # Return the form context = {'form': form, 'return_url': self.return_url} return render( request, template_name= 'consent/asd/guardian-signature-part-1-2.html', context=context) # Fix the date date = form.cleaned_data['date'].isoformat() form.cleaned_data['date'] = date # Retain their responses request.session['guardian'] = form.cleaned_data # Make the ward signature form form = ASDWardSignatureForm() # Get the return URL context = { 'form': form, 'return_url': self.return_url, } # Get the passed parameters return render(request, template_name= 'consent/asd/guardian-signature-part-3.html', context=context) except FHIR.PatientDoesNotExist: logger.warning('Patient does not exist') return render_error( request, title='Patient Does Not Exist', message= 'A FHIR resource does not yet exist for the current user. ' 'Please sign into the People-Powered dashboard to ' 'create your user.', support=False) except FHIR.QuestionnaireDoesNotExist: logger.warning('Consent does not exist: ASD') return render_error( request, title='Consent Does Not Exist', message='The requested consent does not exist!', support=False) except FHIR.QuestionnaireResponseAlreadyExists: logger.warning('Consent already finished') return render_error( request, title='Consent Already Completed', message='You have already filled out and submitted this ' 'consent.', support=False) except Exception as e: logger.error( "Error while submitting consent signature: {}".format(e), exc_info=True, extra={ 'project': 'asd', 'request': request, }) return render_error( request, title='Application Error', message='The application has experienced an unknown error{}'. format(': {}'.format(e) if settings.DEBUG else '.'), support=False)
def get(self, request, *args, **kwargs): # Clearing any leftover sessions [ request.session.pop(key, None) for key in ['quiz', 'individual', 'guardian'] ] # Get the patient email and ensure they exist patient_email = get_jwt_email(request=request, verify=False) try: # If in demo mode, do not check participant and prior submissions if not self.demo(request): FHIR.check_patient(patient_email) # Check response FHIR.check_consent(PPM.Study.ASD.value, patient_email) # Create the form form = ASDTypeForm() context = {'form': form, 'return_url': self.return_url} return render(request, template_name='consent/asd.html', context=context) except FHIR.PatientDoesNotExist: logger.warning('Patient does not exist') return render_error( request, title='Patient Does Not Exist', message= 'A FHIR resource does not yet exist for the current user. ' 'Please sign into the People-Powered dashboard to ' 'create your user.', support=False) except FHIR.QuestionnaireDoesNotExist: logger.warning('Consent does not exist: NEER') return render_error( request, title='Consent Does Not Exist', message='The requested consent does not exist!', support=False) except FHIR.ConsentAlreadyExists: logger.warning('Consent already finished') return render_error( request, title='Consent Already Completed', message='You have already filled out and submitted this ' 'consent.', support=False) except Exception as e: logger.error("Error while rendering consent: {}".format(e), exc_info=True, extra={ 'request': request, 'project': 'asd', }) return render_error( request, title='Application Error', message='The application has experienced an unknown error{}'. format(': {}'.format(e) if settings.DEBUG else '.'), support=False)
def post(self, request, *args, **kwargs): # Get the patient email patient_email = get_jwt_email(request=request, verify=False) # Get the form form = self.Form(request.POST) if not form.is_valid(): # Return the form context = { 'study': self.study, 'form': form, 'return_url': self.return_url } return render(request, template_name='consent/{}.html'.format(self.study), context=context) # Process the form try: # Submit the consent FHIR.submit_consent(self.study, patient_email, form.cleaned_data, dry=self.demo(request)) if not self.demo(request): # Submit consent PDF in the background threading.Thread( target=APIConsentView.create_consent_document_reference, args=(request, self.study)).start() # Get the return URL context = { 'study': self.study, 'return_url': self.return_url, 'demo': self.demo(request) } # Get the passed parameters return render(request, template_name='consent/success.html', context=context) except FHIR.QuestionnaireDoesNotExist: logger.warning('Consent does not exist: {}'.format( PPM.Study.title(self.study))) return render_error( request, title='Consent Does Not Exist: {}'.format( self.questionnaire_id), message='The requested consent does not exist!', support=False) except FHIR.PatientDoesNotExist: logger.warning( 'Patient does not exist: {}'.format(patient_email[:3] + '****' + patient_email[-4:])) return render_error( request, title='Patient Does Not Exist', message= 'A FHIR resource does not yet exist for the current user. ' 'Please sign into the People-Powered dashboard to ' 'create your user.', support=False) except Exception as e: logger.error("Error while submitting consent: {}".format(e), exc_info=True, extra={ 'request': request, 'project': self.study, 'questionnaire': self.questionnaire_id, 'form': self.Form }) return render_error( request, title='Application Error', message='The application has experienced an unknown error {}'. format(': {}'.format(e) if settings.DEBUG else '.'), support=False)
def get(self, request, *args, **kwargs): # Get the patient email and ensure they exist patient_email = get_jwt_email(request=request, verify=False) try: # If in demo mode, do not check participant and prior submissions if not self.demo(request): # Ensure the current user has a record FHIR.check_patient(patient_email) # Check response FHIR.check_consent(self.study, patient_email) # Create the form form = self.Form() context = { 'study': self.study, 'form': form, 'return_url': self.return_url } # Build the template response response = render(request, template_name='consent/{}.html'.format( self.study), context=context) return response except FHIR.PatientDoesNotExist: logger.warning('Patient does not exist') return render_error( request, title='Patient Does Not Exist', message= 'A FHIR resource does not yet exist for the current user. ' 'Please sign into the People-Powered dashboard to ' 'create your user.', support=False) except FHIR.QuestionnaireDoesNotExist: logger.warning('Consent does not exist: {}'.format( PPM.Study.title(self.study))) return render_error( request, title='Consent Does Not Exist: {}'.format( self.questionnaire_id), message='The requested consent does not exist!', support=False) except FHIR.ConsentAlreadyExists: logger.warning('Consent already finished') return render_error( request, title='Consent Already Completed', message='You have already filled out and submitted this ' 'consent.', support=False) except Exception as e: logger.error("Error while rendering consent: {}".format(e), exc_info=True, extra={ 'request': request, 'project': self.study, 'questionnaire': self.questionnaire_id, 'form': self.Form }) return render_error( request, title='Application Error', message='The application has experienced an unknown error{}'. format(': {}'.format(e) if settings.DEBUG else '.'), support=False)
def post(self, request, *args, **kwargs): logger.debug(f'PPM/{self.study}: POST questionnaire') # Get the patient email patient_email = get_jwt_email(request=request, verify=False) # create a form instance and populate it with data from the request: form = self.Form(self.questionnaire_id, request.POST) # check whether it's valid: if not form.is_valid(): logger.debug( f'PPM/{self.study}: Form was invalid: {form.errors.as_json()}') # Return with errors context = { 'study': self.study, 'form': form, 'questionnaire_id': self.questionnaire_id, 'return_url': self.return_url, } # Get the passed parameters return render(request, template_name='questionnaire/{}.html'.format( self.study), context=context) else: logger.debug(f'PPM/{self.study}: Form was valid') # Process the form try: # Submit the form FHIR.submit_questionnaire(self.study, patient_email, form.cleaned_data, dry=self.demo(request)) # Get the return URL logger.debug( f'PPM/{self.study}: Success, returning user to: {self.return_url}' ) context = { 'questionnaire_id': self.questionnaire_id, 'return_url': self.return_url, 'demo': self.demo(request) } # Get the passed parameters return render(request, template_name='questionnaire/success.html', context=context) except FHIR.QuestionnaireDoesNotExist: logger.warning('Questionnaire does not exist: {}'.format( self.questionnaire_id)) return render_error( request, title='Questionnaire Does Not Exist', message='The requested questionnaire does not exist!', support=False) except FHIR.PatientDoesNotExist: logger.warning( 'Patient does not exist: {}'.format(patient_email[:3] + '****' + patient_email[-4:])) return render_error( request, title='Patient Does Not Exist', message= 'A FHIR resource does not yet exist for the current user. ' 'Please sign into the People-Powered dashboard to ' 'create your user.', support=False) except Exception as e: logger.error("Error while submitting questionnaire: {}".format(e), exc_info=True, extra={ 'project': self.study, }) return render_error( request, title='Application Error', message='The application has experienced an unknown error{}'. format(': {}'.format(e) if settings.DEBUG else '.'), support=False)